From 22179b96fc5f66319a63abfb52e96075d2ed4918 Mon Sep 17 00:00:00 2001 From: vchizhevsky Date: Mon, 4 Jul 2016 15:39:33 +0300 Subject: [PATCH] dammy --- .../client-c/Modules/cppcheck/CMakeLists.txt | 32 + .../client-c/Modules/doxygen/CMakeLists.txt | 28 + .../client-multi/client-c/docs/code-style.org | 96 + .../client-multi/client-c/docs/contribute.org | 73 + client/client-multi/client-c/docs/nix.org | 60 + .../client-c/docs/sandbox-updating.org | 45 + .../my-kaa-application/CMakeLists.txt | 27 + .../my-kaa-application/src/kaa-application.c | 47 + .../src/extensions/bootstrap/CMakeLists.txt | 32 + .../bootstrap/kaa_bootstrap_manager.c | 681 +++++++ .../bootstrap/kaa_bootstrap_manager.h | 70 + .../test/test_kaa_bootstrap_manager.c | 492 +++++ .../extensions/configuration/CMakeLists.txt | 33 + .../configuration/kaa_configuration_manager.c | 307 +++ .../configuration/kaa_configuration_manager.h | 54 + .../kaa_configuration_manager_private.h | 34 + .../test/test_kaa_configuration.c | 150 ++ .../src/extensions/event/CMakeLists.txt | 32 + .../client-c/src/extensions/event/kaa_event.c | 1203 ++++++++++++ .../client-c/src/extensions/event/kaa_event.h | 107 ++ .../src/extensions/event/kaa_event_private.h | 42 + .../extensions/event/test/test_kaa_event.c | 659 +++++++ .../src/extensions/logging/CMakeLists.txt | 43 + .../src/extensions/logging/kaa_logging.c | 845 +++++++++ .../src/extensions/logging/kaa_logging.h | 131 ++ .../extensions/logging/kaa_logging_private.h | 45 + .../extensions/logging/test/test_kaa_log.c | 1648 +++++++++++++++++ .../extensions/notification/CMakeLists.txt | 31 + .../notification/kaa_notification_manager.c | 1368 ++++++++++++++ .../notification/kaa_notification_manager.h | 199 ++ .../notification/test/test_kaa_notification.c | 336 ++++ .../src/extensions/profile/CMakeLists.txt | 33 + .../src/extensions/profile/kaa_profile.c | 470 +++++ .../src/extensions/profile/kaa_profile.h | 89 + .../extensions/profile/kaa_profile_private.h | 38 + .../profile/test/test_kaa_profile.c | 432 +++++ .../src/extensions/user/CMakeLists.txt | 32 + .../client-c/src/extensions/user/kaa_user.c | 767 ++++++++ .../client-c/src/extensions/user/kaa_user.h | 136 ++ .../src/extensions/user/kaa_user_private.h | 35 + .../src/extensions/user/test/test_kaa_user.c | 254 +++ .../cc32xx/configuration_persistence.c | 38 + .../src/kaa/platform-impl/cc32xx/file_utils.c | 192 ++ .../src/kaa/platform-impl/cc32xx/key_utils.c | 38 + .../platform-impl/cc32xx/platform/defaults.h | 30 + .../cc32xx/platform/file_utils.h | 36 + .../kaa/platform-impl/cc32xx/platform/mem.h | 38 + .../kaa/platform-impl/cc32xx/platform/sock.h | 88 + .../kaa/platform-impl/cc32xx/platform/stdio.h | 24 + .../kaa/platform-impl/cc32xx/platform/time.h | 30 + .../src/kaa/platform-impl/cc32xx/reboot.c | 31 + .../src/kaa/platform-impl/cc32xx/status.c | 39 + .../src/kaa/platform-impl/cc32xx/tcp_utils.c | 181 ++ .../src/kaa/platform-impl/cc32xx/time.c | 53 + .../esp8266/configuration_persistence.c | 37 + .../kaa/platform-impl/esp8266/kaa_client.c | 464 +++++ .../src/kaa/platform-impl/esp8266/key_utils.c | 29 + .../src/kaa/platform-impl/esp8266/logger.c | 62 + .../platform-impl/esp8266/platform/defaults.h | 30 + .../kaa/platform-impl/esp8266/platform/mem.h | 43 + .../kaa/platform-impl/esp8266/platform/sock.h | 36 + .../platform-impl/esp8266/platform/stdio.h | 24 + .../kaa/platform-impl/esp8266/platform/time.h | 26 + .../src/kaa/platform-impl/esp8266/status.c | 32 + .../src/kaa/platform-impl/esp8266/tcp_utils.c | 159 ++ .../src/kaa/platform-impl/esp8266/time.c | 24 + .../posix/configuration_persistence.c | 41 + .../src/kaa/platform-impl/posix/file_utils.c | 81 + .../src/kaa/platform-impl/posix/kaa_client.c | 474 +++++ .../src/kaa/platform-impl/posix/key_utils.c | 83 + .../platform-impl/posix/platform/defaults.h | 36 + .../platform-impl/posix/platform/file_utils.h | 38 + .../kaa/platform-impl/posix/platform/mem.h | 38 + .../kaa/platform-impl/posix/platform/sock.h | 46 + .../kaa/platform-impl/posix/platform/stdio.h | 28 + .../kaa/platform-impl/posix/platform/time.h | 27 + .../src/kaa/platform-impl/posix/status.c | 32 + .../src/kaa/platform-impl/posix/tcp_utils.c | 192 ++ .../src/kaa/platform/kaa_client_properties.h | 31 + .../src/kaa/platform/kaa_failover_strategy.h | 34 + .../tools/avro-cpp-disable-tests.patch | 92 + keys/bootstrap/private.key | Bin 0 -> 1219 bytes keys/bootstrap/public.key | Bin 0 -> 294 bytes keys/operations/private.key | Bin 0 -> 1218 bytes keys/operations/public.key | Bin 0 -> 294 bytes nix/kaa-docs/Gemfile | 22 + nix/kaa-docs/Gemfile.lock | 55 + nix/kaa-docs/default.nix | 40 + nix/kaa-docs/gemset.nix | 178 ++ .../kaa/server/common/admin/AdminClient.java | 337 +--- .../dao/model/sql/NotificationSchema.java | 2 +- .../dao/service/ApplicationServiceImpl.java | 23 +- .../dao/service/NotificationServiceImpl.java | 2 +- .../kaa/server/common/dao/AbstractTest.java | 43 +- .../dao/impl/sql/HibernateAbstractTest.java | 31 +- .../HibernateNotificationSchemaDaoTest.java | 18 +- .../resources/dao/schema/testProfileBody.json | 24 + .../kaa/common/dto/NotificationSchemaDto.java | 9 +- .../resources/common-dao-mongodb.properties | 2 +- .../dao/NotificationServiceImplTest.java | 32 +- .../admin/client/mvp/ClientFactory.java | 4 +- .../admin/client/mvp/ClientFactoryImpl.java | 8 +- .../mvp/activity/CtlSchemaActivity.java | 24 +- .../activity/NotificationSchemaActivity.java | 44 +- .../activity/NotificationSchemasActivity.java | 18 +- .../admin/client/mvp/data/DataSource.java | 74 +- .../client/mvp/place/CtlSchemaPlace.java | 7 +- .../NotificationSchemaViewImpl.java | 3 +- .../NotificationSchemasViewImpl.java | 3 +- .../admin/controller/KaaAdminController.java | 601 +----- .../admin/services/KaaAdminServiceImpl.java | 437 +++-- .../schema/NotificationSchemaViewDto.java | 23 + .../shared/services/KaaAdminService.java | 20 +- .../service/DefaultControlService.java | 63 +- .../src/main/resources/kaa-node.properties | 2 +- .../client/i18n/KaaAdminConstants.properties | 2 + .../control/AbstractTestControlServer.java | 27 +- test-gh-pages-current | 1 + test-gh-pages.sh | 54 + 119 files changed, 15341 insertions(+), 1185 deletions(-) create mode 100644 client/client-multi/client-c/Modules/cppcheck/CMakeLists.txt create mode 100644 client/client-multi/client-c/Modules/doxygen/CMakeLists.txt create mode 100644 client/client-multi/client-c/docs/code-style.org create mode 100644 client/client-multi/client-c/docs/contribute.org create mode 100644 client/client-multi/client-c/docs/nix.org create mode 100644 client/client-multi/client-c/docs/sandbox-updating.org create mode 100644 client/client-multi/client-c/examples/my-kaa-application/CMakeLists.txt create mode 100644 client/client-multi/client-c/examples/my-kaa-application/src/kaa-application.c create mode 100644 client/client-multi/client-c/src/extensions/bootstrap/CMakeLists.txt create mode 100644 client/client-multi/client-c/src/extensions/bootstrap/kaa_bootstrap_manager.c create mode 100644 client/client-multi/client-c/src/extensions/bootstrap/kaa_bootstrap_manager.h create mode 100644 client/client-multi/client-c/src/extensions/bootstrap/test/test_kaa_bootstrap_manager.c create mode 100644 client/client-multi/client-c/src/extensions/configuration/CMakeLists.txt create mode 100644 client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager.c create mode 100644 client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager.h create mode 100644 client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager_private.h create mode 100644 client/client-multi/client-c/src/extensions/configuration/test/test_kaa_configuration.c create mode 100644 client/client-multi/client-c/src/extensions/event/CMakeLists.txt create mode 100644 client/client-multi/client-c/src/extensions/event/kaa_event.c create mode 100644 client/client-multi/client-c/src/extensions/event/kaa_event.h create mode 100644 client/client-multi/client-c/src/extensions/event/kaa_event_private.h create mode 100644 client/client-multi/client-c/src/extensions/event/test/test_kaa_event.c create mode 100644 client/client-multi/client-c/src/extensions/logging/CMakeLists.txt create mode 100644 client/client-multi/client-c/src/extensions/logging/kaa_logging.c create mode 100644 client/client-multi/client-c/src/extensions/logging/kaa_logging.h create mode 100644 client/client-multi/client-c/src/extensions/logging/kaa_logging_private.h create mode 100644 client/client-multi/client-c/src/extensions/logging/test/test_kaa_log.c create mode 100644 client/client-multi/client-c/src/extensions/notification/CMakeLists.txt create mode 100644 client/client-multi/client-c/src/extensions/notification/kaa_notification_manager.c create mode 100644 client/client-multi/client-c/src/extensions/notification/kaa_notification_manager.h create mode 100644 client/client-multi/client-c/src/extensions/notification/test/test_kaa_notification.c create mode 100644 client/client-multi/client-c/src/extensions/profile/CMakeLists.txt create mode 100644 client/client-multi/client-c/src/extensions/profile/kaa_profile.c create mode 100644 client/client-multi/client-c/src/extensions/profile/kaa_profile.h create mode 100644 client/client-multi/client-c/src/extensions/profile/kaa_profile_private.h create mode 100644 client/client-multi/client-c/src/extensions/profile/test/test_kaa_profile.c create mode 100644 client/client-multi/client-c/src/extensions/user/CMakeLists.txt create mode 100644 client/client-multi/client-c/src/extensions/user/kaa_user.c create mode 100644 client/client-multi/client-c/src/extensions/user/kaa_user.h create mode 100644 client/client-multi/client-c/src/extensions/user/kaa_user_private.h create mode 100644 client/client-multi/client-c/src/extensions/user/test/test_kaa_user.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/configuration_persistence.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/file_utils.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/key_utils.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/defaults.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/file_utils.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/mem.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/sock.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/stdio.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/time.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/reboot.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/status.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/tcp_utils.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/cc32xx/time.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/configuration_persistence.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/kaa_client.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/key_utils.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/logger.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/defaults.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/mem.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/sock.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/stdio.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/time.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/status.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/tcp_utils.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/esp8266/time.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/configuration_persistence.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/file_utils.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/kaa_client.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/key_utils.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/platform/defaults.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/platform/file_utils.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/platform/mem.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/platform/sock.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/platform/stdio.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/platform/time.h create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/status.c create mode 100644 client/client-multi/client-c/src/kaa/platform-impl/posix/tcp_utils.c create mode 100644 client/client-multi/client-c/src/kaa/platform/kaa_client_properties.h create mode 100644 client/client-multi/client-c/src/kaa/platform/kaa_failover_strategy.h create mode 100644 client/client-multi/client-cpp/tools/avro-cpp-disable-tests.patch create mode 100644 keys/bootstrap/private.key create mode 100644 keys/bootstrap/public.key create mode 100644 keys/operations/private.key create mode 100644 keys/operations/public.key create mode 100644 nix/kaa-docs/Gemfile create mode 100644 nix/kaa-docs/Gemfile.lock create mode 100644 nix/kaa-docs/default.nix create mode 100644 nix/kaa-docs/gemset.nix create mode 100644 server/common/dao/src/test/resources/dao/schema/testProfileBody.json create mode 100644 server/node/src/main/java/org/kaaproject/kaa/server/admin/shared/schema/NotificationSchemaViewDto.java create mode 160000 test-gh-pages-current create mode 100755 test-gh-pages.sh diff --git a/client/client-multi/client-c/Modules/cppcheck/CMakeLists.txt b/client/client-multi/client-c/Modules/cppcheck/CMakeLists.txt new file mode 100644 index 0000000000..bc1d28cb32 --- /dev/null +++ b/client/client-multi/client-c/Modules/cppcheck/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This cmake list is responsible for running cppcheck against sources. + +find_program(CPPCHECK_COMMAND cppcheck) + +if(CPPCHECK_COMMAND) + add_custom_target(cppcheck + COMMAND ${CPPCHECK_COMMAND} --quiet --enable=all --std=c99 --suppress=unusedFunction + --force --error-exitcode=1 --template=gcc -I src/kaa + --inline-suppr src/ test/ + WORKING_DERICTORY ${KAA_SDK_DIR} + COMMENT "Running cppcheck" + VERBATIM + ) +else() + message (STATUS "Could NOT find cppcheck") +endif() \ No newline at end of file diff --git a/client/client-multi/client-c/Modules/doxygen/CMakeLists.txt b/client/client-multi/client-c/Modules/doxygen/CMakeLists.txt new file mode 100644 index 0000000000..ccbcc080b2 --- /dev/null +++ b/client/client-multi/client-c/Modules/doxygen/CMakeLists.txt @@ -0,0 +1,28 @@ +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This cmake list is responsible for building doxygen documentation. + +find_package(Doxygen) + +add_custom_target(doxygen + COMMAND ${CMAKE_COMMAND} -E make_directory target/apidocs/doxygen + COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile + WORKING_DIRECTORY + ${KAA_SDK_DIR} + COMMENT "Generating doxygen docs" + VERBATIM + ) \ No newline at end of file diff --git a/client/client-multi/client-c/docs/code-style.org b/client/client-multi/client-c/docs/code-style.org new file mode 100644 index 0000000000..0dee162a3f --- /dev/null +++ b/client/client-multi/client-c/docs/code-style.org @@ -0,0 +1,96 @@ +#+TITLE: C/C++ Code Style +#+AUTHOR: Alexey Shmalko +#+OPTIONS: toc:nil + +# Confluence doesn't have C language highlight, so use C++ here. + +* Rules + + - Use 4 spaces for indentation. Don't use tabs. + + - Use 100-character column width. + + - Use snake_case for the names (not camelCase). + + - All macro names must be in UPPERCASE. + + - Use [[https://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS][1TBS]] (the one true brace style). + #+begin_src c++ + int main(int argc, char *argv[]) + { + if (argc > 2) { + printf("too many args\n"); + } else { + printf("too little args\n"); + } + + return 0; + } + #+end_src + Note brace position. The braces in control structures are required even for a single statement. + + - The pointer star must be aligned to the variable name: + #+begin_src c++ + int *x; + #+end_src + + - Labels must be indented one level less than the normal indentation (except for case labels): + #+begin_src c++ + int main(int argc, char *argv[]) + { + switch (argc) { + case 3: + printf("hi\n"); + + default: + goto fail; + } + return 0; + + fail: + return 1; + } + #+end_src + + - Prefer double-indent instead of alignment in conditions and argument lists: + #+begin_src c++ + int my_function(int param1, int param2, + int param3, int param4) + { + if (param1 > 3 && param2 > 3 && + param3 > 3 && param4 > 3) { + return param1 + param2 + param3 + + param4; + } else { + return 0; + } + } + #+end_src + + - Don't allow trailing spaces or lines. + +* Editor configurations +** Emacs + + #+begin_src emacs-lisp + (require 'whitespace) + (setq-default whitespace-style '(face + tab-mark + empty + trailing + lines-tail)) + (global-whitespace-mode t) + + (c-add-style "kaa" + '("k&r" + (whitespace-line-column . 100) + (indent-tabs-mode . nil) + (c-basic-offset . 4) + (c-label-minimum-indentation . 0) + (c-offsets-alist . ((case-label . +) + (arglist-intro . ++) + (arglist-cont-nonempty . ++) + (inextern-lang . 0))))) + + (setq c-default-style "kaa") + #+end_src diff --git a/client/client-multi/client-c/docs/contribute.org b/client/client-multi/client-c/docs/contribute.org new file mode 100644 index 0000000000..79fa09669e --- /dev/null +++ b/client/client-multi/client-c/docs/contribute.org @@ -0,0 +1,73 @@ +#+TITLE: How to Contribute +#+OPTIONS: toc:nil + +This guide is dedicated to self-review and contribution process of Kaa project. It involves comments and code introspection, functional and unit testing, commits preparation and external code review. + +* Code introspection + When you about to finish your feature or bug fix you need to check your changes following the procedure below: + 1. Code MUST use C99 (for C SDK) or C++11 (for C++ SDK) standards without using any compiler extensions, like GNU. + 2. Code MUST compile for every [[http://docs.kaaproject.org/display/KAA/Supported+platforms][supported platform]]. + 3. Code MUST NOT contain any compiler warnings. + 4. Corresponding doxygen comments SHOULD be added to new or changed interfaces/modules. + 5. Every TODO item MUST reference an issue on Jira and issue key must be placed in a comment next to the TODO. (e.g., =// TODO(KAA-982): Use asserts=) + 6. Bottlenecks and magic places MUST be well-commented. (/Well, ideally there should be no magic places./) + 7. Commented-out chunks of code are forbidden. + 8. Code style MUST be consistent. Check out our [[./code-style.org][Code Style Guide]]. + 9. It is RECOMMENDED to use a source formatting script against modified sources. The script is placed in =client/client-multi/client-c/scripts/srcformat.sh=. + +* Instrumentation + This section describes requirements that every developer must follow using existing tooling, like code analyzers, test frameworks etc. + + 1. Changes SHOULD be covered with unit tests, reflecting the essence of these changes. (Check out our [[./testing.org][Testing Tutorial]].) + 2. If possible and required, changes SHOULD be covered with functional tests too. + 3. Changes MUST NOT break any existing unit or functional tests. A fix SHOULD be provided if any of tests is failing due to introduced changes, except cases when test is no longer valid. + 4. A sample app that directly or indirectly relies on changes SHOULD be well tested in automated or manual fashion to make sure that nothing was broken. If it did break, a fix must be provided either in sample applications or SDK or both. + 5. Cppcheck MUST be triggered against changes; reported issues MUST be reviewed, and important ones MUST be fixed. + + See also: [[./sandbox-updating.org][sandbox updating guide]]. + +* Pre-commit checklist + The following checklist guarantees your patch will pass the Travis build and increases chance for passing code review process: + + #+begin_src sh + # The next commands must be executed from the client-c/ directory + cd client/client-multi/client-c/ + + # Clean up source tree + rm -rf target build-* + + # Check license headers + nix-shell -p maven --run 'mvn apache-rat:check' + # If you have installed maven on your system you can use the following command: + # mvn apache-rat:check + # If it finds any violations, check ./target/rat.txt for the list of files. + + # Format changed files + nix-shell --run './scripts/srcformat.sh ' + + # Check SDK builds for all platforms, passes cppcheck and builds doxygen + nix-shell --arg withWerror true --pure --run './scripts/build.sh' + #+end_src + + Note: You should use nix-shell for source formatting as it contains a patch for the astyle to allow max-instatement-indent values lower than 40 (https://sourceforge.net/p/astyle/bugs/396/). Alternatively, you can patch your astyle with the [[../../../../nix/astyle/max_indent.patch][following patch]] manually. + +* Committing + 1. All of the changes MUST be added to commit(s), and commit messages SHOULD NOT be too long. + 2. The commit message SHOULD begin with ticket number: =KAA-[TICKET NUMBER] Description=. + 3. Changes in commits SHOULD be as atomic as possible. It means that single commit of a pull request contains changes to a single entity (e.g., a module). + 4. Single pull request SHOULD expose single feature or bug fix if possible. That will help reviewers to do their job. + + # TODO: Complete git guide of Kaa (using http://git.kernel.org/cgit/git/git.git/tree/Documentation/SubmittingPatches?id=HEAD as an example) + +* Review + According to our [[http://docs.kaaproject.org/display/KAA/Git+Flow][Git Flow]], every Pull Request must be reviewed and approved by at least two members of the responsible team. When you receive a comment, you should address it; that means either changing the code or answering a question in the comments. + + If you do a small fix, don't add a new commit but rather squash your changes to the old commits. That's required to avoid commit chains as the next one: + #+begin_src example + KAA-XXX Implement feature xxx + KAA-XXX Fix formatting + KAA-XXX Fix typo + KAA-XXX Address CR questions + KAA-XXX Address CR questions + KAA-XXX Fix typo + #+end_src diff --git a/client/client-multi/client-c/docs/nix.org b/client/client-multi/client-c/docs/nix.org new file mode 100644 index 0000000000..4a6a14a0bc --- /dev/null +++ b/client/client-multi/client-c/docs/nix.org @@ -0,0 +1,60 @@ +#+TITLE: Nix Guide +#+AUTHOR: Alexey Shmalko +#+OPTIONS: toc:nil + +[[http://nixos.org/nix/][Nix]] is a powerful Linux package manager. We use it to create a better development environment for C client and manage all dependencies. + +* Setup Nix + The easiest way to setup Nix is next: + #+begin_src sh + curl https://nixos.org/nix/install | sh + source $HOME/.nix-profile/etc/profile.d/nix.sh + #+end_src + + And add =source $HOME/.nix-profile/et/profile.d/nix.sh= to your =.bashrc=. + + If you don't trust piping shell scripts from the Internet (and you shouldn't), feel free to examine the script or use alternate setup. (Though, you should google it up yourself.) + +* Install all dependencies and enter the shell + The first time you enter shell environment, the Nix will install all dependencies needed for development. + #+begin_src sh + nix-shell + #+end_src + + As CC3200 SDK is not freely available, nix-shell will abort and ask you to download the file manually and add it to nix-store -- follow instructions. Then re-run the command above. + +* Using shell + After dependencies are installed you'll find yourself in a custom bash shell (you can enter it with =nix-shell= whenever you want). + + You can use all your development tools from there, =./build.sh=, =cmake=. Furthermore, a custom top-level Makefile is provided that propagates your commands to all targets (it also configures all targets appropriately). So just run =make= to build C SDK for all platforms. + + If you want to run a single command within a shell, you can use =--run= option. For example: + #+begin_src sh + nix-shell --run make + #+end_src + +* Options + | Option | Default value | Meaning | + |--------------------+---------------+-----------------------------------------------| + | posixSupport | true | Host build with gcc. Goes to =build-posix=. | + | clangSupport | true | Host build with clang. Goes to =build-clang=. | + | cc3200Support | true | CC3200. Goes to =build-cc3200=. | + | esp8266Support | true | ESP8266. Goes to =build-esp8266=. | + | raspberrypiSupport | true | Raspberry Pi. Goes to =build-rpi=. | + | testSupport | true | Add all tools for build verification. | + | withTooling | true | Add tools for building docs. | + | withWerror | false | Enable =-Werror= for all builds. | + + You can override any option with the following command: + #+begin_src sh + nix-shell --arg optionName value + #+end_src + + For example: + #+begin_src sh + nix-shell --arg withWerror true + #+end_src + +* Further reading + - [[http://lethalman.blogspot.com/2014/07/nix-pill-1-why-you-should-give-it-try.html][Nix pills series]] + - [[https://nixos.org/nixpkgs/manual/][Nixpkgs Contributors Guide]] diff --git a/client/client-multi/client-c/docs/sandbox-updating.org b/client/client-multi/client-c/docs/sandbox-updating.org new file mode 100644 index 0000000000..524590d80e --- /dev/null +++ b/client/client-multi/client-c/docs/sandbox-updating.org @@ -0,0 +1,45 @@ +#+TITLE: Sandbox Updating +#+AUTHOR: Maxim Olender +#+OPTIONS: toc:nil + +Sometimes there is a need to update already running sandbox with new Kaa server or upload new SDK code. This article describes how to do that with minimum hassle using C SDK as an example. You need Maven, JDK-8 (Java Development Kit), ssh and C compiler. + +* Step-by-step guide + + {info} + Kaa requires JDK-8 to use maven with Kaa. Double check =$JAVA_HOME= environment variable and make sure that it points to JDK-8 home directory. + {info} + + When your sandbox up and running, perform following steps to update it: + + 1. Navigate to the root of the Kaa repository and launch Maven: + #+begin_src sh + mvn clean -P compile-client-c,compile-gwt,mongo-dao,mariadb-dao clean install verify -DskipTests + #+end_src + + 2. After build succeeds it is time to copy node package to the sandbox. =$SANDBOX_HOST= is a host or IP of your sandbox: + #+begin_src sh + scp server/node/target/kaa-node.deb kaa@${SANDBOX_HOST}: + #+end_src + + 3. Copy C SDK archive. =$SDK_VERSION= is a version of the SDK. At the time of this writing, SDK version is 0.9.0. Again, =$SANDBOX_HOST= is a host or IP of your sandbox: + #+begin_src sh + scp client/client-multi/client-c/target/client-c-${SDK_VERSION}-SNAPSHOT-c-sdk.tar.gz kaa@${SANDBOX_HOST}: + ssh kaa@${SANDBOX_HOST} sudo mv -v /home/kaa/client-c-${SDK_VERSION}-SNAPSHOT-c-sdk.tar.gz /usr/lib/kaa-node/sdk/c/kaa-c-ep-sdk-${SDK_VERSION}-SNAPSHOT.tar.gz + #+end_src + + 4. After all files are copied to the proper destinations in sandbox, it is time to install Kaa server node: + #+begin_src sh + ssh kaa@${SANDBOX_HOST} sudo dpkg -i kaa-node.deb + #+end_src + + 5. Now, Kaa restart is required: + #+begin_src sh + ssh kaa@${SANDBOX_HOST} kaa-node stop + ssh kaa@${SANDBOX_HOST} kaa-node start + #+end_src + + 6. If errors occur during restarting of Kaa sandbox, I recommend you to reboot sandbox virtual machine: + #+begin_src sh + ssh kaa@${SANDBOX_HOST} sudo reboot + #+end_src diff --git a/client/client-multi/client-c/examples/my-kaa-application/CMakeLists.txt b/client/client-multi/client-c/examples/my-kaa-application/CMakeLists.txt new file mode 100644 index 0000000000..08861dc4cf --- /dev/null +++ b/client/client-multi/client-c/examples/my-kaa-application/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +cmake_minimum_required(VERSION 3.5.2) +project(kaa-application C) + +find_package(OpenSSL REQUIRED) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g -Wall -Wextra") + +add_subdirectory(kaa) + +add_executable(kaa-app src/kaa-application.c) +target_link_libraries(kaa-app kaac crypto) diff --git a/client/client-multi/client-c/examples/my-kaa-application/src/kaa-application.c b/client/client-multi/client-c/examples/my-kaa-application/src/kaa-application.c new file mode 100644 index 0000000000..40a3c9e8b5 --- /dev/null +++ b/client/client-multi/client-c/examples/my-kaa-application/src/kaa-application.c @@ -0,0 +1,47 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + + +static void dummy_function(void *context) +{ + printf("Hello, I am a Kaa Application!\n"); + kaa_client_stop(context); +} + +int main(void) +{ + kaa_client_t *kaa_client = NULL; + kaa_error_t error = kaa_client_create(&kaa_client, NULL); + if (error) { + return EXIT_FAILURE; + } + + error = kaa_client_start(kaa_client, dummy_function, (void *)kaa_client, 0); + + kaa_client_destroy(kaa_client); + + if (error) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/client/client-multi/client-c/src/extensions/bootstrap/CMakeLists.txt b/client/client-multi/client-c/src/extensions/bootstrap/CMakeLists.txt new file mode 100644 index 0000000000..e0d80ca6d3 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/bootstrap/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set(EXTENSION_BOOTSTRAP_SOURCE_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_bootstrap_manager.c) + +set(EXTENSION_BOOTSTRAP_HEADER_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_bootstrap_manager.h) + +add_library(extension_bootstrap ${EXTENSION_BOOTSTRAP_SOURCE_FILES}) +target_include_directories(extension_bootstrap PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(extension_bootstrap PUBLIC kaac) + +# KAA-989 +#kaa_add_unit_test(NAME test_kaa_bootstrap_manager +# SOURCES +# ${CMAKE_CURRENT_LIST_DIR}/test/test_kaa_bootstrap_manager.c +# DEPENDS +# kaac ${OPENSSL_LIBRARIES}) diff --git a/client/client-multi/client-c/src/extensions/bootstrap/kaa_bootstrap_manager.c b/client/client-multi/client-c/src/extensions/bootstrap/kaa_bootstrap_manager.c new file mode 100644 index 0000000000..ee24d7fa27 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/bootstrap/kaa_bootstrap_manager.c @@ -0,0 +1,681 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaa_private.h" + +#include +#include +#include +#include +#include +#include "platform/stdio.h" +#include "platform/sock.h" +#include "kaa_defaults.h" +#include "kaa_platform_common.h" +#include "kaa_platform_utils.h" +#include "collections/kaa_list.h" +#include "kaa_bootstrap_manager.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include "kaa_channel_manager.h" +#include "platform/ext_kaa_failover_strategy.h" +#include "kaa_protocols/kaa_tcp/kaatcp_common.h" + +typedef struct { + kaa_transport_protocol_id_t protocol_id; + size_t index; +} kaa_bootstrap_access_points_t; + +typedef struct { + kaa_transport_protocol_id_t protocol_id; + kaa_list_t *access_points; + kaa_list_node_t *current_access_points; +} kaa_operations_access_points_t; + +typedef struct { + bool is_failover; + kaa_access_point_t *acess_point; + kaa_server_type_t server; + kaa_transport_protocol_id_t protocol_id; + kaa_time_t next_execution_time; + kaa_failover_reason reason; +} failover_meta_info; + +struct kaa_bootstrap_manager_t { + kaa_channel_manager_t *channel_manager; + kaa_list_t *operations_access_points; + kaa_list_t *bootstrap_access_points; + kaa_context_t *kaa_context; + kaa_logger_t *logger; + kaa_time_t next_operations_request_time; + failover_meta_info failover_meta_info; +}; + +static kaa_extension_id bootstrap_sync_services[1] = { KAA_EXTENSION_BOOTSTRAP }; + +kaa_error_t kaa_extension_bootstrap_init(kaa_context_t *kaa_context, void **context) +{ + kaa_error_t result = kaa_bootstrap_manager_create((kaa_bootstrap_manager_t **)context, kaa_context); + kaa_context->bootstrap_manager = *context; + return result; +} + +kaa_error_t kaa_extension_bootstrap_deinit(void *context) +{ + kaa_bootstrap_manager_destroy(context); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_bootstrap_request_get_size(void *context, size_t *expected_size) +{ + return kaa_channel_manager_bootstrap_request_get_size( + ((kaa_bootstrap_manager_t *)context)->channel_manager, expected_size); +} + +kaa_error_t kaa_extension_bootstrap_request_serialize(void *context, uint32_t request_id, + uint8_t *buffer, size_t *size, bool *need_resync) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !size || !need_resync) { + return KAA_ERR_BADPARAM; + } + + *need_resync = true; + + size_t size_needed; + kaa_error_t error = kaa_channel_manager_bootstrap_request_get_size( + ((kaa_bootstrap_manager_t *)context)->channel_manager, &size_needed); + if (error) { + return error; + } + + if (!buffer || *size < size_needed) { + *size = size_needed; + return KAA_ERR_BUFFER_IS_NOT_ENOUGH; + } + + *size = size_needed; + + kaa_platform_message_writer_t writer = KAA_MESSAGE_WRITER(buffer, *size); + error = kaa_bootstrap_manager_bootstrap_request_serialize(context, &writer); + if (error) { + return error; + } + + *size = writer.current - buffer; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_bootstrap_server_sync(void *context, uint32_t request_id, + uint16_t extension_options, const uint8_t *buffer, size_t size) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !buffer) { + return KAA_ERR_BADPARAM; + } + + kaa_platform_message_reader_t reader = KAA_MESSAGE_READER(buffer, size); + return kaa_bootstrap_manager_handle_server_sync(context, &reader, extension_options, size); +} + +static void destroy_access_point(void *data) +{ + KAA_RETURN_IF_NIL(data,); + kaa_access_point_t *access_point = (kaa_access_point_t *)data; + if (access_point->connection_data) { + KAA_FREE(access_point->connection_data); + } + KAA_FREE(access_point); +} + +static void destroy_operations_access_points(void *data) +{ + KAA_RETURN_IF_NIL(data,); + kaa_operations_access_points_t *operations_access_points = + (kaa_operations_access_points_t *)data; + kaa_list_destroy(operations_access_points->access_points, destroy_access_point); + KAA_FREE(operations_access_points); +} + +static bool find_operations_access_points(void *data, void *context) +{ + KAA_RETURN_IF_NIL2(data, context, false); + kaa_transport_protocol_id_t *matcher = (kaa_transport_protocol_id_t *) context; + kaa_transport_protocol_id_t *source = &(((kaa_operations_access_points_t *)data)->protocol_id); + return kaa_transport_protocol_id_equals(matcher, source); +} + +static bool find_bootstrap_access_points(void *data, void *context) +{ + KAA_RETURN_IF_NIL2(data, context, false); + kaa_transport_protocol_id_t *matcher = (kaa_transport_protocol_id_t *) context; + kaa_transport_protocol_id_t *source = &(((kaa_bootstrap_access_points_t *)data)->protocol_id); + return kaa_transport_protocol_id_equals(matcher, source); +} + +static kaa_error_t do_sync(kaa_bootstrap_manager_t *self) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_BADPARAM); + + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel( + self->channel_manager, KAA_EXTENSION_BOOTSTRAP); + if (channel) { + channel->sync_handler(channel->context, bootstrap_sync_services, 1); + return KAA_ERR_NONE; + } + + return KAA_ERR_NOT_FOUND; +} + +static kaa_error_t kaa_bootstrap_manager_on_server_sync(kaa_bootstrap_manager_t *self) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_BADPARAM); + + kaa_list_node_t *channel_it = kaa_list_begin(self->operations_access_points); + while (channel_it) { + kaa_operations_access_points_t *operations_access_points = kaa_list_get_data(channel_it); + kaa_access_point_t *access_point = kaa_list_get_data( + kaa_list_begin(operations_access_points->access_points)); + + kaa_channel_manager_on_new_access_point(self->channel_manager + , &operations_access_points->protocol_id + , KAA_SERVER_OPERATIONS + , access_point); + + channel_it = kaa_list_next(channel_it); + } + + return KAA_ERR_NONE; +} + +static kaa_error_t add_operations_access_point(kaa_bootstrap_manager_t *self + , kaa_transport_protocol_id_t *protocol_id + , kaa_access_point_t *access_point) +{ + KAA_RETURN_IF_NIL2(protocol_id, access_point, KAA_ERR_BADPARAM); + + kaa_list_node_t *channel_it = kaa_list_find_next(kaa_list_begin(self->operations_access_points) + , find_operations_access_points + , protocol_id); + + if (channel_it) { + kaa_operations_access_points_t *operations_access_points = kaa_list_get_data(channel_it); + kaa_list_node_t *access_point_it = kaa_list_push_front(operations_access_points->access_points + , access_point); + KAA_RETURN_IF_NIL(access_point_it, KAA_ERR_NOMEM); + + operations_access_points->current_access_points = access_point_it; + } else { + kaa_operations_access_points_t *operations_access_points = + (kaa_operations_access_points_t *)KAA_CALLOC(1, sizeof(kaa_operations_access_points_t)); + KAA_RETURN_IF_NIL(operations_access_points, KAA_ERR_NOMEM); + + operations_access_points->protocol_id = *protocol_id; + operations_access_points->access_points = kaa_list_create(); + operations_access_points->current_access_points = kaa_list_push_front(operations_access_points->access_points + , access_point); + + if (!operations_access_points->access_points || !operations_access_points->current_access_points) { + destroy_operations_access_points(operations_access_points); + KAA_LOG_WARN(self->logger, KAA_ERR_NOMEM, "Failed to add new access point: " + "id=0x%08X, protocol: id=0x%08X, version=%u" + , access_point->id, protocol_id->id, protocol_id->version); + return KAA_ERR_NOMEM; + } + + kaa_list_node_t *operations_access_points_it = kaa_list_push_front(self->operations_access_points, operations_access_points); + if (!operations_access_points_it) { + destroy_operations_access_points(operations_access_points); + KAA_LOG_WARN(self->logger, KAA_ERR_NOMEM, "Failed to add new access point: " + "id=0x%08X, protocol: id=0x%08X, version=%u" + , access_point->id, protocol_id->id, protocol_id->version); + return KAA_ERR_NOMEM; + } + } + + return KAA_ERR_NONE; +} + +/** @deprecated Use kaa_extension_manager_init(). */ +kaa_error_t kaa_bootstrap_manager_create(kaa_bootstrap_manager_t **bootstrap_manager_p, kaa_context_t *kaa_context) +{ + KAA_RETURN_IF_NIL2(bootstrap_manager_p, kaa_context, KAA_ERR_BADPARAM); + + *bootstrap_manager_p = (kaa_bootstrap_manager_t*) KAA_CALLOC(1, sizeof(kaa_bootstrap_manager_t)); + KAA_RETURN_IF_NIL(*bootstrap_manager_p, KAA_ERR_NOMEM); + + (*bootstrap_manager_p)->channel_manager = kaa_context->channel_manager; + (*bootstrap_manager_p)->logger = kaa_context->logger; + (*bootstrap_manager_p)->kaa_context = kaa_context; + + (*bootstrap_manager_p)->bootstrap_access_points = kaa_list_create(); + KAA_RETURN_IF_NIL((*bootstrap_manager_p)->bootstrap_access_points, KAA_ERR_NOMEM); + + (*bootstrap_manager_p)->operations_access_points = kaa_list_create(); + KAA_RETURN_IF_NIL((*bootstrap_manager_p)->operations_access_points, KAA_ERR_NOMEM); + + return KAA_ERR_NONE; +} + +/** @deprecated Use kaa_extension_manager_deinit(). */ +void kaa_bootstrap_manager_destroy(kaa_bootstrap_manager_t *self) +{ + KAA_RETURN_IF_NIL(self,); + kaa_list_destroy(self->bootstrap_access_points, NULL); + kaa_list_destroy(self->operations_access_points, destroy_operations_access_points); + KAA_FREE(self); +} + + +static kaa_error_t kaa_bootstrap_manager_schedule_failover(kaa_bootstrap_manager_t *self, kaa_access_point_t* current_access_point, kaa_access_point_t* next_access_point, + kaa_transport_protocol_id_t *protocol_id, kaa_server_type_t type, kaa_failover_reason reason) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_BADPARAM); + kaa_failover_decision_t decision = kaa_failover_strategy_on_failover(self->kaa_context->failover_strategy, reason); + switch (decision.action) { + case KAA_NOOP: + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Nothing to be done..."); + return KAA_ERR_NONE; + case KAA_RETRY: + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to retry in %u seconds...", decision.retry_period); + self->failover_meta_info.acess_point = current_access_point; + break; + case KAA_USE_NEXT_BOOTSTRAP: + case KAA_USE_NEXT_OPERATIONS: + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to retry with another access point in %u seconds...", decision.retry_period); + self->failover_meta_info.acess_point = next_access_point; + break; + case KAA_STOP_APP: + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Stopping SDK according to the failover strategy..."); + return KAA_ERR_SDK_STOP; + } + self->failover_meta_info.next_execution_time = KAA_TIME() + decision.retry_period; + self->failover_meta_info.server = type; + if (protocol_id) { + self->failover_meta_info.protocol_id = *protocol_id; + } + self->failover_meta_info.reason = reason; + self->failover_meta_info.is_failover = true; + + return KAA_ERR_NONE; +} + +kaa_access_point_t *kaa_bootstrap_manager_get_operations_access_point(kaa_bootstrap_manager_t *self + , kaa_transport_protocol_id_t *protocol_id) +{ + KAA_RETURN_IF_NIL2(self, protocol_id, NULL); + + kaa_list_node_t *operations_access_points_it = kaa_list_find_next(kaa_list_begin(self->operations_access_points) + , &find_operations_access_points + , protocol_id); + KAA_RETURN_IF_NIL(operations_access_points_it, NULL); + + kaa_operations_access_points_t *operations_access_points = + (kaa_operations_access_points_t *)kaa_list_get_data(operations_access_points_it); + return (kaa_access_point_t *)kaa_list_get_data(operations_access_points->current_access_points); +} + +static kaa_error_t get_next_bootstrap_access_point_index(kaa_transport_protocol_id_t *protocol_id + , size_t index_from + , size_t *next_index, bool *execute_failover) +{ + KAA_RETURN_IF_NIL4(protocol_id, next_index, execute_failover, KAA_BOOTSTRAP_ACCESS_POINT_COUNT, KAA_ERR_BADPARAM); + + size_t i = index_from; + if (index_from < KAA_BOOTSTRAP_ACCESS_POINT_COUNT) { + for (; i < KAA_BOOTSTRAP_ACCESS_POINT_COUNT; ++i) { + if (kaa_transport_protocol_id_equals(&(KAA_BOOTSTRAP_ACCESS_POINTS[i].protocol_id), protocol_id)) { + *next_index = i; + return KAA_ERR_NONE; + } + } + } + i = 0; // from the beginning + for (; i < KAA_BOOTSTRAP_ACCESS_POINT_COUNT; ++i) { + if (kaa_transport_protocol_id_equals(&(KAA_BOOTSTRAP_ACCESS_POINTS[i].protocol_id), protocol_id)) { + *next_index = i; + *execute_failover = true; //execute failover + return KAA_ERR_NONE; + } + } + + return KAA_ERR_NOT_FOUND; +} + +static kaa_error_t add_bootstrap_access_point(kaa_bootstrap_manager_t *self + , size_t index) +{ + if (index >= KAA_BOOTSTRAP_ACCESS_POINT_COUNT) + return KAA_ERR_BADDATA; + + kaa_bootstrap_access_points_t *bootstrap_access_point = + (kaa_bootstrap_access_points_t *)KAA_MALLOC(sizeof(kaa_bootstrap_access_points_t)); + KAA_RETURN_IF_NIL(bootstrap_access_point, KAA_ERR_NOMEM); + + bootstrap_access_point->protocol_id = KAA_BOOTSTRAP_ACCESS_POINTS[index].protocol_id; + bootstrap_access_point->index = index; + + kaa_list_node_t *bootstrap_access_point_it = kaa_list_push_front(self->bootstrap_access_points, bootstrap_access_point); + if (!bootstrap_access_point_it) { + KAA_FREE(bootstrap_access_point); + KAA_LOG_ERROR(self->logger, KAA_ERR_NOMEM, "Failed to allocate memory for Bootstrap access point info"); + return KAA_ERR_NOMEM; + } + + return KAA_ERR_NONE; +} + +kaa_access_point_t *kaa_bootstrap_manager_get_bootstrap_access_point(kaa_bootstrap_manager_t *self + , kaa_transport_protocol_id_t *protocol_id) +{ + KAA_RETURN_IF_NIL2(self, protocol_id, NULL); + + kaa_list_node_t *bootstrap_access_points_it = kaa_list_find_next(kaa_list_begin(self->bootstrap_access_points) + , &find_bootstrap_access_points + , protocol_id); + + size_t index; + bool execute_failover = false; + + if (bootstrap_access_points_it) { + index = ((kaa_bootstrap_access_points_t *)kaa_list_get_data(bootstrap_access_points_it))->index; + } else { + kaa_error_t error_code = get_next_bootstrap_access_point_index(protocol_id, 0, &index, &execute_failover); + if (error_code) { + KAA_LOG_FATAL(self->logger, error_code, "Error: No bootstrap servers's been found. Please regenerate SDK."); + return NULL; + } + + error_code = add_bootstrap_access_point(self, index); + if (error_code) { + KAA_LOG_ERROR(self->logger, error_code, "Failed to add Bootstrap access point index" + "(protocol: id=0x%08X, version=%u)", protocol_id->id, protocol_id->version); + return NULL; + } + } + + return (kaa_access_point_t *)&(KAA_BOOTSTRAP_ACCESS_POINTS[index].access_point); +} + +kaa_error_t kaa_bootstrap_manager_handle_server_sync(kaa_bootstrap_manager_t *self + , kaa_platform_message_reader_t *reader + , uint16_t extension_options + , size_t extension_length) +{ + // Only used for logging + (void)extension_options; + (void)extension_length; + + KAA_RETURN_IF_NIL2(self, reader, KAA_ERR_BADPARAM); + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received bootstrap server sync: options %u, payload size %u", extension_options, extension_length); + + kaa_list_clear(self->operations_access_points, destroy_operations_access_points); + + + uint16_t request_id; + kaa_error_t error_code = kaa_platform_message_read(reader, &request_id, sizeof(uint16_t)); + KAA_RETURN_IF_ERR(error_code); + request_id = KAA_NTOHS(request_id); + + uint16_t access_point_count; + error_code = kaa_platform_message_read(reader, &access_point_count, sizeof(uint16_t)); + KAA_RETURN_IF_ERR(error_code); + access_point_count = KAA_NTOHS(access_point_count); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received %u access points (request_id %u)" + , access_point_count, request_id); + + if (!access_point_count) { + kaa_transport_channel_interface_t *channel = kaa_channel_manager_get_transport_channel(self->channel_manager, KAA_EXTENSION_BOOTSTRAP); + kaa_transport_protocol_id_t protocol_id = {0, 0}; + error_code = channel->get_protocol_id(channel->context, &protocol_id); + KAA_RETURN_IF_ERR(error_code); + kaa_access_point_t *acc_point = kaa_bootstrap_manager_get_bootstrap_access_point(self, &protocol_id); + + if (!acc_point) { + return KAA_ERR_SDK_STOP; + } + + kaa_list_node_t *bootstrap_access_points_it = kaa_list_find_next(kaa_list_begin(self->bootstrap_access_points) + , &find_bootstrap_access_points + , &protocol_id); + size_t next_index = 0; + bool execute_failover = false; + get_next_bootstrap_access_point_index(&protocol_id, ((kaa_bootstrap_access_points_t *) + kaa_list_get_data(bootstrap_access_points_it))->index + 1, &next_index, &execute_failover); + + kaa_access_point_t *next_acc_point = (kaa_access_point_t *)&(KAA_BOOTSTRAP_ACCESS_POINTS[next_index].access_point); + + error_code = kaa_bootstrap_manager_schedule_failover(self->kaa_context->bootstrap_manager, acc_point, next_acc_point, &protocol_id, KAA_SERVER_BOOTSTRAP, KAA_NO_OPERATION_SERVERS_RECEIVED); + if (error_code) { + return error_code; + } + + return KAA_ERR_EVENT_NOT_ATTACHED; + } + + kaa_transport_protocol_id_t protocol_id; + + while (access_point_count--) { + kaa_access_point_t *new_access_point = (kaa_access_point_t *)KAA_MALLOC(sizeof(kaa_access_point_t)); + KAA_RETURN_IF_NIL(new_access_point, KAA_ERR_NOMEM); + + error_code = kaa_platform_message_read(reader, &new_access_point->id, sizeof(uint32_t)); + KAA_RETURN_IF_ERR(error_code); + new_access_point->id = KAA_NTOHL(new_access_point->id); + + error_code = kaa_platform_message_read(reader, &protocol_id.id, sizeof(uint32_t)); + KAA_RETURN_IF_ERR(error_code); + protocol_id.id = KAA_NTOHL(protocol_id.id); + + error_code = kaa_platform_message_read(reader, &protocol_id.version, sizeof(uint16_t)); + KAA_RETURN_IF_ERR(error_code); + protocol_id.version = KAA_NTOHS(protocol_id.version); + + error_code = kaa_platform_message_read(reader, &new_access_point->connection_data_len, sizeof(uint16_t)); + KAA_RETURN_IF_ERR(error_code); + new_access_point->connection_data_len = KAA_NTOHS(new_access_point->connection_data_len); + + new_access_point->connection_data = (char *)KAA_MALLOC(new_access_point->connection_data_len); + + if (!new_access_point->connection_data || !new_access_point->connection_data_len) { + KAA_LOG_ERROR(self->logger, KAA_ERR_NOMEM, "Failed to allocate buffer for connection data, size %u" + , new_access_point->connection_data_len); + destroy_access_point(new_access_point); + return KAA_ERR_NOMEM; + } + + error_code = kaa_platform_message_read_aligned(reader + , new_access_point->connection_data + , new_access_point->connection_data_len); + if (error_code) { + destroy_access_point(new_access_point); + KAA_LOG_ERROR(self->logger, error_code, "Failed to read connection data"); + return error_code; + } + + error_code = add_operations_access_point(self, &protocol_id, new_access_point); + if (error_code) { + destroy_access_point(new_access_point); + KAA_LOG_WARN(self->logger, error_code, "Failed to add new access point " + "to channel (protocol: id=0x%08X, version=%u)", protocol_id.id, protocol_id.version); + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Added access point: access point id '%u', protocol id '0x%08X', protocol version '%u', connection data length '%u'" + , new_access_point->id, protocol_id.id, protocol_id.version, new_access_point->connection_data_len); + } + + kaa_bootstrap_manager_on_server_sync(self); + self->next_operations_request_time = 0; + + return error_code; +} + +// TODO(KAA-1089): Remove weak linkage +__attribute__((weak)) +kaa_error_t kaa_bootstrap_manager_on_access_point_failed(kaa_bootstrap_manager_t *self, + kaa_transport_protocol_id_t *protocol_id, + kaa_server_type_t type, + kaa_failover_reason reason_code) +{ + KAA_RETURN_IF_NIL2(self, protocol_id, KAA_ERR_BADPARAM); + + kaa_access_point_t *access_point = NULL; + kaa_access_point_t *prev_access_point = NULL; + bool execute_failover = false; + + if (type == KAA_SERVER_BOOTSTRAP) { + kaa_list_node_t *bootstrap_access_points_it = kaa_list_find_next(kaa_list_begin(self->bootstrap_access_points) + , &find_bootstrap_access_points + , protocol_id); + + size_t index_from = 0; + if (bootstrap_access_points_it) + index_from = ((kaa_bootstrap_access_points_t *)kaa_list_get_data(bootstrap_access_points_it))->index + 1; + + prev_access_point = (kaa_access_point_t *)&(KAA_BOOTSTRAP_ACCESS_POINTS[index_from].access_point); + size_t next_index = 0; + kaa_error_t error_code = get_next_bootstrap_access_point_index(protocol_id, index_from, &next_index, &execute_failover); + + if (error_code) { + KAA_LOG_FATAL(self->logger, error_code, "Error: No bootstrap servers's been found. Please regenerate SDK."); + return KAA_ERR_SDK_STOP; + } + + access_point = (kaa_access_point_t *)&(KAA_BOOTSTRAP_ACCESS_POINTS[next_index].access_point); + + if (bootstrap_access_points_it){ + ((kaa_bootstrap_access_points_t *)kaa_list_get_data(bootstrap_access_points_it))->index = next_index; + } else { + error_code = add_bootstrap_access_point(self, next_index); + KAA_RETURN_IF_ERR(error_code); + } + } else { + kaa_list_node_t *operations_access_points_it = kaa_list_find_next(kaa_list_begin(self->operations_access_points) + , &find_operations_access_points + , protocol_id); + KAA_RETURN_IF_NIL(operations_access_points_it, KAA_ERR_NOT_FOUND); + + kaa_operations_access_points_t *operations_access_points = + (kaa_operations_access_points_t *)kaa_list_get_data(operations_access_points_it); + if (kaa_list_get_data(operations_access_points->current_access_points)) { + prev_access_point = (kaa_access_point_t *)kaa_list_get_data(operations_access_points->current_access_points); + } + operations_access_points->current_access_points = + kaa_list_next(operations_access_points->current_access_points); + + access_point = (kaa_access_point_t *)kaa_list_get_data(operations_access_points->current_access_points); + + if (!access_point) + execute_failover = true; + } + + if (execute_failover) { + kaa_error_t error_code = kaa_bootstrap_manager_schedule_failover(self, prev_access_point, access_point, protocol_id, + type, reason_code); + + if (error_code) { + return error_code; + } + } + + if (access_point && !execute_failover) { + kaa_channel_manager_on_new_access_point(self->channel_manager + , protocol_id + , type + , access_point); + return KAA_ERR_EVENT_NOT_ATTACHED; + } else if (type == KAA_SERVER_OPERATIONS) { + KAA_LOG_WARN(self->logger, KAA_ERR_NOT_FOUND, "Could not find next Operations access point " + "(protocol: id=0x%08X, version=%u)" + , protocol_id->id, protocol_id->version); + } else if (type == KAA_SERVER_BOOTSTRAP) { + KAA_LOG_WARN(self->logger, KAA_ERR_NOT_FOUND, "Could not find next Bootstrap access point " + "(protocol: id=0x%08X, version=%u)" + , protocol_id->id, protocol_id->version); + } + + return KAA_ERR_NOT_FOUND; +} + +bool kaa_bootstrap_manager_process_failover(kaa_bootstrap_manager_t *self) +{ + KAA_RETURN_IF_NIL2(self, self->kaa_context->failover_strategy, false); + + kaa_failover_decision_t decision = kaa_failover_strategy_on_failover(self->kaa_context->failover_strategy, self->failover_meta_info.reason); + if(decision.action == KAA_NOOP) + return false; + kaa_error_t error_code = KAA_ERR_NONE; + kaa_time_t current_time = KAA_TIME(); + if (!self->failover_meta_info.is_failover) { + if (self->next_operations_request_time && current_time >= self->next_operations_request_time) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Response bootstrap time expired."); + kaa_bootstrap_access_points_t * acc_point = (kaa_bootstrap_access_points_t *) kaa_list_get_data(kaa_list_begin(self->bootstrap_access_points)); + error_code = kaa_bootstrap_manager_on_access_point_failed(self, &acc_point->protocol_id, KAA_SERVER_BOOTSTRAP, KAA_BOOTSTRAP_SERVERS_NA); + self->next_operations_request_time = 0; + if (error_code == KAA_ERR_EVENT_NOT_ATTACHED) { + do_sync(self); + return false; + } + return true; + } else { + return false; + } + } + + if (current_time >= self->failover_meta_info.next_execution_time) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Executing failover strategy..."); + switch (self->failover_meta_info.server) { + case KAA_SERVER_BOOTSTRAP: + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Processing failover of bootstraps"); + if (decision.action == KAA_RETRY) { + error_code = do_sync(self); + } else { + kaa_channel_manager_on_new_access_point(self->channel_manager, &self->failover_meta_info.protocol_id, self->failover_meta_info.server, self->failover_meta_info.acess_point); + } + break; + case KAA_SERVER_OPERATIONS: { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Processing failover of operations"); + if (decision.action == KAA_RETRY) { + kaa_channel_manager_on_new_access_point(self->channel_manager, &self->failover_meta_info.protocol_id, self->failover_meta_info.server, self->failover_meta_info.acess_point); + } else { + error_code = do_sync(self); + } + if (error_code) { + KAA_LOG_WARN(self->logger, KAA_ERR_NONE, "Failed to connect to bootstrap"); + } + break; + } + default: + KAA_LOG_ERROR(self->logger, KAA_ERR_BADDATA, "Failed to execute failover strategy: unknown server type"); + break; + } + self->failover_meta_info.is_failover = false; + } + + return true; +} + +kaa_error_t kaa_bootstrap_manager_bootstrap_request_serialize(kaa_bootstrap_manager_t *self, kaa_platform_message_writer_t* writer) +{ + KAA_RETURN_IF_NIL2(self, writer, KAA_ERR_BADPARAM); + self->next_operations_request_time = KAA_TIME() + KAA_BOOTSTRAP_RESPONSE_PERIOD; + return kaa_channel_manager_bootstrap_request_serialize(self->channel_manager, writer); +} diff --git a/client/client-multi/client-c/src/extensions/bootstrap/kaa_bootstrap_manager.h b/client/client-multi/client-c/src/extensions/bootstrap/kaa_bootstrap_manager.h new file mode 100644 index 0000000000..e12298ecac --- /dev/null +++ b/client/client-multi/client-c/src/extensions/bootstrap/kaa_bootstrap_manager.h @@ -0,0 +1,70 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * @brief Management of the Operations servers connection parameters. + * + * Manages connection parameters to Operations servers that are received from Bootstrap servers. + */ + +#ifndef KAA_BOOTSTRAP_MANAGER_H_ +#define KAA_BOOTSTRAP_MANAGER_H_ + +#include +#include "kaa_error.h" +#include "kaa_common.h" +#include "platform/ext_kaa_failover_strategy.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef KAA_BOOTSTRAP_MANAGER_T +# define KAA_BOOTSTRAP_MANAGER_T +typedef struct kaa_bootstrap_manager_t kaa_bootstrap_manager_t; +#endif + + + +typedef enum { + KAA_SERVER_BOOTSTRAP = 0, + KAA_SERVER_OPERATIONS = 1 +} kaa_server_type_t; + + + +/** + * @brief Notifies some error has occurred while using an access point. + * + * @param[in] self Bootstrap manager. + * @param[in] protocol_id Transport protocol id that failed access point belongs to. + * @param[in] type Server type that failed access point belongs to. + * @param[in] reason_code The reason of failure. + * @return Error code. + * + * @see kaa_transport_protocol_id_t + */ +kaa_error_t kaa_bootstrap_manager_on_access_point_failed(kaa_bootstrap_manager_t *self, + kaa_transport_protocol_id_t *protocol_id, + kaa_server_type_t type, + kaa_failover_reason reason_code); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* KAA_BOOTSTRAP_MANAGER_H_ */ diff --git a/client/client-multi/client-c/src/extensions/bootstrap/test/test_kaa_bootstrap_manager.c b/client/client-multi/client-c/src/extensions/bootstrap/test/test_kaa_bootstrap_manager.c new file mode 100644 index 0000000000..7d7596aa1e --- /dev/null +++ b/client/client-multi/client-c/src/extensions/bootstrap/test/test_kaa_bootstrap_manager.c @@ -0,0 +1,492 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "kaa_test.h" + +#include "kaa_context.h" +#include "kaa_defaults.h" +#include "kaa_bootstrap_manager.h" +#include "kaa_channel_manager.h" +#include "utilities/kaa_log.h" +#include "utilities/kaa_mem.h" +#include "kaa_platform_common.h" +#include "kaa_platform_protocol.h" +#include "kaa_platform_utils.h" +#include "platform/ext_transport_channel.h" +#include "platform/sock.h" + +#include "kaa_private.h" + +typedef struct { + kaa_transport_protocol_id_t protocol_info; + kaa_access_point_t *access_point; + kaa_extension_id* services; + size_t services_count; + kaa_transport_context_t transport_context; +} test_channel_context_t; + + + +static kaa_context_t kaa_context; +static kaa_logger_t *logger = NULL; +static kaa_channel_manager_t *channel_manager = NULL; + +static kaa_extension_id SUPPORTED_SERVICES[] = { KAA_EXTENSION_PROFILE + , KAA_EXTENSION_USER + , KAA_EXTENSION_EVENT + , KAA_EXTENSION_LOGGING }; + +static size_t SUPPORTED_SERVICES_COUNT = sizeof(SUPPORTED_SERVICES) / sizeof(kaa_extension_id); + + + +static kaa_error_t test_init_channel(void *channel_context + , kaa_transport_context_t *transport_context) +{ + KAA_RETURN_IF_NIL2(channel_context, transport_context, KAA_ERR_BADPARAM); + + ((test_channel_context_t *)channel_context)->transport_context = *transport_context; + return KAA_ERR_NONE; +} + +static kaa_error_t test_set_access_point(void *context + , kaa_access_point_t *access_point) +{ + KAA_RETURN_IF_NIL2(context, access_point, KAA_ERR_BADPARAM); + test_channel_context_t *channel_context = (test_channel_context_t *)context; + channel_context->access_point = access_point; + + return KAA_ERR_NONE; +} + +static kaa_error_t test_get_protocol_info(void *context, kaa_transport_protocol_id_t *protocol_info) +{ + KAA_RETURN_IF_NIL2(context, protocol_info, KAA_ERR_BADPARAM); + + test_channel_context_t *channel_context = (test_channel_context_t *)context; + *protocol_info = channel_context->protocol_info; + + return KAA_ERR_NONE; +} + +static kaa_error_t test_get_supported_services(void *context + , kaa_extension_id **supported_services + , size_t *service_count) +{ + KAA_RETURN_IF_NIL3(context, supported_services, service_count, KAA_ERR_BADPARAM); + + test_channel_context_t *channel_context = (test_channel_context_t *)context; + *supported_services = channel_context->services; + *service_count = channel_context->services_count; + + return KAA_ERR_NONE; +} + +static kaa_error_t test_sync_handler(void *context + , const kaa_extension_id services[] + , size_t service_count) +{ + KAA_RETURN_IF_NIL3(context, services, service_count, KAA_ERR_BADPARAM); + return KAA_ERR_NONE; +} + +static void test_create_channel_interface(kaa_transport_channel_interface_t *channel + , test_channel_context_t *context) +{ + channel->context = context; + channel->get_protocol_id = &test_get_protocol_info; + channel->get_supported_services = &test_get_supported_services; + channel->sync_handler = &test_sync_handler; + channel->destroy = NULL; + channel->init = &test_init_channel; + channel->set_access_point = &test_set_access_point; +} + + + +void test_create_bootstrap_manager(void **state) +{ + (void)state; + + kaa_error_t error_code; + kaa_bootstrap_manager_t* manager = NULL; + + error_code = kaa_bootstrap_manager_create(NULL, &kaa_context); + ASSERT_NOT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_bootstrap_manager_create(&manager, NULL); + ASSERT_NOT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_bootstrap_manager_create(&manager, &kaa_context); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_bootstrap_manager_destroy(manager); +} + +static kaa_access_point_t *create_access_point(void) +{ + const uint16_t MAX_CONNECTION_DATA_SIZE = 16; + + kaa_access_point_t *access_point = (kaa_access_point_t *)KAA_MALLOC(sizeof(kaa_access_point_t)); + ASSERT_NOT_NULL(access_point); + + access_point->id = rand(); + access_point->connection_data_len = 1 + rand() % MAX_CONNECTION_DATA_SIZE; + access_point->connection_data = (char *)KAA_MALLOC(access_point->connection_data_len); + + ASSERT_NOT_NULL(access_point->connection_data); + + return access_point; +} + +static void destroy_access_point(kaa_access_point_t * access_point) +{ + if (access_point) { + if (access_point->connection_data) { + KAA_FREE(access_point->connection_data); + } + KAA_FREE(access_point); + } +} + +void test_handle_server_sync(void **state) +{ + (void)state; + + kaa_error_t error_code; + kaa_bootstrap_manager_t *bootstrap_manager = NULL; + + + error_code = kaa_bootstrap_manager_create(&bootstrap_manager, &kaa_context); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_context.bootstrap_manager = bootstrap_manager; + /** + * TEST DATA + */ + kaa_transport_protocol_id_t protocol1 = { 1, 1 }; + kaa_transport_protocol_id_t protocol2 = { 2, 1 }; + kaa_transport_protocol_id_t protocol3 = { 465, 564 }; + + test_channel_context_t protocol1_channel_context = { protocol1, NULL, SUPPORTED_SERVICES, + SUPPORTED_SERVICES_COUNT, {NULL} }; + kaa_transport_channel_interface_t protocol1_channel; + test_create_channel_interface(&protocol1_channel, &protocol1_channel_context); + + test_channel_context_t protocol2_channel_context = { protocol2, NULL, SUPPORTED_SERVICES, + SUPPORTED_SERVICES_COUNT, {NULL} }; + kaa_transport_channel_interface_t protocol2_channel; + test_create_channel_interface(&protocol2_channel, &protocol2_channel_context); + + test_channel_context_t protocol3_channel_context = { protocol3, NULL, SUPPORTED_SERVICES, + SUPPORTED_SERVICES_COUNT, {NULL} }; + kaa_transport_channel_interface_t protocol3_channel; + test_create_channel_interface(&protocol3_channel, &protocol3_channel_context); + + uint32_t protocol1_channel_id; + error_code = kaa_channel_manager_add_transport_channel(channel_manager + , &protocol1_channel + , &protocol1_channel_id); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint32_t protocol3_channel_id; + error_code = kaa_channel_manager_add_transport_channel(channel_manager + , &protocol3_channel + , &protocol3_channel_id); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint16_t server_sync_payload_size = 0; + + size_t access_point_count = 0; + kaa_access_point_t *access_point1_protocol1 = create_access_point(); + ASSERT_NOT_NULL(access_point1_protocol1); + ++access_point_count; + server_sync_payload_size += kaa_aligned_size_get(access_point1_protocol1->connection_data_len); + + kaa_access_point_t *access_point2_protocol1 = create_access_point(); + ASSERT_NOT_NULL(access_point2_protocol1); + ++access_point_count; + server_sync_payload_size += kaa_aligned_size_get(access_point2_protocol1->connection_data_len); + + kaa_access_point_t *access_point1_protocol2 = create_access_point(); + ASSERT_NOT_NULL(access_point1_protocol2); + ++access_point_count; + server_sync_payload_size += kaa_aligned_size_get(access_point1_protocol2->connection_data_len); + + server_sync_payload_size += sizeof(uint16_t) + + sizeof(uint16_t) + + access_point_count * (sizeof(uint32_t) + + sizeof(uint32_t) + + sizeof(uint16_t) + + sizeof(uint16_t)); + + /** + * SERIALIZE TEST DATA + */ + char server_sync_buffer[server_sync_payload_size]; + kaa_platform_message_writer_t *writer; + error_code = kaa_platform_message_writer_create(&writer, server_sync_buffer, server_sync_payload_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint16_t network_order_16 = 0xAEF5; + uint32_t network_order_32 = 0xEAFC5370; + + // request id + error_code = kaa_platform_message_write(writer, &network_order_16, sizeof(uint16_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + network_order_16 = KAA_HTONS(access_point_count); + error_code = kaa_platform_message_write(writer, &network_order_16, sizeof(uint16_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + /** + * DO NOT CHANGE ELEMENT ORDER IN NEXT TWO ARRAYS + */ + kaa_access_point_t *access_points[] = { access_point1_protocol1 + , access_point2_protocol1 + , access_point1_protocol2 }; + + kaa_transport_protocol_id_t *protoco_infos[] = { &protocol1 + , &protocol1 + , &protocol2 }; + + for (uint16_t i = 0; i < access_point_count; ++i) { + network_order_32 = KAA_HTONL(access_points[i]->id); + error_code = kaa_platform_message_write(writer, &network_order_32, sizeof(uint32_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + network_order_32 = KAA_HTONL(protoco_infos[i]->id); + error_code = kaa_platform_message_write(writer, &network_order_32, sizeof(uint32_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + network_order_16 = KAA_HTONS(protoco_infos[i]->version); + error_code = kaa_platform_message_write(writer, &network_order_16, sizeof(uint16_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + network_order_16 = KAA_HTONS(access_points[i]->connection_data_len); + error_code = kaa_platform_message_write(writer, &network_order_16, sizeof(uint16_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_platform_message_write_aligned(writer + , access_points[i]->connection_data + , access_points[i]->connection_data_len); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + } + + kaa_platform_message_reader_t *reader = NULL; + error_code = kaa_platform_message_reader_create(&reader, server_sync_buffer, server_sync_payload_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + /** + * TEST + */ + ASSERT_NULL(protocol1_channel_context.access_point); + ASSERT_NULL(protocol2_channel_context.access_point); + ASSERT_NULL(protocol3_channel_context.access_point); + + error_code = kaa_bootstrap_manager_handle_server_sync(bootstrap_manager, reader, 0, server_sync_payload_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + ASSERT_NOT_NULL(protocol1_channel_context.access_point); + ASSERT_EQUAL(protocol1_channel_context.access_point->id, access_point2_protocol1->id); + + ASSERT_NULL(protocol3_channel_context.access_point); + + uint32_t protocol2_channel_id; + error_code = kaa_channel_manager_add_transport_channel(channel_manager + , &protocol2_channel + , &protocol2_channel_id); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_NOT_NULL(protocol2_channel_context.access_point); + ASSERT_EQUAL(protocol2_channel_context.access_point->id, access_point1_protocol2->id); + + ASSERT_NOT_NULL(protocol1_channel_context.transport_context.kaa_context); + ASSERT_NOT_NULL(protocol1_channel_context.transport_context.kaa_context->bootstrap_manager); + error_code = kaa_bootstrap_manager_on_access_point_failed(protocol1_channel_context.transport_context.kaa_context->bootstrap_manager + , &protocol1_channel_context.protocol_info + , KAA_SERVER_OPERATIONS); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_NOT_NULL(protocol1_channel_context.access_point); + ASSERT_EQUAL(protocol1_channel_context.access_point->id, access_point1_protocol1->id); + + error_code = kaa_bootstrap_manager_on_access_point_failed(protocol1_channel_context.transport_context.kaa_context->bootstrap_manager + , &protocol1_channel_context.protocol_info + , KAA_SERVER_OPERATIONS); + ASSERT_EQUAL(error_code, KAA_ERR_NOT_FOUND); + + /** + * CLEAN UP + */ + destroy_access_point(access_point1_protocol1); + destroy_access_point(access_point1_protocol2); + destroy_access_point(access_point2_protocol1); + kaa_channel_manager_remove_transport_channel(channel_manager, protocol1_channel_id); + kaa_channel_manager_remove_transport_channel(channel_manager, protocol2_channel_id); + kaa_channel_manager_remove_transport_channel(channel_manager, protocol3_channel_id); + kaa_platform_message_reader_destroy(reader); + kaa_platform_message_writer_destroy(writer); + kaa_bootstrap_manager_destroy(bootstrap_manager); + kaa_context.bootstrap_manager = NULL; +} + + + +#if KAA_BOOTSTRAP_ACCESS_POINT_COUNT > 0 + +static kaa_error_t find_bootstrap_access_point_index(kaa_transport_protocol_id_t *id + , size_t index_from + , size_t *access_point_index) +{ + KAA_RETURN_IF_NIL2(id, access_point_index, KAA_ERR_BADDATA); + + if (index_from < KAA_BOOTSTRAP_ACCESS_POINT_COUNT) { + for (size_t i = index_from; i < KAA_BOOTSTRAP_ACCESS_POINT_COUNT; ++i) { + if (0 == memcmp(id, &(KAA_BOOTSTRAP_ACCESS_POINTS[i].protocol_id), sizeof(kaa_transport_protocol_id_t))) { + *access_point_index = i; + return KAA_ERR_NONE; + } + } + } + + return KAA_ERR_NOT_FOUND; +} + +void test_bootstrap_channel(void **state) +{ + (void)state; + + kaa_error_t error_code; + kaa_bootstrap_manager_t *bootstrap_manager = NULL; + + + error_code = kaa_bootstrap_manager_create(&bootstrap_manager, &kaa_context); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_context.bootstrap_manager = bootstrap_manager; + + /** + * TEST DATA + */ + size_t index = rand() % KAA_BOOTSTRAP_ACCESS_POINT_COUNT; + kaa_extension_id bootstrap_service[] = { KAA_EXTENSION_BOOTSTRAP }; + + test_channel_context_t bootstrap_channel_context = { KAA_BOOTSTRAP_ACCESS_POINTS[index].protocol_id + , NULL + , bootstrap_service + , 1 + , {NULL} }; + kaa_transport_channel_interface_t bootstrap_channel; + test_create_channel_interface(&bootstrap_channel, &bootstrap_channel_context); + + uint32_t bootstrap_channel_id; + error_code = kaa_channel_manager_add_transport_channel(channel_manager + , &bootstrap_channel + , &bootstrap_channel_id); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + size_t expected_index; + error_code = find_bootstrap_access_point_index((kaa_transport_protocol_id_t *)&(KAA_BOOTSTRAP_ACCESS_POINTS[index].protocol_id) + , 0 + , &expected_index); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + ASSERT_NOT_NULL(bootstrap_channel_context.access_point); + ASSERT_EQUAL(bootstrap_channel_context.access_point->connection_data_len + , KAA_BOOTSTRAP_ACCESS_POINTS[expected_index].access_point.connection_data_len); + ASSERT_EQUAL(0, memcmp(bootstrap_channel_context.access_point->connection_data + , KAA_BOOTSTRAP_ACCESS_POINTS[expected_index].access_point.connection_data + , bootstrap_channel_context.access_point->connection_data_len)); + + +#if KAA_BOOTSTRAP_ACCESS_POINT_COUNT > 1 + error_code = find_bootstrap_access_point_index((kaa_transport_protocol_id_t *)&(KAA_BOOTSTRAP_ACCESS_POINTS[index].protocol_id) + , expected_index + 1 + , &expected_index); + + if (!error_code) { + error_code = kaa_bootstrap_manager_on_access_point_failed(bootstrap_manager + , (kaa_transport_protocol_id_t *)&(KAA_BOOTSTRAP_ACCESS_POINTS[index].protocol_id) + , KAA_SERVER_BOOTSTRAP); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + ASSERT_NOT_NULL(bootstrap_channel_context.access_point); + ASSERT_EQUAL(bootstrap_channel_context.access_point->connection_data_len + , KAA_BOOTSTRAP_ACCESS_POINTS[expected_index].access_point.connection_data_len); + ASSERT_EQUAL(0, memcmp(bootstrap_channel_context.access_point->connection_data + , KAA_BOOTSTRAP_ACCESS_POINTS[expected_index].access_point.connection_data + , bootstrap_channel_context.access_point->connection_data_len)); + } +#endif + + error_code = kaa_bootstrap_manager_on_access_point_failed(bootstrap_manager + , (kaa_transport_protocol_id_t *)&(KAA_BOOTSTRAP_ACCESS_POINTS[index].protocol_id) + , KAA_SERVER_BOOTSTRAP); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_bootstrap_manager_destroy(bootstrap_manager); + kaa_context.bootstrap_manager = NULL; +} +#endif + +int test_init(void) +{ + srand(time(NULL)); + + kaa_error_t error = kaa_log_create(&logger, KAA_MAX_LOG_MESSAGE_LENGTH, KAA_MAX_LOG_LEVEL, NULL); + if (error || !logger) + return error; + + kaa_context.logger = logger; + kaa_context.platform_protocol = NULL; + kaa_context.bootstrap_manager = NULL; + + error = kaa_channel_manager_create(&channel_manager, &kaa_context); + if (error || !channel_manager) + return error; + + return 0; +} + +int test_deinit(void) +{ + kaa_channel_manager_destroy(channel_manager); + kaa_log_destroy(logger); + + return 0; +} + + +#if KAA_BOOTSTRAP_ACCESS_POINT > 0 +KAA_SUITE_MAIN(Bootstrap, test_init, test_deinit, + KAA_TEST_CASE(create_bootstrap_manager, test_create_bootstrap_manager) + KAA_TEST_CASE(handle_server_sync, test_handle_server_sync) + KAA_TEST_CASE(test_bootstrap_channel, test_bootstrap_channel) +) +#else +KAA_SUITE_MAIN(Bootstrap, test_init, test_deinit, + KAA_TEST_CASE(create_bootstrap_manager, test_create_bootstrap_manager) + KAA_TEST_CASE(handle_server_sync, test_handle_server_sync) +) +#endif + diff --git a/client/client-multi/client-c/src/extensions/configuration/CMakeLists.txt b/client/client-multi/client-c/src/extensions/configuration/CMakeLists.txt new file mode 100644 index 0000000000..d03afb0031 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/configuration/CMakeLists.txt @@ -0,0 +1,33 @@ +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set(EXTENSION_CONFIGURATION_SOURCE_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_configuration_manager.c) + +set(EXTENSION_CONFIGURATION_HEADER_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_configuration_manager.h) + +add_library(extension_configuration ${EXTENSION_CONFIGURATION_SOURCE_FILES}) +target_include_directories(extension_configuration PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(extension_configuration PUBLIC kaac) + +kaa_add_unit_test(NAME test_kaa_configuration_manager + SOURCES + ${CMAKE_CURRENT_LIST_DIR}/test/test_kaa_configuration.c + ${KAA_SRC_FOLDER}/platform-impl/common/kaa_failover_strategy.c + test/kaa_test_external.c + DEPENDS + kaac extension_configuration ${OPENSSL_LIBRARIES}) diff --git a/client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager.c b/client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager.c new file mode 100644 index 0000000000..6d127fe0c9 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager.c @@ -0,0 +1,307 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaa_configuration_manager_private.h" +#include "kaa_configuration_manager.h" + +#include "kaa_private.h" + +#include +#include +#include +#include +#include "platform/stdio.h" +#include "platform/sock.h" + +#include "platform/ext_sha.h" +#include "platform/ext_configuration_persistence.h" +#include "collections/kaa_list.h" +#include "kaa_common.h" +#include "kaa_status.h" +#include "kaa_defaults.h" +#include "kaa_platform_utils.h" +#include "kaa_platform_common.h" +#include "kaa_channel_manager.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include "avro_src/avro/io.h" + +#define KAA_CONFIGURATION_RECEIVE_UPDATES_FLAG 0x01 +#define KAA_CONFIGURATION_HASH_FLAG 0x02 +#define KAA_CONFIGURATION_FULL_RESYNC_FLAG 0x04 +#define KAA_CONFIGURATION_ALL_FLAGS (KAA_CONFIGURATION_RECEIVE_UPDATES_FLAG | KAA_CONFIGURATION_HASH_FLAG | KAA_CONFIGURATION_FULL_RESYNC_FLAG) + + +#define KAA_CONFIGURATION_BODY_PRESENT 0x02 + +static kaa_extension_id configuration_sync_services[1] = { KAA_EXTENSION_CONFIGURATION }; + +struct kaa_configuration_manager_t { + kaa_digest configuration_hash; + kaa_configuration_root_receiver_t root_receiver; + kaa_root_configuration_t *root_record; + kaa_channel_manager_t *channel_manager; + kaa_status_t *status; + kaa_logger_t *logger; + size_t payload_size; +}; + +kaa_error_t kaa_extension_configuration_init(kaa_context_t *kaa_context, void **context) +{ + kaa_error_t result = kaa_configuration_manager_create(&kaa_context->configuration_manager, + kaa_context->channel_manager, + kaa_context->status->status_instance, + kaa_context->logger); + *context = kaa_context->configuration_manager; + return result; +} + +kaa_error_t kaa_extension_configuration_deinit(void *context) +{ + kaa_configuration_manager_destroy(context); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_configuration_request_get_size(void *context, size_t *expected_size) +{ + return kaa_configuration_manager_get_size(context, expected_size); +} + +kaa_error_t kaa_extension_configuration_request_serialize(void *context, uint32_t request_id, + uint8_t *buffer, size_t *size, bool *need_resync) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !size || !need_resync) { + return KAA_ERR_BADPARAM; + } + + *need_resync = true; + + size_t size_needed; + kaa_error_t error = kaa_configuration_manager_get_size(context, &size_needed); + if (error) { + return error; + } + + if (!buffer || *size < size_needed) { + *size = size_needed; + return KAA_ERR_BUFFER_IS_NOT_ENOUGH; + } + + *size = size_needed; + + kaa_platform_message_writer_t writer = KAA_MESSAGE_WRITER(buffer, *size); + error = kaa_configuration_manager_request_serialize(context, &writer); + if (error) { + return error; + } + + *size = writer.current - buffer; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_configuration_server_sync(void *context, uint32_t request_id, + uint16_t extension_options, const uint8_t *buffer, size_t size) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !buffer) { + return KAA_ERR_BADPARAM; + } + + kaa_platform_message_reader_t reader = KAA_MESSAGE_READER(buffer, size); + return kaa_configuration_manager_handle_server_sync(context, &reader, extension_options, size); +} + +static kaa_root_configuration_t *kaa_configuration_manager_deserialize(const char *buffer, size_t buffer_size) +{ + KAA_RETURN_IF_NIL2(buffer, buffer_size, NULL); + + avro_reader_t reader = avro_reader_memory(buffer, buffer_size); + KAA_RETURN_IF_NIL(reader, NULL); + kaa_root_configuration_t *result = KAA_CONFIGURATION_DESERIALIZE(reader); + avro_reader_free(reader); + return result; +} + + +/** @deprecated Use kaa_extension_configuration_init(). */ +kaa_error_t kaa_configuration_manager_create(kaa_configuration_manager_t **configuration_manager_p, kaa_channel_manager_t *channel_manager, kaa_status_t *status, kaa_logger_t *logger) +{ + KAA_RETURN_IF_NIL3(configuration_manager_p, status, logger, KAA_ERR_BADPARAM); + kaa_configuration_manager_t *manager = (kaa_configuration_manager_t *) KAA_MALLOC(sizeof(kaa_configuration_manager_t)); + KAA_RETURN_IF_NIL(manager, KAA_ERR_NOMEM); + + manager->channel_manager = channel_manager; + manager->status = status; + manager->logger = logger; + manager->root_receiver = (kaa_configuration_root_receiver_t) { NULL, NULL }; + + char *buffer = NULL; + size_t buffer_size = 0; + bool need_deallocation = false; + if (status->is_updated) + ext_configuration_read(&buffer, &buffer_size, &need_deallocation); + else + ext_configuration_delete(); + if (!buffer || !buffer_size) { + need_deallocation = false; +#if KAA_CONFIGURATION_DATA_LENGTH > 0 + buffer = (char *)KAA_CONFIGURATION_DATA; + buffer_size = KAA_CONFIGURATION_DATA_LENGTH; +#endif + } + + if (buffer && buffer_size > 0) { + ext_calculate_sha_hash(buffer, buffer_size, manager->configuration_hash); + manager->root_record = kaa_configuration_manager_deserialize(buffer, buffer_size); + + if (!manager->root_record) { + KAA_FREE(manager); + if (need_deallocation && buffer) + KAA_FREE(buffer); + return KAA_ERR_NOMEM; + } + + if (need_deallocation) + KAA_FREE(buffer); + } + + *configuration_manager_p = manager; + return KAA_ERR_NONE; +} + + + +/** @deprecated Use kaa_extension_configuration_deinit(). */ +void kaa_configuration_manager_destroy(kaa_configuration_manager_t *self) +{ + if (self) { + if (self->root_record) + self->root_record->destroy(self->root_record); + KAA_FREE(self); + } +} + + + +kaa_error_t kaa_configuration_manager_get_size(kaa_configuration_manager_t *self, size_t *expected_size) +{ + KAA_RETURN_IF_NIL2(self, expected_size, KAA_ERR_BADPARAM); + + *expected_size = KAA_EXTENSION_HEADER_SIZE + SHA_1_DIGEST_LENGTH; + self->payload_size = SHA_1_DIGEST_LENGTH; + + return KAA_ERR_NONE; +} + + + +kaa_error_t kaa_configuration_manager_request_serialize(kaa_configuration_manager_t *self, kaa_platform_message_writer_t *writer) +{ + KAA_RETURN_IF_NIL2(self, writer, KAA_ERR_BADPARAM); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to serialize client configuration sync"); + + kaa_platform_message_writer_t tmp_writer = *writer; + kaa_error_t error_code = kaa_platform_message_write_extension_header(&tmp_writer, KAA_EXTENSION_CONFIGURATION, KAA_CONFIGURATION_ALL_FLAGS, self->payload_size); + if (error_code) { + KAA_LOG_ERROR(self->logger, error_code, "Failed to write configuration extension header"); + return KAA_ERR_WRITE_FAILED; + } + + error_code = kaa_platform_message_write_aligned(&tmp_writer, self->configuration_hash, SHA_1_DIGEST_LENGTH); + if (error_code) { + KAA_LOG_ERROR(self->logger, error_code, "Failed to write configuration hash"); + return KAA_ERR_WRITE_FAILED; + } + + *writer = tmp_writer; + + return KAA_ERR_NONE; +} + + + +kaa_error_t kaa_configuration_manager_handle_server_sync(kaa_configuration_manager_t *self + , kaa_platform_message_reader_t *reader + , uint16_t extension_options + , size_t extension_length) +{ + KAA_RETURN_IF_NIL2(self, reader, KAA_ERR_BADPARAM); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received configuration server sync: options %u, payload size %u", extension_options, extension_length); + + if (extension_length >= sizeof(uint32_t)) { + if (extension_options & KAA_CONFIGURATION_BODY_PRESENT) { + uint32_t body_size = KAA_NTOHL(*((uint32_t *) reader->current)); + reader->current += sizeof(uint32_t); + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received configuration body, size '%u' ", body_size); + const uint8_t *body = reader->current; + kaa_error_t error = kaa_platform_message_skip(reader, kaa_aligned_size_get(body_size)); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read configuration body, size %u", body_size); + return error; + } + + if (self->root_record) + self->root_record->destroy(self->root_record); + + self->root_record = kaa_configuration_manager_deserialize((const char *)body, body_size); + if (!self->root_record) { + KAA_LOG_ERROR(self->logger, KAA_ERR_READ_FAILED, "Failed to deserialize configuration body, size %u", body_size); + return KAA_ERR_READ_FAILED; + } + + kaa_error_t err = ext_calculate_sha_hash((const char *)body, body_size, self->configuration_hash); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to calculate configuration body hash"); + return err; + } + ext_configuration_store((const char *)body, body_size); + + if (self->root_receiver.on_configuration_updated) + self->root_receiver.on_configuration_updated(self->root_receiver.context, self->root_record); + + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, configuration_sync_services[0]); + if (channel) + channel->sync_handler(channel->context, configuration_sync_services, 1); + } + } + return KAA_ERR_NONE; +} + + + +const kaa_root_configuration_t *kaa_configuration_manager_get_configuration(kaa_configuration_manager_t *self) +{ + return self ? self->root_record : NULL; +} + + + +kaa_error_t kaa_configuration_manager_set_root_receiver(kaa_configuration_manager_t *self, const kaa_configuration_root_receiver_t *receiver) +{ + KAA_RETURN_IF_NIL2(self, receiver, KAA_ERR_BADPARAM); + + self->root_receiver = *receiver; + + return KAA_ERR_NONE; +} diff --git a/client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager.h b/client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager.h new file mode 100644 index 0000000000..cb0597c8aa --- /dev/null +++ b/client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager.h @@ -0,0 +1,54 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KAA_CONFIGURATION_MANAGER_H_ +#define KAA_CONFIGURATION_MANAGER_H_ + +#include "gen/kaa_configuration_definitions.h" +#include "platform/ext_configuration_receiver.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct kaa_configuration_manager_t; +typedef struct kaa_configuration_manager_t kaa_configuration_manager_t; + +/** + * @brief Retrieves the current configuration data. + * + * @param[in] self The valid pointer to @link kaa_configuration_manager_t @endlink instance. + * + * @return The current configuration data (NOTE: don't modify this instance), or NULL if something went wrong. + * Don't cache this pointer, it could become invalid after the next configuration update. + */ +const kaa_root_configuration_t *kaa_configuration_manager_get_configuration(kaa_configuration_manager_t *self); + +/** + * @brief Sets the new receiver of updated configuration data. See @link kaa_configuration_root_receiver_t @endlink . + * + * @param[in] self The valid pointer to @link kaa_configuration_manager_t @endlink instance. + * @param[in] receiver The new receiver instance. This callback will be called each time when the new configuration arrives. + * + * @return Error code. + */ +kaa_error_t kaa_configuration_manager_set_root_receiver(kaa_configuration_manager_t *self, const kaa_configuration_root_receiver_t *receiver); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* KAA_CONFIGURATION_MANAGER_H_ */ diff --git a/client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager_private.h b/client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager_private.h new file mode 100644 index 0000000000..fce721cc5b --- /dev/null +++ b/client/client-multi/client-c/src/extensions/configuration/kaa_configuration_manager_private.h @@ -0,0 +1,34 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef KAA_CONFIGURATION_MANAGER_PRIVATE_H +#define KAA_CONFIGURATION_MANAGER_PRIVATE_H + +#include +#include "kaa_configuration_manager.h" +#include +#include +#include +#include + +kaa_error_t kaa_configuration_manager_create(kaa_configuration_manager_t **configuration_manager_p, + kaa_channel_manager_t *channel_manager, kaa_status_t *status, kaa_logger_t *logger); +void kaa_configuration_manager_destroy(kaa_configuration_manager_t *self); +kaa_error_t kaa_configuration_manager_get_size(kaa_configuration_manager_t *self, size_t *expected_size); +kaa_error_t kaa_configuration_manager_request_serialize(kaa_configuration_manager_t *self, + kaa_platform_message_writer_t *writer); +kaa_error_t kaa_configuration_manager_handle_server_sync(kaa_configuration_manager_t *self, kaa_platform_message_reader_t *reader, uint16_t extension_options, size_t extension_length); + +#endif /* KAA_CONFIGURATION_MANAGER_PRIVATE_H */ diff --git a/client/client-multi/client-c/src/extensions/configuration/test/test_kaa_configuration.c b/client/client-multi/client-c/src/extensions/configuration/test/test_kaa_configuration.c new file mode 100644 index 0000000000..6d81f32b57 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/configuration/test/test_kaa_configuration.c @@ -0,0 +1,150 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "kaa_configuration_manager.h" +#include "kaa_configuration_manager_private.h" + +#include "platform/sock.h" +#include "platform/ext_sha.h" +#include "platform/ext_configuration_persistence.h" +#include "platform/ext_configuration_receiver.h" +#include "kaa_test.h" +#include "kaa_platform_utils.h" +#include "kaa_status.h" +#include "kaa_defaults.h" +#include "kaa_channel_manager.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" + +#include "kaa_private.h" + +static kaa_logger_t *logger = NULL; +static kaa_status_t *status = NULL; +static kaa_configuration_manager_t *config_manager = NULL; + +#define CONFIG_START_SEQ_N 5 +#define CONFIG_NEW_SEQ_N 6 +#define CONFIG_RESPONSE_FLAGS 0x02 +#define CONFIG_DATA_FIELD "Basic configuration schema" + +static const size_t CONFIG_UUID_SIZE = 16; +static const char CONFIG_UUID[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; + +static kaa_error_t on_configuration_updated(void *context, const kaa_root_configuration_t *configuration) +{ + (void)configuration; + bool *result = (bool *) context; + *result = true; + return KAA_ERR_NONE; +} + +void test_create_request(void **state) +{ + (void)state; + + size_t expected_size = 0; + ASSERT_EQUAL(kaa_configuration_manager_get_size(config_manager, &expected_size), KAA_ERR_NONE); + ASSERT_EQUAL(expected_size, KAA_EXTENSION_HEADER_SIZE + SHA_1_DIGEST_LENGTH); + + uint8_t request_buffer[expected_size]; + kaa_platform_message_writer_t *writer = NULL; + ASSERT_EQUAL(kaa_platform_message_writer_create(&writer, request_buffer, expected_size), KAA_ERR_NONE); + ASSERT_EQUAL(kaa_configuration_manager_request_serialize(config_manager, writer), KAA_ERR_NONE); + + uint8_t *cursor = writer->begin; + ASSERT_EQUAL(KAA_HTONS(*((uint16_t *) cursor)), KAA_EXTENSION_CONFIGURATION); + cursor += sizeof(uint32_t); + + ASSERT_EQUAL(KAA_NTOHL(*((uint32_t *) cursor)), SHA_1_DIGEST_LENGTH); // checking payload size + cursor += sizeof(uint32_t); + + kaa_digest check_hash; + ext_calculate_sha_hash(KAA_CONFIGURATION_DATA, KAA_CONFIGURATION_DATA_LENGTH, check_hash); // checking configuration hash + ASSERT_EQUAL(memcmp(cursor, check_hash, SHA_1_DIGEST_LENGTH), 0); + cursor += SHA_1_DIGEST_LENGTH; + + ASSERT_EQUAL(cursor, writer->end); + + kaa_platform_message_writer_destroy(writer); +} + +void test_response(void **state) +{ + (void)state; + const size_t response_size = kaa_aligned_size_get(KAA_CONFIGURATION_DATA_LENGTH) + sizeof(uint32_t); + uint8_t response[response_size]; + uint8_t *response_cursor = response; + + *((uint32_t *) response_cursor) = KAA_HTONL(KAA_CONFIGURATION_DATA_LENGTH); + response_cursor += sizeof(uint32_t); + + memcpy(response_cursor, KAA_CONFIGURATION_DATA, KAA_CONFIGURATION_DATA_LENGTH); + + kaa_platform_message_reader_t *reader = NULL; + ASSERT_EQUAL(kaa_platform_message_reader_create(&reader, response, response_size), KAA_ERR_NONE); + + bool is_callback_invoked = false; + kaa_configuration_root_receiver_t receiver = { &is_callback_invoked, &on_configuration_updated }; + ASSERT_EQUAL(kaa_configuration_manager_set_root_receiver(config_manager, &receiver), KAA_ERR_NONE); + + ASSERT_EQUAL(kaa_configuration_manager_handle_server_sync(config_manager, reader, CONFIG_RESPONSE_FLAGS, response_size), KAA_ERR_NONE); + + ASSERT_EQUAL(is_callback_invoked, true); + + const kaa_root_configuration_t *root_config = kaa_configuration_manager_get_configuration(config_manager); + ASSERT_EQUAL(strcmp(root_config->data->data, CONFIG_DATA_FIELD), 0); + + kaa_bytes_t *uuid = (kaa_bytes_t *) root_config->__uuid->data; + ASSERT_EQUAL(uuid->size, CONFIG_UUID_SIZE); + ASSERT_EQUAL(memcmp(uuid->buffer, CONFIG_UUID, uuid->size), 0); + + kaa_platform_message_reader_destroy(reader); +} + +int test_init(void) +{ + kaa_error_t error = kaa_log_create(&logger, KAA_MAX_LOG_MESSAGE_LENGTH, KAA_MAX_LOG_LEVEL, NULL); + if (error || !logger) { + return error; + } + + error = kaa_status_create(&status); + if (error || !status) { + return error; + } + + error = kaa_configuration_manager_create(&config_manager, NULL, status, logger); + if (error || config_manager) { + return error; + } + + return 0; +} + +int test_deinit(void) +{ + kaa_status_destroy(status); + kaa_configuration_manager_destroy(config_manager); + kaa_log_destroy(logger); + return 0; +} + +KAA_SUITE_MAIN(Log, test_init, test_deinit, + KAA_TEST_CASE(create_request, test_create_request) + KAA_TEST_CASE(process_response, test_response)) diff --git a/client/client-multi/client-c/src/extensions/event/CMakeLists.txt b/client/client-multi/client-c/src/extensions/event/CMakeLists.txt new file mode 100644 index 0000000000..ee265b5abe --- /dev/null +++ b/client/client-multi/client-c/src/extensions/event/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set(EXTENSION_EVENT_SOURCE_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_event.c) + +set(EXTENSION_EVENT_HEADER_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_event.h) + +add_library(extension_event ${EXTENSION_EVENT_SOURCE_FILES}) +target_include_directories(extension_event PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(extension_event PUBLIC kaac) + +kaa_add_unit_test(NAME test_event + SOURCES + ${CMAKE_CURRENT_LIST_DIR}/test/test_kaa_event.c + test/kaa_test_external.c + DEPENDS + kaac ${OPENSSL_LIBRARIES}) diff --git a/client/client-multi/client-c/src/extensions/event/kaa_event.c b/client/client-multi/client-c/src/extensions/event/kaa_event.c new file mode 100644 index 0000000000..b4b870fd6e --- /dev/null +++ b/client/client-multi/client-c/src/extensions/event/kaa_event.c @@ -0,0 +1,1203 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaa_event.h" +#include "kaa_event_private.h" + +#include "kaa_private.h" + +#include +#include +#include +#include +#include +#include "platform/stdio.h" +#include "platform/sock.h" +#include "platform/ext_sha.h" +#include "kaa_status.h" +#include "kaa_channel_manager.h" +#include "kaa_platform_utils.h" +#include "kaa_platform_common.h" +#include "kaa_common_schema.h" +#include "collections/kaa_list.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include "platform/ext_system_logger.h" +#include "gen/kaa_event_fqn_definitions.h" + +#define KAA_EVENT_CLIENT_SYNC_EXTENSION_FLAG_RECEIVE_EVENTS 0x1 +#define KAA_EVENT_CLIENT_SYNC_EXTENSION_FLAG_SEQUENCE_NUMBER_SYNC 0x2 + +#define KAA_EVENT_SERVER_SYNC_EXTENSION_FLAG_SEQUENCE_NUMBER_PRESENT 0x1 + +#define KAA_EVENT_OPTION_TARGET_ID_PRESENT 0x1 +#define KAA_EVENT_OPTION_EVENT_HAS_DATA 0x2 + +typedef enum { + EVENT_LISTENERS_FIELD = 0x00, + EVENTS_FIELD = 0x01, +} event_server_sync_field_t; + +typedef enum { + EVENT_LISTENERS_SUCCESS = 0x00, + EVENT_LISTENERS_FAILURE = 0x01 +} event_listeners_result_t; + +typedef struct { + int32_t seq_num; + /** + * Use kaa_bytes_t for the fqn parameter (string type) to reduce strlen overhead. + * The kaa_bytes_t buffer will be null terminated. + */ + kaa_bytes_t* event_class_fqn; + kaa_bytes_t* event_data; + kaa_bytes_t* target; +} kaa_event_t; + +typedef struct { + size_t request_id; + kaa_list_t *sent_events; +} sent_events_tuple_t; + +typedef struct { + char *fqn; + kaa_event_callback_t cb; +} event_callback_pair_t; + +typedef struct { + kaa_event_block_id id; + kaa_list_t *events; +} event_transaction_t; + +typedef enum { + KAA_EVENT_SEQUENCE_NUMBER_UNSYNCHRONIZED, + KAA_EVENT_SEQUENCE_NUMBER_SYNC_IN_PROGRESS, + KAA_EVENT_SEQUENCE_NUMBER_SYNCHRONIZED +} kaa_event_sequence_number_status_t; + +typedef struct { + uint16_t request_id; + bool is_sent; + kaa_bytes_t **fqns; + size_t fqns_count; + kaa_event_listeners_callback_t callback; +} kaa_event_listeners_request_t; + +/* Public stuff */ +struct kaa_event_manager_t { + sent_events_tuple_t events_awaiting_response; + kaa_list_t *pending_events; + kaa_list_t *event_callbacks; + kaa_list_t *transactions; + kaa_list_t *event_listeners_requests; + kaa_event_block_id trx_counter; + kaa_event_callback_t global_event_callback; + size_t event_sequence_number; + size_t extension_payload_size; + kaa_event_sequence_number_status_t sequence_number_status; + + kaa_status_t *status; + kaa_channel_manager_t *channel_manager; + kaa_logger_t *logger; + + uint16_t event_listeners_request_id; + + kaa_endpoint_id_p event_source; +}; + +static kaa_extension_id event_sync_services[1] = { KAA_EXTENSION_EVENT }; + +kaa_error_t kaa_extension_event_init(kaa_context_t *kaa_context, void **context) +{ + kaa_error_t error = kaa_event_manager_create(&kaa_context->event_manager, kaa_context->status->status_instance, + kaa_context->channel_manager, kaa_context->logger); + *context = kaa_context->event_manager; + return error; +} + +kaa_error_t kaa_extension_event_deinit(void *context) +{ + kaa_event_manager_destroy(context); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_event_request_get_size(void *context, size_t *expected_size) +{ + return kaa_event_request_get_size(context, expected_size); +} + +kaa_error_t kaa_extension_event_request_serialize(void *context, uint32_t request_id, + uint8_t *buffer, size_t *size, bool *need_resync) +{ + // TODO(KAA-982): Use asserts + if (!context || !size || !need_resync) { + return KAA_ERR_BADPARAM; + } + + *need_resync = true; + + size_t size_needed; + kaa_error_t error = kaa_extension_event_request_get_size(context, &size_needed); + if (error) { + return error; + } + + if (!buffer || *size < size_needed) { + *size = size_needed; + return KAA_ERR_BUFFER_IS_NOT_ENOUGH; + } + + *size = size_needed; + + kaa_platform_message_writer_t writer = KAA_MESSAGE_WRITER(buffer, *size); + error = kaa_event_request_serialize(context, request_id, &writer); + if (error) { + return error; + } + + *size = writer.current - buffer; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_event_server_sync(void *context, uint32_t request_id, + uint16_t extension_options, const uint8_t *buffer, size_t size) +{ + // TODO(KAA-982): Use asserts + if (!context || !buffer) { + return KAA_ERR_BADPARAM; + } + + kaa_platform_message_reader_t reader = KAA_MESSAGE_READER(buffer, size); + return kaa_event_handle_server_sync(context, &reader, extension_options, size, request_id); +} + +static void destroy_event_listener_request(void *request_p) +{ + KAA_RETURN_IF_NIL(request_p,); + kaa_event_listeners_request_t *subscriber = (kaa_event_listeners_request_t *) request_p; + if (subscriber->fqns) { + size_t i = 0; + for (; i < subscriber->fqns_count; ++i) { + if (subscriber->fqns[i]) { + kaa_bytes_destroy(subscriber->fqns[i]); + } + } + KAA_FREE(subscriber->fqns); + } + KAA_FREE(subscriber); +} + +static kaa_event_listeners_request_t *create_event_listener_request(kaa_event_manager_t *self, const char *fqns[], size_t fqns_count, const kaa_event_listeners_callback_t *callback) +{ + kaa_event_listeners_request_t *result = (kaa_event_listeners_request_t *) KAA_MALLOC(sizeof(kaa_event_listeners_request_t)); + KAA_RETURN_IF_NIL(result, NULL); + + result->is_sent = false; + result->callback = *callback; + result->request_id = self->event_listeners_request_id + 1; + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to create event listeners request: fqns count %zu, request id '%u'", fqns_count, result->request_id); + + result->fqns = (kaa_bytes_t **) KAA_CALLOC(fqns_count, sizeof(kaa_bytes_t *)); + if (!result->fqns) { + destroy_event_listener_request(result); + return NULL; + } + result->fqns_count = fqns_count; + + size_t i = 0; + for (; i < fqns_count; ++i) { + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Target fqn[%d]: '%s'", i+1, fqns[i]); + result->fqns[i] = kaa_bytes_copy_create((const uint8_t *) fqns[i], strlen(fqns[i])); + if (!result->fqns[i]) { + destroy_event_listener_request(result); + return NULL; + } + } + + return result; +} + +static bool find_listeners_request_by_id(void *request_p, void *context) +{ + kaa_event_listeners_request_t *request = (kaa_event_listeners_request_t *) request_p; + uint16_t *request_id = (uint16_t *) context; + return (request && request_id && ((*request_id) == request->request_id)); +} + +static void kaa_event_destroy(void* data) +{ + if (data) { + kaa_event_t* record = (kaa_event_t*)data; + + kaa_bytes_destroy(record->event_class_fqn); + kaa_bytes_destroy(record->event_data); + kaa_bytes_destroy(record->target); + + KAA_FREE(record); + } +} + +static event_callback_pair_t *create_event_callback_pair(const char *fqn + , kaa_event_callback_t callback) +{ + event_callback_pair_t *pair = (event_callback_pair_t *) KAA_MALLOC(sizeof(event_callback_pair_t)); + KAA_RETURN_IF_NIL(pair, NULL); + + size_t fqn_length = strlen(fqn); + pair->fqn = (char *) KAA_MALLOC((fqn_length + 1) * sizeof(char)); + if (!pair->fqn) { + KAA_FREE(pair); + return NULL; + } + strcpy(pair->fqn, fqn); + pair->cb = callback; + return pair; +} + +static void kaa_event_destroy_callback_pair(void *pair_p) +{ + KAA_RETURN_IF_NIL(pair_p, ); + event_callback_pair_t *pair = (event_callback_pair_t *) pair_p; + KAA_FREE(pair->fqn); + KAA_FREE(pair); +} + +static kaa_event_callback_t find_event_callback(kaa_list_t *head, const char *fqn) +{ + kaa_list_node_t *it = kaa_list_begin(head); + while (it) { + event_callback_pair_t *pair = (event_callback_pair_t *) kaa_list_get_data(it); + if (strcmp(fqn, pair->fqn) == 0) + return pair->cb; + it = kaa_list_next(it); + } + return NULL; +} + +static event_transaction_t *create_transaction(kaa_event_block_id id) +{ + event_transaction_t *trx = (event_transaction_t *) KAA_MALLOC(sizeof(event_transaction_t)); + KAA_RETURN_IF_NIL(trx, NULL); + + trx->id = id; + trx->events = kaa_list_create(); + if (!trx->events) { + KAA_FREE(trx); + return NULL; + } + + return trx; +} + +static void destroy_transaction(void *trx_p) +{ + KAA_RETURN_IF_NIL(trx_p, ); + event_transaction_t *trx = (event_transaction_t *) trx_p; + kaa_list_destroy(trx->events, &kaa_event_destroy); + KAA_FREE(trx); +} + +static bool transaction_search_by_id_predicate(void *trx_p, void *context) +{ + event_transaction_t *trx = (event_transaction_t *) trx_p; + kaa_event_block_id *matcher = (kaa_event_block_id *) context; + return (matcher && trx) ? ((*matcher) == trx->id) : false; +} + +/** @deprecated Use kaa_extension_event_deinit(). */ +void kaa_event_manager_destroy(kaa_event_manager_t *self) +{ + if (self) { + kaa_list_destroy(self->pending_events, &kaa_event_destroy); + kaa_list_destroy(self->events_awaiting_response.sent_events, &kaa_event_destroy); + kaa_list_destroy(self->event_callbacks, &kaa_event_destroy_callback_pair); + kaa_list_destroy(self->transactions, &destroy_transaction); + kaa_list_destroy(self->event_listeners_requests, &destroy_event_listener_request); + + if (self->event_source) { + KAA_FREE((void*)self->event_source); + } + KAA_FREE(self); + } +} + +/** @deprecated Use kaa_extension_event_init(). */ +kaa_error_t kaa_event_manager_create(kaa_event_manager_t **context, kaa_status_t *status, + kaa_channel_manager_t *channel_manager, kaa_logger_t *logger) +{ + kaa_event_manager_t **event_manager_p = (kaa_event_manager_t **)context; + KAA_RETURN_IF_NIL(event_manager_p, KAA_ERR_BADPARAM); + + *event_manager_p = (kaa_event_manager_t *) KAA_MALLOC(sizeof(kaa_event_manager_t)); + KAA_RETURN_IF_NIL(*event_manager_p, KAA_ERR_NOMEM); + + (*event_manager_p)->events_awaiting_response.request_id = (size_t) -1; + (*event_manager_p)->event_listeners_request_id = 0; + (*event_manager_p)->trx_counter = 0; + (*event_manager_p)->global_event_callback = NULL; + (*event_manager_p)->event_sequence_number = status->event_seq_n; + + (*event_manager_p)->sequence_number_status = KAA_EVENT_SEQUENCE_NUMBER_UNSYNCHRONIZED; + + (*event_manager_p)->status = status; + (*event_manager_p)->channel_manager = channel_manager; + (*event_manager_p)->logger = logger; + + (*event_manager_p)->event_source = (kaa_endpoint_id_p)KAA_MALLOC(sizeof(kaa_endpoint_id)); + KAA_RETURN_IF_NIL((*event_manager_p)->event_source, KAA_ERR_NOMEM); + + (*event_manager_p)->pending_events = kaa_list_create(); + (*event_manager_p)->events_awaiting_response.sent_events = kaa_list_create(); + (*event_manager_p)->event_callbacks = kaa_list_create(); + (*event_manager_p)->transactions = kaa_list_create(); + (*event_manager_p)->event_listeners_requests = kaa_list_create(); + + if (!(*event_manager_p)->pending_events || !(*event_manager_p)->events_awaiting_response.sent_events || + !(*event_manager_p)->event_callbacks || !(*event_manager_p)->transactions || + !(*event_manager_p)->event_listeners_requests) + { + kaa_event_manager_destroy(*event_manager_p); + return KAA_ERR_NOMEM; + } + + return KAA_ERR_NONE; +} + +static kaa_error_t kaa_fill_event_structure(kaa_event_t *event + , size_t sequence_number + , const char *fqn + , const char *event_data + , size_t event_data_size + , kaa_endpoint_id_p target) +{ + KAA_RETURN_IF_NIL2(event, fqn, KAA_ERR_BADPARAM); + + event->seq_num = sequence_number; + event->event_class_fqn = kaa_bytes_copy_create((const uint8_t *)fqn + , strlen(fqn) + 1); + --event->event_class_fqn->size; + KAA_RETURN_IF_NIL(event->event_class_fqn, KAA_ERR_NOMEM); + + if (event_data && event_data_size > 0) { + event->event_data = kaa_bytes_move_create((const uint8_t *) event_data + , event_data_size + , &kaa_data_destroy); + KAA_RETURN_IF_NIL(event->event_data, KAA_ERR_NOMEM); + } + + if (target) { + event->target = kaa_bytes_copy_create(target, KAA_ENDPOINT_ID_LENGTH); + KAA_RETURN_IF_NIL(event->target, KAA_ERR_NOMEM); + } + + return KAA_ERR_NONE; +} + +/* + * @brief Sends raw event + * + * It is not recommended to use this function directly. Instead you should use + * functions contained in EventClassFamily auto-generated headers (placed at src/event/) + * + * @param[in] self Valid pointer to the event manager instance. + * @param[in] fqn Fully-qualified name of the event (null-terminated string). + * @param[in] event_data Serialized event object. + * @param[in] event_data_size Size of data in event_data parameter. + * @param[in] target The target endpoint of the event (null-terminated string). The size of + * the target parameter should be equal to @link KAA_ENDPOINT_ID_LENGTH @endlink . + * If @code NULL @endcode event will be broadcasted. + * + * @return Error code. + */ +kaa_error_t kaa_event_manager_send_event(kaa_event_manager_t *self + , const char *fqn + , const char *event_data + , size_t event_data_size + , kaa_endpoint_id_p target) +{ + /** + * Both the event data + its size and the target may be left unspecified (null). + */ + KAA_RETURN_IF_NIL(self, KAA_ERR_NOT_INITIALIZED); + KAA_RETURN_IF_NIL(fqn, KAA_ERR_EVENT_BAD_FQN); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Adding a new event \"%s\"", fqn); + + /** + * KAA_CALLOC is really needed there. + */ + kaa_event_t *event = (kaa_event_t*)KAA_CALLOC(1, sizeof(kaa_event_t)); + if (!event) { + KAA_LOG_ERROR(self->logger, KAA_ERR_NOMEM, "Failed to allocate a new event structure"); + return KAA_ERR_NOMEM; + } + + size_t new_sequence_number = (self->sequence_number_status == KAA_EVENT_SEQUENCE_NUMBER_SYNCHRONIZED ? + ++self->event_sequence_number : + (size_t) -1); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Filling a new event with data size %u", event_data_size); +# ifdef KAA_LOG_LEVEL_TRACE_ENABLED + if (target) { + char target_string[2 * KAA_ENDPOINT_ID_LENGTH + 1]; + int i = 0; + for (; i < KAA_ENDPOINT_ID_LENGTH; ++i) { + ext_snpintf(&target_string[2 * i], 3, "%02X", target[i]); + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Event target = %s", target_string); + } +# endif + kaa_error_t error = kaa_fill_event_structure(event + , new_sequence_number + , fqn + , event_data + , event_data_size + , target); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to fill a new event (size=%u)", event_data_size); + kaa_event_destroy(event); + return error; + } + + if (!kaa_list_push_back(self->pending_events, event)) { + KAA_LOG_ERROR(self->logger, KAA_ERR_NOMEM, "Failed to save a new event"); + kaa_event_destroy(event); + return KAA_ERR_NOMEM; + } + + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, event_sync_services[0]); + if (channel) + channel->sync_handler(channel->context, event_sync_services, 1); + + return KAA_ERR_NONE; +} + +static size_t kaa_event_list_get_request_size(kaa_list_t *events) +{ + size_t expected_size = 0; + + kaa_list_node_t *it = kaa_list_begin(events); + while (it) { + kaa_event_t *event = (kaa_event_t *)kaa_list_get_data(it); + + expected_size += sizeof(uint32_t) /*Event sequence number*/ + + sizeof(uint16_t) /*Event options*/ + + sizeof(uint16_t); /*Event class FQN length */ + + if (event->event_data) { + expected_size += sizeof(uint32_t); /*Event data size*/ + } + + if (event->target /*have Target Endpoint ID*/) { + expected_size += KAA_ENDPOINT_ID_LENGTH; /*Target Endpoint ID*/ + } + + expected_size += kaa_aligned_size_get(event->event_class_fqn->size); /*Event class FQN + padding */ + + if (event->event_data) { + expected_size += kaa_aligned_size_get(event->event_data->size);/*Event data + padding*/ + } + + it = kaa_list_next(it); + } + return expected_size; +} + +static kaa_error_t kaa_event_request_get_size_no_header(kaa_event_manager_t *self, size_t *expected_size) +{ + KAA_RETURN_IF_NIL2(self, expected_size, KAA_ERR_BADPARAM); + + *expected_size = 0; + if (self->sequence_number_status == KAA_EVENT_SEQUENCE_NUMBER_SYNCHRONIZED) { + bool have_events = (kaa_list_get_size(self->pending_events) > 0) || + (kaa_list_get_size(self->events_awaiting_response.sent_events) > 0); + + if (have_events) { + *expected_size += sizeof(uint32_t); // field id(1) + reserved + events count + *expected_size += kaa_event_list_get_request_size(self->pending_events); + *expected_size += kaa_event_list_get_request_size(self->events_awaiting_response.sent_events); + } + } + + if (kaa_list_get_size(self->event_listeners_requests) > 0) { + *expected_size += sizeof(uint32_t); // field id(0) + reserved + listeners count + + kaa_list_node_t *it = kaa_list_begin(self->event_listeners_requests); + while (it) { + kaa_event_listeners_request_t *request = (kaa_event_listeners_request_t *) kaa_list_get_data(it); + if (!request->is_sent) { + *expected_size += sizeof(uint32_t); // request id + fqns count + *expected_size += sizeof(uint32_t) * request->fqns_count; // fqn length + reserved + for (size_t i = 0; i < request->fqns_count; ++i) { + *expected_size += kaa_aligned_size_get(request->fqns[i]->size); + } + } + it = kaa_list_next(it); + } + } + return KAA_ERR_NONE; +} + +kaa_error_t kaa_event_request_get_size(kaa_event_manager_t *self, size_t *expected_size) +{ + KAA_RETURN_IF_NIL2(self, expected_size, KAA_ERR_BADPARAM); + + kaa_error_t error = kaa_event_request_get_size_no_header(self, expected_size); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to get event extension length"); + *expected_size = 0; + return error; + } + + self->extension_payload_size = *expected_size; + *expected_size += KAA_EXTENSION_HEADER_SIZE; + + return KAA_ERR_NONE; +} + +static kaa_error_t kaa_event_list_serialize(kaa_event_manager_t *self, kaa_list_t *events, kaa_platform_message_writer_t *writer) +{ + uint16_t temp_network_order_16 = 0; + uint16_t options = 0; + uint32_t temp_network_order_32 = 0; + + kaa_list_node_t *it = kaa_list_begin(events); + while (it) { + kaa_event_t *event = (kaa_event_t *) kaa_list_get_data(it); + + if (event->seq_num == -1) { + event->seq_num = ++self->event_sequence_number; + } + + temp_network_order_32 = KAA_HTONL(event->seq_num); + kaa_error_t error = kaa_platform_message_write(writer, &temp_network_order_32, sizeof(uint32_t)); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write event sequence number"); + return error; + } + + /** + * Event options + */ + options = (!event->target ? 0 : KAA_EVENT_OPTION_TARGET_ID_PRESENT) + | (event->event_data ? KAA_EVENT_OPTION_EVENT_HAS_DATA : 0); + options = KAA_HTONS(options); + + error = kaa_platform_message_write(writer, &options, sizeof(uint16_t)); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write event options"); + return error; + } + + temp_network_order_16 = KAA_HTONS(event->event_class_fqn->size); + error = kaa_platform_message_write(writer, &temp_network_order_16, sizeof(uint16_t)); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write event class fqn length"); + return error; + } + + if (event->event_data) { + temp_network_order_32 = KAA_HTONL(event->event_data->size); + error = kaa_platform_message_write(writer, &temp_network_order_32, sizeof(uint32_t)); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write event data size"); + return error; + } + } + + if (event->target) { + error = kaa_platform_message_write_aligned(writer, + event->target->buffer + , KAA_ENDPOINT_ID_LENGTH); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write event target id"); + return error; + } + } + + error = kaa_platform_message_write_aligned(writer + , event->event_class_fqn->buffer + , event->event_class_fqn->size); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write event class fqn aligned"); + return error; + } + + + if (event->event_data) { + error = kaa_platform_message_write_aligned(writer + , event->event_data->buffer + , event->event_data->size); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write event data aligned"); + return error; + } + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Serialized event: sqn '%u', options '%u', data size '%u', fqn '%s'" + , event->seq_num, KAA_NTOHS(options), event->event_data ? event->event_data->size : 0, event->event_class_fqn->buffer); + it = kaa_list_next(it); + } + return KAA_ERR_NONE; +} + +static kaa_error_t kaa_event_listeners_request_serialize(kaa_event_manager_t *self, kaa_platform_message_writer_t *writer, uint16_t *serialized_listeners_count) +{ + kaa_list_node_t *it = kaa_list_begin(self->event_listeners_requests); + *serialized_listeners_count = 0; + while (it) { + kaa_event_listeners_request_t *request = (kaa_event_listeners_request_t *) kaa_list_get_data(it); + if (!request->is_sent) { + *((uint16_t *) writer->current) = KAA_HTONS(request->request_id); + writer->current += sizeof(uint16_t); + *((uint16_t *) writer->current) = KAA_HTONS((uint16_t) request->fqns_count); + writer->current += sizeof(uint16_t); + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to serialize event listeners: request id '%u', fqn count '%u'" + , request->request_id, request->fqns_count); + for (size_t i = 0; i < request->fqns_count; ++i) { + size_t fqn_length = request->fqns[i]->size; + *((uint16_t *) writer->current) = KAA_HTONS((uint16_t) fqn_length); + writer->current += sizeof(uint32_t); // fqn length + reserved + kaa_error_t error = kaa_platform_message_write_aligned(writer, request->fqns[i]->buffer, fqn_length); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write event listener request"); + *serialized_listeners_count = 0; + return error; + } + } + + request->is_sent = true; + (*serialized_listeners_count)++; + } + it = kaa_list_next(it); + } + return KAA_ERR_NONE; +} + +kaa_error_t kaa_event_request_serialize(kaa_event_manager_t *self, size_t request_id, kaa_platform_message_writer_t *writer) +{ + KAA_RETURN_IF_NIL2(self, writer, KAA_ERR_BADPARAM); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to serialize client event sync"); + + /* write extension header */ + uint16_t extension_options = 0; + if (self->sequence_number_status != KAA_EVENT_SEQUENCE_NUMBER_SYNCHRONIZED) { + extension_options |= KAA_EVENT_CLIENT_SYNC_EXTENSION_FLAG_SEQUENCE_NUMBER_SYNC; + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to sync event SQN"); + } else { + extension_options |= KAA_EVENT_CLIENT_SYNC_EXTENSION_FLAG_RECEIVE_EVENTS; + } + + kaa_error_t error = kaa_platform_message_write_extension_header(writer + , KAA_EXTENSION_EVENT + , extension_options + , self->extension_payload_size); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write event extension header (ext type %u, options %X, payload size %u)" + , KAA_EXTENSION_EVENT, extension_options, self->extension_payload_size); + return error; + } + + /* write events */ + if (self->extension_payload_size) { + kaa_list_t *pending_events = self->pending_events; + kaa_list_t *resending_events = self->events_awaiting_response.sent_events; + uint16_t events_count = kaa_list_get_size(pending_events) + kaa_list_get_size(resending_events); + + if (events_count) { + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Serializing %u events", events_count); + *((uint8_t *) writer->current) = EVENTS_FIELD; + writer->current += sizeof(uint16_t); // field id + reserved + *((uint16_t *) writer->current) = KAA_HTONS(events_count); + writer->current += sizeof(uint16_t); + + error = kaa_event_list_serialize(self, resending_events, writer); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write events"); + return error; + } + + error = kaa_event_list_serialize(self, pending_events, writer); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write events"); + return error; + } + + self->events_awaiting_response.request_id = request_id; + self->events_awaiting_response.sent_events = kaa_lists_merge(self->events_awaiting_response.sent_events + , self->pending_events); + } + + if (kaa_list_get_size(self->event_listeners_requests)) { + *((uint8_t *) writer->current) = EVENT_LISTENERS_FIELD; + writer->current += sizeof(uint16_t); // field id + reserved + uint8_t *listeners_count_p = writer->current; // Pointer to the listeners count. Will be filled in later + writer->current += sizeof(uint16_t); + + uint16_t listeners_count = 0; + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Serializing event listeners"); + error = kaa_event_listeners_request_serialize(self, writer, &listeners_count); + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Serialized %u event listeners", listeners_count); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to serialize event listeners request"); + return error; + } + *((uint16_t *) listeners_count_p) = KAA_HTONS(listeners_count); + } + } + return KAA_ERR_NONE; +} + +static kaa_error_t kaa_event_read_event(kaa_event_manager_t *self, kaa_platform_message_reader_t *reader) +{ + + KAA_RETURN_IF_NIL2(self, reader, KAA_ERR_BADPARAM); + + uint16_t event_options = 0; + kaa_error_t error = kaa_platform_message_read(reader, &event_options, sizeof(uint16_t)); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read event options field"); + return error; + } + event_options = KAA_NTOHS(event_options); + + uint16_t event_class_fqn_length = 0; + error = kaa_platform_message_read(reader, &event_class_fqn_length, sizeof(uint16_t)); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read event class fqn length field"); + return error; + } + event_class_fqn_length = KAA_NTOHS(event_class_fqn_length); + + uint32_t event_data_size = 0; + if (event_options & KAA_EVENT_OPTION_EVENT_HAS_DATA) { + error = kaa_platform_message_read(reader, &event_data_size, sizeof(uint32_t)); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read event data size field"); + return error; + } + event_data_size = KAA_NTOHL(event_data_size); + } + + error = kaa_platform_message_read(reader, (void*)self->event_source, sizeof(kaa_endpoint_id)); + + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read event source endpoint id field"); + return error; + } + + bool is_enough = kaa_platform_message_is_buffer_large_enough(reader, kaa_aligned_size_get(event_class_fqn_length)); + + if (!is_enough) { + KAA_LOG_ERROR(self->logger, KAA_ERR_READ_FAILED, "Buffer size is less than event class fqn length value"); + return KAA_ERR_READ_FAILED; + } + char* event_fqn = (char *)KAA_MALLOC(event_class_fqn_length + 1); + + KAA_RETURN_IF_NIL(event_fqn, KAA_ERR_NOMEM); + + error = kaa_platform_message_read_aligned(reader, event_fqn, event_class_fqn_length); + + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read event class fqn field"); + KAA_FREE(event_fqn); + return error; + } + event_fqn[event_class_fqn_length] = '\0'; + + KAA_LOG_DEBUG(self->logger, KAA_ERR_NONE, "Processing event: options '%u', fqn length '%u', fqn '%s' ", event_options, event_class_fqn_length, event_fqn); + + kaa_event_callback_t callback = find_event_callback(self->event_callbacks, event_fqn); + if (!callback) + callback = self->global_event_callback; + + if (event_options & KAA_EVENT_OPTION_EVENT_HAS_DATA) { + const uint8_t *event_data = reader->current; + kaa_error_t error = kaa_platform_message_skip(reader, kaa_aligned_size_get(event_data_size)); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read event data, size %u", event_data_size); + KAA_FREE(event_fqn); + return error; + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Successfully retrieved event data size=%u", event_data_size); + if (callback) + (*callback)(event_fqn, (const char *)event_data, event_data_size, self->event_source); + } else if (callback) { + (*callback)(event_fqn, NULL, 0, self->event_source); + } + KAA_FREE(event_fqn); + return KAA_ERR_NONE; +} + +static kaa_error_t kaa_event_read_listeners_response(kaa_event_manager_t *self, kaa_platform_message_reader_t *reader) +{ + uint16_t request_id = KAA_NTOHS(*(uint16_t *) reader->current); + reader->current += sizeof(uint16_t); + uint16_t listeners_result = KAA_NTOHS(*(uint16_t *) reader->current); + reader->current += sizeof(uint16_t); + + uint32_t listeners_count = KAA_NTOHL(*(uint32_t *) reader->current); + reader->current += sizeof(uint32_t); + + KAA_LOG_DEBUG(self->logger, KAA_ERR_NONE, "Received %u event listener(s) on %u request" + , listeners_count, request_id); + + kaa_list_node_t *request_node = kaa_list_find_next(kaa_list_begin(self->event_listeners_requests) + , &find_listeners_request_by_id + , &request_id); + if (request_node) { + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Found event listeners callback with request id %u", request_id); + if (reader->current + (listeners_count * KAA_ENDPOINT_ID_LENGTH) > reader->end) { + KAA_LOG_ERROR(self->logger, KAA_ERR_READ_FAILED, "Failed to read endpoint ids for request id %u", request_id); + return KAA_ERR_READ_FAILED; + } + kaa_event_listeners_request_t *request = (kaa_event_listeners_request_t *) kaa_list_get_data(request_node); + if (listeners_result == EVENT_LISTENERS_SUCCESS) { + request->callback.on_event_listeners(request->callback.context, (const kaa_endpoint_id *) reader->current, listeners_count); + KAA_LOG_DEBUG(self->logger, KAA_ERR_NONE, "Success event listeners response for request id %u", request_id); + } else { + request->callback.on_event_listeners_failed(request->callback.context); + KAA_LOG_DEBUG(self->logger, KAA_ERR_NONE, "Failed to find event listeners, request id %u", request_id); + } + kaa_list_remove_at(self->event_listeners_requests, request_node, &destroy_event_listener_request); + } else { + KAA_LOG_WARN(self->logger, KAA_ERR_NOT_FOUND, "Failed to find event listeners callback with request id %u", request_id); + } + reader->current += listeners_count * KAA_ENDPOINT_ID_LENGTH; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_event_handle_server_sync(kaa_event_manager_t *self + , kaa_platform_message_reader_t *reader + , uint16_t extension_options + , size_t extension_length + , size_t request_id) +{ + KAA_RETURN_IF_NIL2(self, reader, KAA_ERR_BADPARAM); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received event server sync: options %u, payload size %u", extension_options, extension_length); + + if (extension_options & KAA_EVENT_SERVER_SYNC_EXTENSION_FLAG_SEQUENCE_NUMBER_PRESENT) { + uint32_t event_sequence_number = 0; + kaa_error_t error = kaa_platform_message_read(reader, &event_sequence_number, sizeof(uint32_t)); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read event_sequence number field"); + return error; + } + + event_sequence_number = KAA_HTONL(event_sequence_number); + extension_length -= sizeof(uint32_t); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received event sequence number '%u'", event_sequence_number); + if (self->sequence_number_status != KAA_EVENT_SEQUENCE_NUMBER_SYNCHRONIZED) { + self->sequence_number_status = KAA_EVENT_SEQUENCE_NUMBER_SYNCHRONIZED; + + if (self->event_sequence_number != event_sequence_number) { + KAA_LOG_WARN(self->logger, KAA_ERR_BAD_STATE, "Stored event sequence number is not correct (stored %u, received %u).", self->event_sequence_number, event_sequence_number); + self->event_sequence_number = event_sequence_number; + kaa_list_node_t *it = kaa_list_begin(self->pending_events); + while (it) { + kaa_event_t *event = (kaa_event_t *)kaa_list_get_data(it); + event->seq_num = ++self->event_sequence_number; + it = kaa_list_next(it); + } + } + } + if (kaa_list_get_size(self->pending_events) > 0) { + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, event_sync_services[0]); + if (channel) + channel->sync_handler(channel->context, event_sync_services, 1); + } + } + + if (request_id == self->events_awaiting_response.request_id) { + kaa_list_clear(self->events_awaiting_response.sent_events, &kaa_event_destroy); + self->events_awaiting_response.request_id = (size_t) -1; + } + + while (extension_length > 0) { + uint8_t field_id = 0; + const uint8_t *field_id_pos = reader->current; + + kaa_error_t error = kaa_platform_message_read(reader, &field_id, sizeof(uint8_t)); //read field id + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read field id in Event server sync message"); + return error; + } + + error = kaa_platform_message_skip(reader, sizeof(uint8_t)); // skip reserved + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to skip reserved field in Event server sync message"); + return error; + } + switch (field_id) { + case EVENTS_FIELD: { + uint16_t events_count = 0; + error = kaa_platform_message_read(reader, &events_count, sizeof(uint16_t)); // read events count + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read field id in Event server sync message"); + return error; + } + events_count = KAA_NTOHS(events_count); + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received %u events", events_count); + while (events_count--) { + error = kaa_event_read_event(self, reader); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read event from server sync"); + return error; + } + } + break; + } + case EVENT_LISTENERS_FIELD: { + uint16_t event_listeners_responses_count = 0; + error = kaa_platform_message_read(reader, &event_listeners_responses_count, sizeof(uint16_t)); // read events count + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read event listener response in Event server sync message"); + return error; + } + + event_listeners_responses_count = KAA_NTOHS(event_listeners_responses_count); + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received %u event listeners response(s)" + , event_listeners_responses_count); + + while (event_listeners_responses_count--) { + error = kaa_event_read_listeners_response(self, reader); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to read event listeners response"); + return error; + } + } + break; + } + default: + KAA_LOG_ERROR(self->logger, KAA_ERR_BADDATA, "Unexpected field id %u", field_id); + return KAA_ERR_BADDATA; + } + + extension_length -= (reader->current - field_id_pos); + } + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_event_manager_find_event_listeners(kaa_event_manager_t *self, const char *fqns[], size_t fqns_count, const kaa_event_listeners_callback_t *callback) +{ + KAA_RETURN_IF_NIL5(self, fqns_count, callback, callback->on_event_listeners, callback->on_event_listeners_failed, KAA_ERR_BADPARAM); + + kaa_event_listeners_request_t *subscriber = create_event_listener_request(self, fqns, fqns_count, callback); + KAA_RETURN_IF_NIL(subscriber, KAA_ERR_NOMEM); + + kaa_list_node_t *request_it = kaa_list_push_front(self->event_listeners_requests, subscriber); + if (!request_it) { + destroy_event_listener_request(subscriber); + return KAA_ERR_NOMEM; + } + + ++self->event_listeners_request_id; + + kaa_transport_channel_interface_t *channel = kaa_channel_manager_get_transport_channel(self->channel_manager, event_sync_services[0]); + if (channel) + channel->sync_handler(channel->context, event_sync_services, 1); + + return KAA_ERR_NONE; +} + +/* + * @brief Register listener to an event. + * + * It is not recommended to use this function directly. Instead you should use + * functions contained in EventClassFamily auto-generated headers (placed at src/event/) + * + * @param[in] self Valid pointer to the event manager instance. + * @param[in] fqn Fully-qualified name of the event (null-terminated string). + * If @code NULL @endcode, this callback will be invoked for + * all events which do not have registered specific callback. + * @param[in] callback Event callback function. + * + * @return Error code. + */ +kaa_error_t kaa_event_manager_add_on_event_callback(kaa_event_manager_t *self, const char *fqn, kaa_event_callback_t callback) +{ + KAA_RETURN_IF_NIL2(self, callback, KAA_ERR_BADPARAM); + if (fqn) { + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Adding callback for events, fqn '%s'", fqn); + event_callback_pair_t *pair = create_event_callback_pair(fqn, callback); + KAA_RETURN_IF_NIL(pair, KAA_ERR_NOMEM); + kaa_list_node_t *it = kaa_list_begin(self->event_callbacks); + while (it) { + event_callback_pair_t *data = (event_callback_pair_t *) kaa_list_get_data(it); + if (strcmp(fqn, data->fqn) == 0) { + kaa_list_set_data_at(it, pair, &kaa_event_destroy_callback_pair); + return KAA_ERR_NONE; + } + it = kaa_list_next(it); + } + if (!kaa_list_push_back(self->event_callbacks, pair)) { + kaa_event_destroy_callback_pair(pair); + return KAA_ERR_NOMEM; + } + } else { + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Adding global event callback"); + self->global_event_callback = callback; + } + return KAA_ERR_NONE; +} + +kaa_error_t kaa_event_create_transaction(kaa_event_manager_t *self, kaa_event_block_id *trx_id) +{ + KAA_RETURN_IF_NIL2(self, trx_id, KAA_ERR_NOT_INITIALIZED); + + *trx_id = self->trx_counter + 1; + event_transaction_t *new_transaction = create_transaction(*trx_id); + KAA_RETURN_IF_NIL(new_transaction, KAA_ERR_NOMEM); + + if (!kaa_list_push_back(self->transactions, new_transaction)) { + destroy_transaction(new_transaction); + return KAA_ERR_NOMEM; + } + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Creating new events batch, id %zu", *trx_id); + + self->trx_counter = *trx_id; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_event_finish_transaction(kaa_event_manager_t *self, kaa_event_block_id trx_id) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_NOT_INITIALIZED); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to send events from event batch, id %zu", trx_id); + + kaa_list_node_t *it = kaa_list_find_next(kaa_list_begin(self->transactions), &transaction_search_by_id_predicate, &trx_id); + if (it) { + event_transaction_t *trx = kaa_list_get_data(it); + bool need_sync = false; + if (kaa_get_max_log_level(self->logger) >= KAA_LOG_LEVEL_TRACE) { + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Events batch with id %zu has %zu events", trx_id, kaa_list_get_size(trx->events)); + } + if (kaa_list_get_size(trx->events) > 0) { + self->pending_events = kaa_lists_merge(self->pending_events, trx->events); + need_sync = true; + } + kaa_list_remove_at(self->transactions, it, &destroy_transaction); + + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, event_sync_services[0]); + if (need_sync && channel) + channel->sync_handler(channel->context, event_sync_services, 1); + + return KAA_ERR_NONE; + } + + KAA_LOG_WARN(self->logger, KAA_ERR_NOT_FOUND, "Events batch with id %zu was not created before", trx_id); + + return KAA_ERR_NOT_FOUND; +} + +kaa_error_t kaa_event_remove_transaction(kaa_event_manager_t *self, kaa_event_block_id trx_id) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_NOT_INITIALIZED); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to remove events batch with id %zu", trx_id); + + kaa_error_t error = kaa_list_remove_first(self->transactions, &transaction_search_by_id_predicate, &trx_id, &destroy_transaction); + if (error) { + KAA_LOG_WARN(self->logger, error, "Events batch with id %zu was not created before", trx_id); + } + + return error; +} + +/* + * @brief Adds a raw event to the transaction. + * + * It is not recommended to use this function directly. Instead you should use + * functions contained in EventClassFamily auto-generated headers (@code kaa_event_manager_add_*_event_to_block(...) @endcode) + * + * @param[in] self Valid pointer to the event manager instance. + * @param[in] trx_id The ID of the event block to be sent. + * @param[in] fqn Fully-qualified name of the event (null-terminated string). + * @param[in] event_data Serialized event object. + * @param[in] event_data_size Size of data in event_data parameter. + * @param[in] target The target endpoint of the event. If @code NULL @endcode event will be broadcasted. + * @param[in] target_size Size of data in target parameter. + * + * @return Error code. + */ +kaa_error_t kaa_event_manager_add_event_to_transaction(kaa_event_manager_t *self + , kaa_event_block_id trx_id + , const char *fqn + , const char *event_data + , size_t event_data_size + , kaa_endpoint_id_p target) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_NOT_INITIALIZED); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to add event to events batch, id %zu", trx_id); + + KAA_RETURN_IF_NIL(fqn, KAA_ERR_EVENT_BAD_FQN); + + kaa_list_node_t *it = kaa_list_find_next(kaa_list_begin(self->transactions), &transaction_search_by_id_predicate, &trx_id); + if (it) { + /** + * KAA_CALLOC is really needed there. + */ + kaa_event_t *event = (kaa_event_t*)KAA_CALLOC(1, sizeof(kaa_event_t)); + KAA_RETURN_IF_NIL(event, KAA_ERR_NOMEM); + + kaa_error_t error = kaa_fill_event_structure(event + , (size_t)-1 + , fqn + , event_data + , event_data_size + , target); + if (error) { + kaa_event_destroy(event); + return error; + } + + event_transaction_t *trx = kaa_list_get_data(it); + if (!kaa_list_push_back(trx->events, event)) { + kaa_event_destroy(event); + return KAA_ERR_NOMEM; + } + + return KAA_ERR_NONE; + } + + KAA_LOG_WARN(self->logger, KAA_ERR_NOT_FOUND, "Can not add event to events batch, id %zu.", trx_id); + + return KAA_ERR_EVENT_TRX_NOT_FOUND; +} + +const char *kaa_find_class_family_name(const char *fqn) +{ + size_t i = 0; + while (SUPPORTED_EVENT_CLASS_FAMILIES_SIZE - i++) { + size_t fqn_count = SUPPORTED_EVENT_CLASS_FAMILIES[SUPPORTED_EVENT_CLASS_FAMILIES_SIZE - i].supported_incoming_fqns_count; + char **fqns = SUPPORTED_EVENT_CLASS_FAMILIES[SUPPORTED_EVENT_CLASS_FAMILIES_SIZE - i].supported_incoming_fqns; + while (fqn_count--) { + if (strcmp(fqn, fqns[fqn_count]) == 0) { + return SUPPORTED_EVENT_CLASS_FAMILIES[SUPPORTED_EVENT_CLASS_FAMILIES_SIZE - i].ecf_name; + } + } + } + return NULL; +} diff --git a/client/client-multi/client-c/src/extensions/event/kaa_event.h b/client/client-multi/client-c/src/extensions/event/kaa_event.h new file mode 100644 index 0000000000..e139d3e0b7 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/event/kaa_event.h @@ -0,0 +1,107 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file kaa_event.h + * @brief Kaa event subsystem API + * + * Supplies API for Kaa event subsystem + */ + +#ifndef KAA_EVENT_H_ +#define KAA_EVENT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "kaa_error.h" +#include "platform/ext_event_listeners_callback.h" + +typedef void (*kaa_event_callback_t)(const char *event_fqn, const char *event_data, size_t event_data_size, kaa_endpoint_id_p event_source); +typedef size_t kaa_event_block_id; + +#ifndef KAA_EVENT_MANAGER_T +# define KAA_EVENT_MANAGER_T + typedef struct kaa_event_manager_t kaa_event_manager_t; +#endif + + +/** + * @brief Initiates a request to the server to search for available event listeners by given FQNs. + * + * + * @param[in] self Valid pointer to the event manager instance. + * @param[in] fqns List of FQN strings. + * @param[in] fqns_count Number of FQNs in the list. + * @param[in] callback Pointer to callback structure. + * + * @return Error code. + */ +kaa_error_t kaa_event_manager_find_event_listeners(kaa_event_manager_t *self, const char *fqns[], size_t fqns_count, const kaa_event_listeners_callback_t *callback); + + +/** + * @brief Start a new event block. + * + * Returns a new id which must be used to add an event to the block. + * + * @param[in] self Valid pointer to the event manager instance. + * @param[in,out] trx_id Pointer to the @link kaa_event_block_id @endlink instance which will be fulfilled with a corresponding ID. + * + * @return Error code. + */ +kaa_error_t kaa_event_create_transaction(kaa_event_manager_t *self, kaa_event_block_id *trx_id); + + +/** + * @brief Send all the events from the event block at once. + * + * The event block is identified by the given @p trx_id. + * + * @param[in] self Valid pointer to the event manager instance. + * @param[in] trx_id The ID of the event block to be sent. + * + * @return Error code. + */ +kaa_error_t kaa_event_finish_transaction(kaa_event_manager_t *self, kaa_event_block_id trx_id); + + +/** + * @brief Removes the event block without sending events. + * + * @param[in] self Valid pointer to the event manager instance. + * @param[in] trx_id The ID of the event block to be sent. + * + * @return Error code. + */ +kaa_error_t kaa_event_remove_transaction(kaa_event_manager_t *self, kaa_event_block_id trx_id); + +/** + * @brief Find class family name of the event by its fully-qualified name. + * + * @param[in] fqn Fully-qualified name of the event (null-terminated string). + * + * @return Null-terminated string if corresponding event class family was found, @c NULL otherwise. + */ + +const char *kaa_find_class_family_name(const char *fqn); + +#ifdef __cplusplus +} // extern "C" +#endif +#endif /* KAA_EVENT_H_ */ diff --git a/client/client-multi/client-c/src/extensions/event/kaa_event_private.h b/client/client-multi/client-c/src/extensions/event/kaa_event_private.h new file mode 100644 index 0000000000..5a6823434a --- /dev/null +++ b/client/client-multi/client-c/src/extensions/event/kaa_event_private.h @@ -0,0 +1,42 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef KAA_EVENT_PRIVATE_H +#define KAA_EVENT_PRIVATE_H + +#include +#include +#include +#include +#include + +kaa_error_t kaa_event_manager_create(kaa_event_manager_t **event_manager_p, kaa_status_t *status, + kaa_channel_manager_t *channel_manager, kaa_logger_t *logger); +void kaa_event_manager_destroy(kaa_event_manager_t *self); +kaa_error_t kaa_event_manager_send_event(kaa_event_manager_t *self, const char *fqn, const char *event_data, + size_t event_data_size, kaa_endpoint_id_p target); + +kaa_error_t kaa_event_manager_add_event_to_transaction(kaa_event_manager_t *self, kaa_event_block_id trx_id, + const char *fqn, const char *event_data, size_t event_data_size, kaa_endpoint_id_p target); + +kaa_error_t kaa_event_manager_add_on_event_callback(kaa_event_manager_t *self, const char *fqn, + kaa_event_callback_t callback); + +kaa_error_t kaa_event_request_get_size(kaa_event_manager_t *self, size_t *expected_size); +kaa_error_t kaa_event_request_serialize(kaa_event_manager_t *self, size_t request_id, + kaa_platform_message_writer_t *writer); +kaa_error_t kaa_event_handle_server_sync(kaa_event_manager_t *self, kaa_platform_message_reader_t *reader, uint16_t extension_options, size_t extension_length, size_t request_id); + +#endif /* KAA_EVENT_PRIVATE_H */ diff --git a/client/client-multi/client-c/src/extensions/event/test/test_kaa_event.c b/client/client-multi/client-c/src/extensions/event/test/test_kaa_event.c new file mode 100644 index 0000000000..9c85460ada --- /dev/null +++ b/client/client-multi/client-c/src/extensions/event/test/test_kaa_event.c @@ -0,0 +1,659 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "platform/ext_sha.h" +#include "kaa_event.h" +#include "kaa_event_private.h" + +#include "kaa_test.h" + +#include "kaa_context.h" +#include "utilities/kaa_log.h" +#include "utilities/kaa_mem.h" +#include "kaa_status.h" +#include "kaa_channel_manager.h" +#include "kaa_platform_utils.h" +#include "platform/sock.h" + +#include "kaa_private.h" + + +static int global_events_counter = 0; +static int specific_events_counter = 0; + +static kaa_context_t kaa_context; +static kaa_logger_t *logger = NULL; +static kaa_status_t *status = NULL; +static kaa_channel_manager_t *channel_manager = NULL; +static kaa_event_manager_t *event_manager = NULL; + + + +static int test_init(void); +static int test_deinit(void); + + + +void test_kaa_create_event_manager(void **state) +{ + (void)state; + + kaa_status_t* status = NULL; + kaa_status_create(&status); + kaa_event_manager_t *event_manager = NULL; + kaa_error_t err_code = kaa_event_manager_create(&event_manager, status, NULL, logger); + ASSERT_EQUAL(err_code, KAA_ERR_NONE); + ASSERT_NOT_NULL(event_manager); + kaa_event_manager_destroy(event_manager); + + KAA_FREE(status); +} + +static kaa_endpoint_id endpoint_id1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; +static kaa_endpoint_id endpoint_id2 = { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 }; +static bool is_event_listeners_cb_called = false; + +static kaa_error_t event_listeners_callback(void *context, const kaa_endpoint_id listeners[], size_t listeners_count) +{ + (void)context; + ASSERT_EQUAL(listeners_count, 2); + ASSERT_EQUAL(memcmp(listeners[0], endpoint_id1, KAA_ENDPOINT_ID_LENGTH), 0); + ASSERT_EQUAL(memcmp(listeners[1], endpoint_id2, KAA_ENDPOINT_ID_LENGTH), 0); + is_event_listeners_cb_called = true; + return KAA_ERR_NONE; +} + +static kaa_error_t event_listeners_request_failed(void *context) +{ + (void)context; + return KAA_ERR_NONE; +} + +void test_kaa_event_listeners_serialize_request(void **state) +{ + (void)state; + + size_t expected_size = KAA_EXTENSION_HEADER_SIZE + sizeof(uint32_t); // header + field id + reserved + listeners count + expected_size += sizeof(uint32_t); // request id + fqns count + expected_size += 2 * sizeof(uint32_t); // fqn length + reserved + + const char *fqns[] = { "test.fqn1", "test.fqn2" }; + const size_t fqn1_len = strlen(fqns[0]); + const size_t fqn2_len = strlen(fqns[1]); + + expected_size += kaa_aligned_size_get(fqn1_len) + kaa_aligned_size_get(fqn2_len); + + kaa_event_listeners_callback_t callback = { NULL, NULL, NULL }; + ASSERT_NOT_EQUAL(kaa_event_manager_find_event_listeners(event_manager, fqns, 2, &callback), KAA_ERR_NONE); + + callback = (kaa_event_listeners_callback_t) { NULL, &event_listeners_callback, &event_listeners_request_failed }; + ASSERT_EQUAL(kaa_event_manager_find_event_listeners(event_manager, fqns, 2, &callback), KAA_ERR_NONE); + + size_t actual_size = 0; + ASSERT_EQUAL(kaa_event_request_get_size(event_manager, &actual_size), KAA_ERR_NONE); + ASSERT_EQUAL(actual_size, expected_size); + + uint8_t buffer[actual_size]; + kaa_platform_message_writer_t *writer; + ASSERT_EQUAL(kaa_platform_message_writer_create(&writer, buffer, actual_size), KAA_ERR_NONE); + + ASSERT_EQUAL(kaa_event_request_serialize(event_manager, 1, writer), KAA_ERR_NONE); + + uint8_t *cursor = buffer + KAA_EXTENSION_HEADER_SIZE; + ASSERT_EQUAL((*(uint8_t *) cursor), 0); // verifying field id 0 + cursor += sizeof(uint16_t); // skipping field id + reserved + + ASSERT_EQUAL(KAA_NTOHS(*(uint16_t *) cursor), 1); // verifying listeners count = 1 + cursor += sizeof(uint16_t); + + ASSERT_EQUAL(KAA_NTOHS(*(uint16_t *) cursor), 1); // verifying request id = 1 + cursor += sizeof(uint16_t); + + ASSERT_EQUAL(KAA_NTOHS(*(uint16_t *) cursor), 2); // verifying FQNs count = 2 + cursor += sizeof(uint16_t); + + ASSERT_EQUAL(KAA_NTOHS(*(uint16_t *) cursor), fqn1_len); // verifying FQN 1 length + cursor += sizeof(uint32_t); // skipping FQN length + reserved + + ASSERT_EQUAL(memcmp(cursor, fqns[0], fqn1_len), 0); // verifying FQN 1 + cursor += kaa_aligned_size_get(fqn1_len); + + ASSERT_EQUAL(KAA_NTOHS(*(uint16_t *) cursor), fqn2_len); // verifying FQN 2 length + cursor += sizeof(uint32_t); // skipping FQN length + reserved + + ASSERT_EQUAL(memcmp(cursor, fqns[1], fqn2_len), 0); // verifying FQN 2 + cursor += kaa_aligned_size_get(fqn2_len); + + ASSERT_EQUAL(cursor, buffer + actual_size); + + kaa_platform_message_writer_destroy(writer); +} + +void test_kaa_event_listeners_handle_sync(void **state) +{ + (void)state; + + const uint32_t extension_size = 52; + uint8_t buffer[extension_size]; + + uint8_t *cursor = buffer; + *cursor = 0; // field id (0) + cursor += sizeof(uint16_t); // skipping field id + reserved + + *((uint16_t *) cursor) = KAA_HTONS(1); // responses count = 1 + cursor += sizeof(uint16_t); + + *((uint16_t *) cursor) = KAA_HTONS(1); // request id = 1 + cursor += sizeof(uint16_t); + + *((uint16_t *) cursor) = KAA_HTONS(0); // listener result = 0 (SUCCESS) + cursor += sizeof(uint16_t); + + *((uint32_t *) cursor) = KAA_HTONL(2); // listener count = 2 + cursor += sizeof(uint32_t); + + memcpy(cursor, endpoint_id1, KAA_ENDPOINT_ID_LENGTH); // copying endpoint id 1 + cursor += KAA_ENDPOINT_ID_LENGTH; + + memcpy(cursor, endpoint_id2, KAA_ENDPOINT_ID_LENGTH); // copying endpoint id 2 + cursor += KAA_ENDPOINT_ID_LENGTH; + + ASSERT_EQUAL(cursor, buffer + extension_size); + + kaa_platform_message_reader_t *reader; + ASSERT_EQUAL(kaa_platform_message_reader_create(&reader, buffer, extension_size), KAA_ERR_NONE); + + ASSERT_FALSE(is_event_listeners_cb_called); + ASSERT_EQUAL(kaa_event_handle_server_sync(event_manager, reader, 0, extension_size, 1), KAA_ERR_NONE); + ASSERT_TRUE(is_event_listeners_cb_called); + ASSERT_EQUAL(reader->current, reader->end); + + // Verifying that callback was removed from the manager + is_event_listeners_cb_called = false; + reader->current = reader->begin; + ASSERT_EQUAL(kaa_event_handle_server_sync(event_manager, reader, 0, extension_size, 1), KAA_ERR_NONE); + ASSERT_FALSE(is_event_listeners_cb_called); + + kaa_platform_message_reader_destroy(reader); +} + +void test_kaa_event_sync_get_size(void **state) +{ + (void)state; + + kaa_error_t error_code; + size_t expected_size = KAA_EXTENSION_HEADER_SIZE + + sizeof(uint8_t) + + sizeof(uint8_t) + + sizeof(uint16_t); + + const char *fqn = "test fqn"; + const size_t fqn_len = strlen(fqn); + + const size_t event_data_size = 16; + /** + * Allocated data will be freed automatically. + */ + const char *event_data = (const char *) KAA_MALLOC(event_data_size); + + kaa_endpoint_id target = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; + + const size_t buffer_size = KAA_EXTENSION_HEADER_SIZE + sizeof(uint32_t); + uint8_t buffer[buffer_size]; + kaa_platform_message_writer_t *writer; + error_code = kaa_platform_message_writer_create(&writer, buffer, buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + const uint32_t sequence_number = KAA_HTONL(12345); + error_code = kaa_platform_message_write(writer, &sequence_number, sizeof(uint32_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_platform_message_reader_t *reader; + error_code = kaa_platform_message_reader_create(&reader, buffer, buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_event_manager_send_event(event_manager, fqn, event_data, event_data_size, target); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + expected_size += sizeof(uint32_t) + + sizeof(uint16_t) + + sizeof(uint16_t) + + sizeof(uint32_t) + + KAA_ENDPOINT_ID_LENGTH + + kaa_aligned_size_get(fqn_len) + + kaa_aligned_size_get(event_data_size); + + error_code = kaa_event_manager_send_event(event_manager, fqn, NULL, 0, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + expected_size += sizeof(uint32_t) + + sizeof(uint16_t) + + sizeof(uint16_t) + + kaa_aligned_size_get(fqn_len); + + size_t event_sync_size; + error_code = kaa_event_request_get_size(event_manager, &event_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + ASSERT_EQUAL(event_sync_size, KAA_EXTENSION_HEADER_SIZE); + + error_code = kaa_event_handle_server_sync(event_manager, reader, 0x1, sizeof(uint32_t), 1); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_event_request_get_size(event_manager, &event_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_EQUAL(event_sync_size, expected_size); + + kaa_platform_message_writer_destroy(writer); + kaa_platform_message_reader_destroy(reader); +} + +static kaa_error_t serialize_event(kaa_platform_message_writer_t *writer + , const char *fqn + , const char *event_data + , size_t event_data_size + , kaa_endpoint_id_p target + , size_t sequence_number + , bool need_sequence_number) +{ + KAA_RETURN_IF_NIL2(writer, fqn, KAA_ERR_BADPARAM); + + kaa_error_t error_code; + + uint16_t event_options = (event_data ? 0x2 : 0) | (target ? 0x1 : 0); + uint16_t fqn_len = strlen(fqn); + + uint16_t network_order_16; + uint32_t network_order_32; + + if (need_sequence_number) { + network_order_32 = KAA_HTONL(sequence_number); + error_code = kaa_platform_message_write(writer, &network_order_32, sizeof(uint32_t)); + KAA_RETURN_IF_ERR(error_code); + } + + network_order_16 = KAA_HTONS(event_options); + error_code = kaa_platform_message_write(writer, &network_order_16, sizeof(uint16_t)); + KAA_RETURN_IF_ERR(error_code); + + network_order_16 = KAA_HTONS(fqn_len); + error_code = kaa_platform_message_write(writer, &network_order_16, sizeof(uint16_t)); + KAA_RETURN_IF_ERR(error_code); + + if (event_data) { + network_order_32 = KAA_HTONL(event_data_size); + error_code = kaa_platform_message_write(writer, &network_order_32, sizeof(uint32_t)); + KAA_RETURN_IF_ERR(error_code); + } + + if (target) { + error_code = kaa_platform_message_write_aligned(writer, target, KAA_ENDPOINT_ID_LENGTH); + KAA_RETURN_IF_ERR(error_code); + } + + error_code = kaa_platform_message_write_aligned(writer, fqn, fqn_len); + KAA_RETURN_IF_ERR(error_code); + + if (event_data) { + error_code = kaa_platform_message_write_aligned(writer, event_data, event_data_size); + KAA_RETURN_IF_ERR(error_code); + } + + return error_code; +} + + + +void test_event_sync_serialize(void **state) +{ + (void)state; + test_deinit(); + test_init(); + + kaa_error_t error_code; + + const uint8_t event_field = 1; + const uint8_t reserved_field = 0; + uint16_t event_count = 0; + + const char *fqn = "test fqn"; + /** + * Allocated data will be freed automatically. + */ + const size_t event_data_size = 16; + const char *event_data = (const char *) KAA_MALLOC(event_data_size); + + kaa_endpoint_id target = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; + + const size_t server_sync_buffer_size = KAA_EXTENSION_HEADER_SIZE + sizeof(uint32_t); + uint8_t server_sync_buffer[server_sync_buffer_size]; + kaa_platform_message_writer_t *server_sync_writer; + error_code = kaa_platform_message_writer_create(&server_sync_writer, server_sync_buffer, server_sync_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint32_t sequence_number = KAA_HTONL(12345); + error_code = kaa_platform_message_write(server_sync_writer, &sequence_number, sizeof(uint32_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + sequence_number = KAA_NTOHL(sequence_number); + + kaa_platform_message_reader_t *server_sync_reader; + error_code = kaa_platform_message_reader_create(&server_sync_reader, server_sync_buffer, server_sync_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_event_handle_server_sync(event_manager, server_sync_reader, 0x1, sizeof(uint32_t), 1); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_event_manager_send_event(event_manager, fqn, event_data, event_data_size, target); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ++event_count; + error_code = kaa_event_manager_send_event(event_manager, fqn, NULL, 0, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ++event_count; + + size_t event_sync_size = 0; + error_code = kaa_event_request_get_size(event_manager, &event_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint8_t manual_buffer[event_sync_size]; + uint8_t auto_buffer[event_sync_size]; + + uint8_t *manual_buffer_ptr = manual_buffer; + uint8_t *auto_buffer_ptr = auto_buffer; + + kaa_platform_message_writer_t *manual_writer; + error_code = kaa_platform_message_writer_create(&manual_writer, manual_buffer, event_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + kaa_platform_message_writer_t *auto_writer; + error_code = kaa_platform_message_writer_create(&auto_writer, auto_buffer, event_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_platform_message_write_extension_header(manual_writer + , KAA_EXTENSION_EVENT + , 0x1 + , event_sync_size - KAA_EXTENSION_HEADER_SIZE); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + error_code = kaa_platform_message_write(manual_writer, &event_field, sizeof(uint8_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + error_code = kaa_platform_message_write(manual_writer, &reserved_field, sizeof(uint8_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + event_count = KAA_HTONS(event_count); + error_code = kaa_platform_message_write(manual_writer, &event_count, sizeof(uint16_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = serialize_event(manual_writer, fqn, event_data, event_data_size, target, ++sequence_number, true); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + error_code = serialize_event(manual_writer, fqn, NULL, 0, NULL, ++sequence_number, true); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_event_request_serialize(event_manager, 1, auto_writer); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = (memcmp(auto_buffer_ptr, manual_buffer_ptr, KAA_EXTENSION_HEADER_SIZE) == 0 ? KAA_ERR_NONE : KAA_ERR_BADDATA); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + auto_buffer_ptr += KAA_EXTENSION_HEADER_SIZE; + manual_buffer_ptr += KAA_EXTENSION_HEADER_SIZE; + event_sync_size -= KAA_EXTENSION_HEADER_SIZE; + + ASSERT_EQUAL(*auto_buffer_ptr, *manual_buffer_ptr); + + auto_buffer_ptr += sizeof(uint16_t); + manual_buffer_ptr += sizeof(uint16_t); + event_sync_size -= sizeof(uint16_t); + + error_code = (memcmp(auto_buffer_ptr, manual_buffer_ptr, event_sync_size) == 0 ? KAA_ERR_NONE : KAA_ERR_BADDATA); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_platform_message_writer_destroy(manual_writer); + kaa_platform_message_writer_destroy(auto_writer); + kaa_platform_message_reader_destroy(server_sync_reader); + kaa_platform_message_writer_destroy(server_sync_writer); +} + + + +void global_event_cb(const char *fqn, const char *data, size_t size, kaa_endpoint_id_p source) +{ + (void)fqn; + (void)data; + (void)size; + (void)source; + global_events_counter++; +} + + + +void specific_event_cb(const char *fqn, const char *data, size_t size, kaa_endpoint_id_p source) +{ + (void)fqn; + (void)data; + (void)size; + (void)source; + specific_events_counter++; +} + +static size_t event_get_size(const char *fqn + , const char *event_data + , size_t event_data_size + , kaa_endpoint_id_p source) +{ + (void)source; + size_t size = 0; + + size += sizeof(uint16_t) /* event options */ + + sizeof(uint16_t); /* fqn */ + + if (event_data) { + size += sizeof(uint32_t); + } + + size += KAA_ENDPOINT_ID_LENGTH + + kaa_aligned_size_get(strlen(fqn)); + + if (event_data) { + size += kaa_aligned_size_get(event_data_size); + } + + return size; +} + +void test_event_blocks(void **state) +{ + (void)state; + + test_deinit(); + test_init(); + + size_t server_sync_buffer_size = sizeof(uint32_t); + + uint8_t server_sync_buffer[server_sync_buffer_size]; + memset(server_sync_buffer, 0, server_sync_buffer_size); + + kaa_platform_message_reader_t *server_sync_reader; + kaa_error_t error_code = kaa_platform_message_reader_create(&server_sync_reader, server_sync_buffer, server_sync_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + // Synchronizing sequence number + error_code = kaa_event_handle_server_sync(event_manager, server_sync_reader, 0x01, server_sync_buffer_size, 1); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_event_block_id trx_id = 0; + error_code = kaa_event_create_transaction(event_manager, &trx_id); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_TRUE(trx_id); + + const size_t event1_size = 6; + char *event1 = (char *) KAA_MALLOC(event1_size + 1); + strcpy(event1, "event1"); + error_code = kaa_event_manager_add_event_to_transaction(event_manager, trx_id, "test.fqn1", event1, event1_size, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + const size_t event2_size = 6; + char *event2 = (char *) KAA_MALLOC(event2_size + 1); + strcpy(event2, "event2"); + error_code = kaa_event_manager_add_event_to_transaction(event_manager, trx_id, "test.fqn2", event2, event2_size, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_event_manager_add_event_to_transaction(event_manager, trx_id, "test.fqn3", NULL, 0, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_event_finish_transaction(event_manager, trx_id); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + size_t expected_size = KAA_EXTENSION_HEADER_SIZE + + sizeof(uint32_t) // Events count + + 3 * sizeof(uint32_t) // Event sequence numbers + + 3 * sizeof(uint32_t) // Event options + FQN length + + 2 * sizeof(uint32_t) // Event data sizes + + kaa_aligned_size_get(event1_size) + kaa_aligned_size_get(strlen("test.fqn1")) + + kaa_aligned_size_get(event2_size) + kaa_aligned_size_get(strlen("test.fqn2")) + + kaa_aligned_size_get(strlen("test.fqn3")); + + size_t request_size = 0; + error_code = kaa_event_request_get_size(event_manager, &request_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_EQUAL(request_size, expected_size); + + kaa_platform_message_reader_destroy(server_sync_reader); +} + +void test_kaa_server_sync_with_event_callbacks(void **state) +{ + (void)state; + + test_deinit(); + test_init(); + + kaa_error_t error_code; + + const uint8_t event_field = 1; + const uint8_t reserved_field = 0; + uint16_t event_count = 2; + + const char *important_fqn = "important fqn"; + const char *unimportant_fqn = "unimportant fqn"; + + /** + * Allocated data will be freed automatically. + */ + const size_t event_data_size = 14; + const char *event_data = (const char *) KAA_MALLOC(event_data_size); + + kaa_endpoint_id source = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; + + error_code = kaa_event_manager_add_on_event_callback(event_manager, important_fqn, specific_event_cb); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + error_code = kaa_event_manager_add_on_event_callback(event_manager, NULL, global_event_cb); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + size_t server_sync_buffer_size = 0; + + server_sync_buffer_size += sizeof(uint32_t) + + sizeof(uint8_t) + + sizeof(uint8_t) + + sizeof(uint16_t) + + event_get_size(unimportant_fqn, NULL, 0, source) + + event_get_size(important_fqn, event_data, event_data_size, source); + + uint8_t server_sync_buffer[server_sync_buffer_size]; + kaa_platform_message_writer_t *server_sync_writer; + error_code = kaa_platform_message_writer_create(&server_sync_writer, server_sync_buffer, server_sync_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint32_t sequence_number = KAA_HTONL(12345); + error_code = kaa_platform_message_write(server_sync_writer, &sequence_number, sizeof(uint32_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + sequence_number = KAA_NTOHL(sequence_number); + + error_code = kaa_platform_message_write(server_sync_writer, &event_field, sizeof(uint8_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_platform_message_write(server_sync_writer, &reserved_field, sizeof(uint8_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + event_count = KAA_HTONS(event_count); + error_code = kaa_platform_message_write(server_sync_writer, &event_count, sizeof(uint16_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = serialize_event(server_sync_writer, unimportant_fqn, NULL, 0, source, 0, false); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = serialize_event(server_sync_writer, important_fqn, event_data, event_data_size, source, 0, false); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_platform_message_reader_t *server_sync_reader; + error_code = kaa_platform_message_reader_create(&server_sync_reader, server_sync_buffer, server_sync_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_event_handle_server_sync(event_manager, server_sync_reader, 0x1, server_sync_buffer_size, 1); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + ASSERT_EQUAL(global_events_counter, 1); + ASSERT_EQUAL(specific_events_counter, 1); + + kaa_platform_message_reader_destroy(server_sync_reader); + kaa_platform_message_writer_destroy(server_sync_writer); + KAA_FREE((void *) event_data); +} + +static int test_init(void) +{ + kaa_error_t error = kaa_log_create(&logger, KAA_MAX_LOG_MESSAGE_LENGTH, KAA_MAX_LOG_LEVEL, NULL); + if (error || !logger) { + return error; + } + + kaa_context.logger = logger; + + error = kaa_status_create(&status); + if (error || !status) { + return error; + } + + error = kaa_channel_manager_create(&channel_manager, &kaa_context); + if (error || !channel_manager) { + return error; + } + + error = kaa_event_manager_create(&event_manager, status, channel_manager, logger); + if (error || !event_manager) { + return error; + } + return 0; +} + + + +static int test_deinit(void) +{ + kaa_event_manager_destroy(event_manager); + kaa_channel_manager_destroy(channel_manager); + kaa_status_destroy(status); + kaa_log_destroy(logger); + return 0; +} + +KAA_SUITE_MAIN(Event, test_init, test_deinit, + KAA_TEST_CASE(create_event_manager, test_kaa_create_event_manager) + KAA_TEST_CASE(compile_event_request, test_kaa_event_sync_get_size) + KAA_TEST_CASE(event_sync_serialize, test_event_sync_serialize) + KAA_TEST_CASE(add_on_event_callback, test_kaa_server_sync_with_event_callbacks) + KAA_TEST_CASE(event_listeners_serialize_request, test_kaa_event_listeners_serialize_request) + KAA_TEST_CASE(event_listeners_handle_sync, test_kaa_event_listeners_handle_sync) + KAA_TEST_CASE(event_test_blocks, test_event_blocks)) diff --git a/client/client-multi/client-c/src/extensions/logging/CMakeLists.txt b/client/client-multi/client-c/src/extensions/logging/CMakeLists.txt new file mode 100644 index 0000000000..3780491651 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/logging/CMakeLists.txt @@ -0,0 +1,43 @@ +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set(EXTENSION_LOGGING_SOURCE_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_logging.c) + +set(EXTENSION_LOGGING_HEADER_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_logging.h) + +add_library(extension_logging ${EXTENSION_LOGGING_SOURCE_FILES}) +target_include_directories(extension_logging PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(extension_logging PUBLIC kaac) + +kaa_add_unit_test(NAME test_log + SOURCES + ${CMAKE_CURRENT_LIST_DIR}/test/test_kaa_log.c + test/kaa_test_external.c + ${KAA_SRC_FOLDER}/gen/kaa_logging_gen.c + ${KAA_SRC_FOLDER}/avro_src/io.c + ${KAA_SRC_FOLDER}/avro_src/encoding_binary.c + ${KAA_SRC_FOLDER}/collections/kaa_list.c + ${KAA_SRC_FOLDER}/utilities/kaa_log.c + ${KAA_SRC_FOLDER}/platform-impl/posix/logger.c + ${KAA_SRC_FOLDER}/kaa_platform_utils.c + ${KAA_SRC_FOLDER}/kaa_channel_manager.c + ${KAA_SRC_FOLDER}/kaa_common_schema.c + ${KAA_SRC_FOLDER}/kaa_status.c + ${KAA_SRC_FOLDER}/platform-impl/common/kaa_failover_strategy.c + DEPENDS + kaac extension_logging ${OPENSSL_LIBRARIES}) diff --git a/client/client-multi/client-c/src/extensions/logging/kaa_logging.c b/client/client-multi/client-c/src/extensions/logging/kaa_logging.c new file mode 100644 index 0000000000..020b8e4f92 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/logging/kaa_logging.c @@ -0,0 +1,845 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaa_logging_private.h" +#include "kaa_logging.h" + +#include "kaa_private.h" + +#include +#include +#include +#include +#include "platform/stdio.h" +#include "platform/sock.h" +#include "platform/time.h" +#include "platform/ext_sha.h" +#include "collections/kaa_list.h" +#include "kaa_common.h" +#include "kaa_status.h" +#include "kaa_channel_manager.h" +#include "kaa_platform_utils.h" +#include "kaa_platform_common.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include "avro_src/avro/io.h" + +#define KAA_LOGGING_RECEIVE_UPDATES_FLAG 0x01 +#define KAA_MAX_PADDING_LENGTH (KAA_ALIGNMENT - 1) + +typedef enum { + LOGGING_RESULT_SUCCESS = 0x00, + LOGGING_RESULT_FAILURE = 0x01 +} logging_sync_result_t; + +typedef struct { + /** Bucket must be threated as expired after this deadline. + * Already expired buckets marked with deadline equal to 0. + */ + kaa_time_t deadline; + uint16_t log_bucket_id; /**< ID of bucket present in storage. */ + uint16_t log_count; /**< Current logs count. */ +} timeout_info_t; + +typedef struct { + size_t size; /**< Bucket size in bytes. */ + size_t log_count; /**< Count of logs in bucket. */ + uint16_t id; /**< Bucket id. */ +} last_bucket_info_t; + +struct kaa_log_collector_t { + kaa_log_bucket_constraints_t bucket_size; + void *log_storage_context; + void *log_upload_strategy_context; + kaa_status_t *status; + kaa_channel_manager_t *channel_manager; + kaa_logger_t *logger; + kaa_list_t *timeouts; + kaa_log_delivery_listener_t log_delivery_listeners; + bool is_sync_ignored; + uint32_t log_last_id; /**< Last log record ID */ + uint16_t log_bucket_id; + last_bucket_info_t last_bucket; +}; + +static const kaa_extension_id logging_sync_services[] = {KAA_EXTENSION_LOGGING}; + +kaa_error_t kaa_extension_logging_init(kaa_context_t *kaa_context, void **context) +{ + kaa_error_t error = kaa_log_collector_create(&kaa_context->log_collector, + kaa_context->status->status_instance, + kaa_context->channel_manager, kaa_context->logger); + *context = kaa_context->log_collector; + return error; +} + +kaa_error_t kaa_extension_logging_deinit(void *context) +{ + kaa_log_collector_destroy(context); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_logging_request_get_size(void *context, size_t *expected_size) +{ + return kaa_logging_request_get_size(context, expected_size); +} + +kaa_error_t kaa_extension_logging_request_serialize(void *context, uint32_t request_id, + uint8_t *buffer, size_t *size, bool *need_resync) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !size || !need_resync) { + return KAA_ERR_BADPARAM; + } + + kaa_error_t error; + + size_t size_needed; + error = kaa_logging_request_get_size(context, &size_needed); + if (error) { + return error; + } + + *need_resync = false; + error = kaa_logging_need_logging_resync(context, need_resync); + if (error) { + return error; + } + + if (!*need_resync) { + *size = 0; + return KAA_ERR_NONE; + } + + if (!buffer || *size < size_needed) { + *size = size_needed; + return KAA_ERR_BUFFER_IS_NOT_ENOUGH; + } + + *size = size_needed; + + kaa_platform_message_writer_t writer = KAA_MESSAGE_WRITER(buffer, *size); + error = kaa_logging_request_serialize(context, &writer); + if (error) { + return error; + } + + *size = writer.current - buffer; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_logging_server_sync(void *context, uint32_t request_id, + uint16_t extension_options, const uint8_t *buffer, size_t size) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !buffer) { + return KAA_ERR_BADPARAM; + } + + kaa_platform_message_reader_t reader = KAA_MESSAGE_READER(buffer, size); + return kaa_logging_handle_server_sync(context, &reader, extension_options, size); +} + +kaa_error_t kaa_logging_need_logging_resync(kaa_log_collector_t *self, bool *result) +{ + // TODO(KAA-982): Use asserts + if (!self || !result) { + return KAA_ERR_BADPARAM; + } + + if (self->is_sync_ignored) { + *result = false; + return KAA_ERR_NONE; + } + + *result = true; + return KAA_ERR_NONE; +} + +static kaa_error_t remember_request(kaa_log_collector_t *self, uint16_t bucket_id, uint16_t count) +{ + // TODO(KAA-982): Use asserts + if (!self) { + return KAA_ERR_BADPARAM; + } + + timeout_info_t *info = KAA_MALLOC(sizeof(*info)); + if (!info) { + return KAA_ERR_NOMEM; + } + + info->log_bucket_id = bucket_id; + info->deadline = KAA_TIME() + (kaa_time_t)ext_log_upload_strategy_get_timeout(self->log_upload_strategy_context); + info->log_count = count; + + kaa_list_node_t *it = kaa_list_push_back(self->timeouts, info); + if (!it) { + KAA_FREE(info); + return KAA_ERR_NOMEM; + } + + return KAA_ERR_NONE; +} + +static bool find_by_bucket_id(void *data, void *context) +{ + // TODO(KAA-982): Use asserts + if (!data || !context) { + return false; + } + + uint16_t bucket_id = *(uint16_t *) context; + timeout_info_t *timeout_info = data; + + if (timeout_info->log_bucket_id == bucket_id) { + return true; + } + + return false; +} + + +/* Returns amount of logs in bucket */ +static size_t remove_request(kaa_log_collector_t *self, uint16_t bucket_id) +{ + kaa_list_node_t *node; + timeout_info_t *info; + size_t logs_sent = 0; + + node = kaa_list_find_next(kaa_list_begin(self->timeouts), find_by_bucket_id, &bucket_id); + + if (node) { + info = kaa_list_get_data(node); + + if (info) { + logs_sent = info->log_count; + } + + kaa_list_remove_at(self->timeouts, node, NULL); + } + + return logs_sent; +} + +/** Handles timeout event. Must be called if timeout is occurred. */ +static void handle_timeout(kaa_log_collector_t *self) +{ + // TODO(KAA-982): Use asserts + if (!self || !self->timeouts) { + return; + } + + kaa_error_t err = ext_log_upload_strategy_on_timeout(self->log_upload_strategy_context); + + bool expire_every_entry = (err == KAA_ERR_EVENT_NOT_ATTACHED); + + if (expire_every_entry) { + /* Upload strategy decided to switch an access point. All pending logs are now deemed as + * timeouted. + */ + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Access point has been switched. All buckets are expired."); + } + + for (kaa_list_node_t *it = kaa_list_begin(self->timeouts); it; it = kaa_list_next(it)) { + timeout_info_t *info = kaa_list_get_data(it); + + if (expire_every_entry || !info->deadline) { + ext_log_storage_unmark_by_bucket_id(self->log_storage_context, info->log_bucket_id); + if (self->log_delivery_listeners.on_timeout) { + kaa_log_bucket_info_t log_bucket_info = { + .bucket_id = info->log_bucket_id, + .log_count = info->log_count, + }; + + self->log_delivery_listeners.on_timeout(self->log_delivery_listeners.ctx, + &log_bucket_info); + } + } + } + + kaa_list_clear(self->timeouts, NULL); +} + +static bool is_timeout(kaa_log_collector_t *self) +{ + kaa_time_t now = KAA_TIME(); + bool timeout_occur = false; + + for (kaa_list_node_t *it = kaa_list_begin(self->timeouts); it; it = kaa_list_next(it)) { + timeout_info_t *info = kaa_list_get_data(it); + if (now >= info->deadline) { + KAA_LOG_ERROR(self->logger, KAA_ERR_TIMEOUT, + "Log delivery timeout occurred (bucket_id %u)", info->log_bucket_id); + timeout_occur = true; + /* Mark bucket as expired */ + info->deadline = 0; + } + } + + return timeout_occur; +} + +static bool is_upload_allowed(kaa_log_collector_t *self) +{ + size_t pendingCount = kaa_list_get_size(self->timeouts); + size_t allowedCount = ext_log_upload_strategy_get_max_parallel_uploads(self->log_upload_strategy_context); + + if (pendingCount >= allowedCount) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Ignore log upload: too much pending requests %zu, max allowed %zu", + pendingCount, allowedCount); + return false; + } + + return true; +} + +/** @deprecated Use kaa_extension_logging_deinit(). */ +void kaa_log_collector_destroy(kaa_log_collector_t *self) +{ + // TODO(KAA-982): Use asserts + if (!self) { + return; + } + + ext_log_upload_strategy_destroy(self->log_upload_strategy_context); + ext_log_storage_destroy(self->log_storage_context); + kaa_list_destroy(self->timeouts, NULL); + KAA_FREE(self); +} + +/** @deprecated Use kaa_extension_logging_init(). */ +kaa_error_t kaa_log_collector_create(kaa_log_collector_t **log_collector_p, + kaa_status_t *status, kaa_channel_manager_t *channel_manager, kaa_logger_t *logger) +{ + // TODO(KAA-982): Use asserts + if (!log_collector_p) { + return KAA_ERR_BADPARAM; + } + + kaa_log_collector_t *collector = KAA_MALLOC(sizeof(*collector)); + if (!collector) { + return KAA_ERR_NOMEM; + } + + collector->log_bucket_id = 0; + collector->log_last_id = 0; + collector->log_storage_context = NULL; + collector->log_upload_strategy_context = NULL; + collector->status = status; + collector->channel_manager = channel_manager; + collector->logger = logger; + collector->is_sync_ignored = false; + collector->last_bucket.log_count = 0; + collector->last_bucket.size = 0; + collector->last_bucket.id = 1; + + /* Must be overriden in _init() */ + collector->bucket_size.max_bucket_log_count = 0; + collector->bucket_size.max_bucket_size = 0; + + collector->timeouts = kaa_list_create(); + if (!collector->timeouts) { + KAA_FREE(collector); + return KAA_ERR_NOMEM; + } + + *log_collector_p = collector; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_logging_init(kaa_log_collector_t *self, void *log_storage_context, + void *log_upload_strategy_context, const kaa_log_bucket_constraints_t *bucket_sizes) +{ + // TODO(KAA-982): Use asserts + if (!self || !log_storage_context || !log_upload_strategy_context) { + return KAA_ERR_BADPARAM; + } + + ext_log_storage_destroy(self->log_storage_context); + ext_log_upload_strategy_destroy(self->log_upload_strategy_context); + + self->log_storage_context = log_storage_context; + self->log_upload_strategy_context = log_upload_strategy_context; + self->log_delivery_listeners = KAA_LOG_EMPTY_LISTENERS; + self->bucket_size = *bucket_sizes; + + KAA_LOG_DEBUG(self->logger, KAA_ERR_NONE, "Initialized log collector with log storage {%p}, log upload strategy {%p}" + , log_storage_context, log_upload_strategy_context); + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_logging_set_strategy(kaa_log_collector_t *self, void *log_upload_strategy_context) +{ + // TODO(KAA-982): Use asserts + if (!self || !log_upload_strategy_context) { + return KAA_ERR_BADPARAM; + } + + if (self->log_upload_strategy_context) { + ext_log_upload_strategy_destroy(self->log_upload_strategy_context); + } + + self->log_upload_strategy_context = log_upload_strategy_context; + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_logging_set_storage(kaa_log_collector_t *self, void *log_storage_context) +{ + // TODO(KAA-982): Use asserts + if (!self || !log_storage_context) { + return KAA_ERR_BADPARAM; + } + + if (self->log_storage_context) { + ext_log_storage_destroy(self->log_storage_context); + } + + self->log_storage_context = log_storage_context; + + return KAA_ERR_NONE; +} + +static void do_sync(kaa_log_collector_t *self) +{ + // TODO(KAA-982): Use asserts + if (!self) { + return; + } + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Initiating log upload..."); + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, logging_sync_services[0]); + if (channel) { + channel->sync_handler(channel->context, logging_sync_services, 1); + } +} + +static void update_storage(kaa_log_collector_t *self) +{ + // TODO(KAA-982): Use asserts + if (!self) { + return; + } + + switch (ext_log_upload_strategy_decide(self->log_upload_strategy_context, self->log_storage_context)) { + case UPLOAD: + if (is_upload_allowed(self)) { + do_sync(self); + } + break; + default: + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Upload will not be triggered now."); + break; + } +} + +/** Checks if there is a place for a new record */ +static bool no_more_space_in_bucket(kaa_log_collector_t *self, kaa_log_record_t *new_record) +{ + if (self->last_bucket.log_count + 1 > self->bucket_size.max_bucket_log_count + || self->last_bucket.size + new_record->size > self->bucket_size.max_bucket_size) { + return true; + } + + return false; +} + +/** Checks if the given bucket is the last bucket */ +static bool last_bucket(kaa_log_collector_t *self, uint16_t bucket) +{ + return self->last_bucket.id == bucket; +} + +/** Creates a new empty bucket */ +static void form_new_bucket(kaa_log_collector_t *self) +{ + self->last_bucket.id++; + // Avoid hitting reserved values + if (!self->last_bucket.id) { + self->last_bucket.id++; + } + + self->last_bucket.log_count = 0; + self->last_bucket.size = 0; +} + +/** Puts the given record in the current bucket */ +static void place_record_in_current_bucket(kaa_log_collector_t *self, kaa_log_record_t *new_record) +{ + new_record->bucket_id = self->last_bucket.id; + self->last_bucket.log_count++; + self->last_bucket.size += new_record->size; +} + +/** Creates the next bucket and puts the given record there */ +static void place_record_in_next_bucket(kaa_log_collector_t *self, kaa_log_record_t *new_record) +{ + form_new_bucket(self); + place_record_in_current_bucket(self, new_record); +} + +kaa_error_t kaa_logging_add_record(kaa_log_collector_t *self, kaa_user_log_record_t *entry, kaa_log_record_info_t *log_info) +{ + // TODO(KAA-982): Use asserts + if (!self || !entry) { + return KAA_ERR_BADPARAM; + } + + // TODO(KAA-982): Use asserts + if (!self->log_storage_context) { + return KAA_ERR_NOT_INITIALIZED; + } + + // Bucket ID will be incremented only if it will be added without errors + kaa_log_record_t record = { + .data = NULL, + .size = entry->get_size(entry), + .bucket_id = 0, + }; + + if (!record.size) { + KAA_LOG_ERROR(self->logger, KAA_ERR_BADDATA, + "Failed to add log record: serialized record size is null. " + "Maybe log record schema is empty"); + return KAA_ERR_BADDATA; + } + + bool next_bucket_required = no_more_space_in_bucket(self, &record); + + // To restore last bucket state in case of error + last_bucket_info_t fallback = self->last_bucket; + + // Put log in the next bucket if required + if (next_bucket_required) { + place_record_in_next_bucket(self, &record); + } else { + place_record_in_current_bucket(self, &record); + } + + kaa_error_t error = ext_log_storage_allocate_log_record_buffer(self->log_storage_context, + &record); + if (error) { + KAA_LOG_ERROR(self->logger, error, + "Failed to add log record: cannot allocate buffer for log record"); + goto err_buffer; + } + + avro_writer_t writer = avro_writer_memory((char *)record.data, record.size); + if (!writer) { + error = KAA_ERR_NOMEM; + KAA_LOG_ERROR(self->logger, error, + "Failed to add log record: cannot create serializer"); + ext_log_storage_deallocate_log_record_buffer(self->log_storage_context, &record); + goto err_avro; + } + + entry->serialize(writer, entry); + avro_writer_free(writer); + + error = ext_log_storage_add_log_record(self->log_storage_context, &record); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to add log record to storage"); + ext_log_storage_deallocate_log_record_buffer(self->log_storage_context, &record); + goto err_add; + } + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Added log record, size %zu, bucket %u", + record.size, + record.bucket_id); + + if (is_timeout(self)) { + handle_timeout(self); + } else { + update_storage(self); + } + + if (log_info) { + log_info->bucket_id = self->last_bucket.id; + log_info->log_id = self->log_last_id++; + } + + return KAA_ERR_NONE; + +err_add: +err_avro: + ext_log_storage_deallocate_log_record_buffer(self->log_storage_context, &record); +err_buffer: + self->last_bucket = fallback; + return error; +} + +kaa_error_t kaa_logging_set_listeners(kaa_log_collector_t *self, const kaa_log_delivery_listener_t *listeners) +{ + // TODO(KAA-982): Use asserts + if (!self || !listeners) { + return KAA_ERR_BADPARAM; + } + + self->log_delivery_listeners = *listeners; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_logging_request_get_size(kaa_log_collector_t *self, size_t *expected_size) +{ + // TODO(KAA-982): Use asserts + if (!self || !expected_size) { + return KAA_ERR_BADPARAM; + } + + // TODO(KAA-982): Use asserts + if (!self->log_storage_context) { + return KAA_ERR_NOT_INITIALIZED; + } + + *expected_size = 0; + + if (!is_upload_allowed(self)) { + self->is_sync_ignored = true; + return KAA_ERR_NONE; + } + + size_t records_count = ext_log_storage_get_records_count(self->log_storage_context); + size_t total_size = ext_log_storage_get_total_size(self->log_storage_context); + + self->is_sync_ignored = (!records_count || !total_size); + + if (!self->is_sync_ignored) { + *expected_size = KAA_EXTENSION_HEADER_SIZE; + *expected_size += sizeof(uint32_t); // request id + log records count + + size_t actual_size = records_count * sizeof(uint32_t) + records_count * KAA_MAX_PADDING_LENGTH + total_size; + size_t bucket_size = self->bucket_size.max_bucket_size; + + *expected_size += ((actual_size > bucket_size) ? bucket_size : actual_size); + } + + return KAA_ERR_NONE; +} + + + +kaa_error_t kaa_logging_request_serialize(kaa_log_collector_t *self, kaa_platform_message_writer_t *writer) +{ + // TODO(KAA-982): Use asserts + if (!self || !writer) { + return KAA_ERR_BADPARAM; + } + + // TODO(KAA-982): Use asserts + if (!self->log_storage_context) { + return KAA_ERR_NOT_INITIALIZED; + } + + if (self->is_sync_ignored) { + return KAA_ERR_NONE; + } + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to serialize client logging sync"); + + kaa_platform_message_writer_t tmp_writer = *writer; + + uint8_t *extension_size_p = tmp_writer.current + sizeof(uint32_t); // Pointer to the extension size. Will be filled in later. + kaa_error_t error = kaa_platform_message_write_extension_header(&tmp_writer, + KAA_EXTENSION_LOGGING, + KAA_LOGGING_RECEIVE_UPDATES_FLAG, + 0); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to write log extension header"); + return KAA_ERR_WRITE_FAILED; + } + + uint8_t *bucket_id_p = tmp_writer.current; + tmp_writer.current += sizeof(uint16_t); + uint8_t *records_count_p = tmp_writer.current; // Pointer to the records count. Will be filled in later. + tmp_writer.current += sizeof(uint16_t); + + uint16_t bucket_id; + uint16_t first_bucket = 0; + + /* Bucket size constraints */ + + size_t bucket_size = self->bucket_size.max_bucket_size; + size_t max_log_count = self->bucket_size.max_bucket_log_count; + size_t actual_size = (tmp_writer.end - tmp_writer.current); + + bucket_size = (actual_size > bucket_size ? bucket_size : actual_size); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, + "Extracting log records... (bucket size %zu)", bucket_size); + + uint16_t records_count = 0; + + while (!error && bucket_size > sizeof(uint32_t) && records_count < max_log_count) { + size_t record_len = 0; + error = ext_log_storage_write_next_record(self->log_storage_context, + (char *)tmp_writer.current + sizeof(uint32_t), + bucket_size - sizeof(uint32_t), + &bucket_id, + &record_len); + + switch (error) { + case KAA_ERR_NONE: + if (first_bucket == 0) { + first_bucket = bucket_id; + } else if (bucket_id != first_bucket) { + // Put back log item if it is in another bucket + ext_log_storage_unmark_by_bucket_id(self->log_storage_context, bucket_id); + break; + } + + ++records_count; + *((uint32_t *) tmp_writer.current) = KAA_HTONL(record_len); + tmp_writer.current += (sizeof(uint32_t) + record_len); + kaa_platform_message_write_alignment(&tmp_writer); + bucket_size -= kaa_aligned_size_get(record_len) + sizeof(uint32_t); + break; + case KAA_ERR_NOT_FOUND: + case KAA_ERR_INSUFFICIENT_BUFFER: + // These errors are normal if they appear after at least one record got serialized + if (!records_count) { + KAA_LOG_ERROR(self->logger, error, "Failed to write the log record"); + return error; + } + break; + default: + KAA_LOG_ERROR(self->logger, error, "Failed to write the log record"); + return error; + } + } + + *((uint16_t *) bucket_id_p) = KAA_HTONS(first_bucket); + + size_t payload_size = tmp_writer.current - writer->current - KAA_EXTENSION_HEADER_SIZE; + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, + "Created log bucket: id '%u', log records count %u, payload size %zu", + first_bucket, records_count, payload_size); + + *((uint32_t *) extension_size_p) = KAA_HTONL(payload_size); + *((uint16_t *) records_count_p) = KAA_HTONS(records_count); + *writer = tmp_writer; + + error = remember_request(self, first_bucket, records_count); + if (error) { + KAA_LOG_ERROR(self->logger, error, "Failed to remember request time stamp"); + } + + // Incomplete bucket sent, so let's go and step to the next bucket + if (last_bucket(self, first_bucket)) { + form_new_bucket(self); + } + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_logging_handle_server_sync(kaa_log_collector_t *self, + kaa_platform_message_reader_t *reader, uint16_t extension_options, size_t extension_length) +{ + // Only used for logging + (void)extension_options; + (void)extension_length; + + // TODO(KAA-982): Use asserts + if (!self || !reader) { + return KAA_ERR_BADPARAM; + } + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received logging server sync: options 0, payload size %u", extension_length); + + uint32_t delivery_status_count; + kaa_error_t error_code = kaa_platform_message_read(reader, &delivery_status_count, sizeof(uint32_t)); + if (error_code) { + return error_code; + } + + delivery_status_count = KAA_NTOHL(delivery_status_count); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received %lu log delivery statuses", delivery_status_count); + + uint16_t bucket_id; + int8_t delivery_result; + int8_t delivery_error_code; + + for (uint32_t i = 0; i < delivery_status_count; ++i) { + error_code = kaa_platform_message_read(reader, &bucket_id, sizeof(uint16_t)); + if (error_code) { + return error_code; + } + + bucket_id = KAA_NTOHS(bucket_id); + + error_code = kaa_platform_message_read(reader, &delivery_result, sizeof(int8_t)); + if (error_code) { + return error_code; + } + + error_code = kaa_platform_message_read(reader, &delivery_error_code, sizeof(int8_t)); + if (error_code) { + return error_code; + } + + if (delivery_result == LOGGING_RESULT_SUCCESS) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Log bucket uploaded successfully, id '%u'", bucket_id); + } else { + KAA_LOG_ERROR(self->logger, KAA_ERR_WRITE_FAILED, + "Failed to upload log bucket, id '%u' (delivery error code '%u')", + bucket_id, delivery_error_code); + } + + size_t uploaded_count = remove_request(self, bucket_id); + + kaa_log_bucket_info_t log_bucket_info = { + .log_count = uploaded_count, + .bucket_id = bucket_id, + }; + + if (delivery_result == LOGGING_RESULT_SUCCESS) { + if (self->log_delivery_listeners.on_success) { + self->log_delivery_listeners.on_success(self->log_delivery_listeners.ctx, + &log_bucket_info); + } + ext_log_storage_remove_by_bucket_id(self->log_storage_context, bucket_id); + } else { + if (self->log_delivery_listeners.on_failed) { + self->log_delivery_listeners.on_failed(self->log_delivery_listeners.ctx, + &log_bucket_info); + } + ext_log_storage_unmark_by_bucket_id(self->log_storage_context, bucket_id); + ext_log_upload_strategy_on_failure(self->log_upload_strategy_context, + (logging_delivery_error_code_t)delivery_error_code); + } + } + + update_storage(self); + + return error_code; +} + + +void ext_log_upload_timeout(kaa_log_collector_t *self) +{ + if (!is_timeout(self) + || ext_log_upload_strategy_is_timeout_strategy(self->log_upload_strategy_context)) { + update_storage(self); + } else { + handle_timeout(self); + } +} diff --git a/client/client-multi/client-c/src/extensions/logging/kaa_logging.h b/client/client-multi/client-c/src/extensions/logging/kaa_logging.h new file mode 100644 index 0000000000..f9082e4ad3 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/logging/kaa_logging.h @@ -0,0 +1,131 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file kaa_logging.h + * @brief Kaa data logging subsystem API + * + * Supplies API for Kaa data collection / logging subsystem. + */ + +#ifndef KAA_LOGGING_H_ +#define KAA_LOGGING_H_ + + +#include "gen/kaa_logging_definitions.h" +#include "platform/ext_log_storage.h" +#include "platform/ext_log_upload_strategy.h" +#include "platform/ext_log_delivery_listener.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Private log collector structure. + */ +struct kaa_log_collector_t; +typedef struct kaa_log_collector_t kaa_log_collector_t; + +/** + * @brief Log record info. + * + * Each log is contained in the bucket. Bucket is used to agreggate + * multiple logs into one entity that will be atomically sent to the server. + * Bucket can either be entirely successfully sent or be entirely failed. + * Corresponding events are generated. User may subscribe to them. + * @sa kaa_log_event_fn + * @sa kaa_log_listeners_t + * @sa kaa_logging_set_listeners + */ +typedef struct { + uint32_t log_id; /**< Id of a log record processed by kaa_logging_add_record() */ + uint16_t bucket_id; /**< Id of a bucket where a log record contained */ +} kaa_log_record_info_t; + +/** Constraints applied to log buckets */ +typedef struct { + size_t max_bucket_size; /**< The maximum bucket size in bytes */ + size_t max_bucket_log_count; /**< The maximum log count within a single bucket */ +} kaa_log_bucket_constraints_t; + +/** + * @brief Initializes data collection module with the storage interface, upload strategy, and other settings. + * + * @param[in] self Pointer to a @link kaa_log_collector_t @endlink instance. + * @param[in] log_storage_context Log storage context. + * @param[in] log_upload_strategy_context Log upload strategy context. + * @param[in] bucket_sizes Bucket size constraints. + * + * @return Error code. + */ +kaa_error_t kaa_logging_init(kaa_log_collector_t *self, void *log_storage_context, void *log_upload_strategy_context, const kaa_log_bucket_constraints_t *bucket_sizes); + +/** + * @brief Sets custom strategy for given collector. + * + * If a strategy has been assigned to collector previously then it will be + * destroyed and new strategy will be assigned. + * + * @param[in] self Pointer to a @link kaa_log_collector_t @endlink instance. + * @param[in] log_upload_strategy_context Log storage context. + * + * @return Error code. + */ +kaa_error_t kaa_logging_set_strategy(kaa_log_collector_t *self, void *log_upload_strategy_context); + +/** + * @brief Sets custom storage for given collector. + * + * If a storage has been assigned to collector previously then it will be + * destroyed and new storage will be assigned. Be aware that all items from + * previous storage will be deleted. + * + * @param[in] self Pointer to a @link kaa_log_collector_t @endlink instance. + * @param[in] log_storage_context Log storage context. + * + * @return Error code. + */ +kaa_error_t kaa_logging_set_storage(kaa_log_collector_t *self, void *log_storage_context); + +/** + * @brief Serializes and adds a log record to the log storage. + * + * @param[in] self Pointer to a @link kaa_log_collector_t @endlink instance. + * @param[in] entry Pointer to log entry to be added to the storage. + * @param[out] log_info Pointer to log info. May be @c NULL. + * + * @return Error code. + */ +kaa_error_t kaa_logging_add_record(kaa_log_collector_t *self, kaa_user_log_record_t *entry, kaa_log_record_info_t *log_info); + +/** + * @brief Sets listeners of log events. + * + * @param[in] self Pointer to a @link kaa_log_collector_t @endlink instance. + * @param[in] listeners Pointer to listeners that will be used to handle + * various log delivery events. @sa KAA_LOG_EMPTY_LISTENERS + * can be used to unsubscribe from log events. + * @return Error code. + */ +kaa_error_t kaa_logging_set_listeners(kaa_log_collector_t *self, const kaa_log_delivery_listener_t *listeners); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* KAA_LOGGING_H_ */ diff --git a/client/client-multi/client-c/src/extensions/logging/kaa_logging_private.h b/client/client-multi/client-c/src/extensions/logging/kaa_logging_private.h new file mode 100644 index 0000000000..2fe7c53074 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/logging/kaa_logging_private.h @@ -0,0 +1,45 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef KAA_LOGGING_PRIVATE_H +#define KAA_LOGGING_PRIVATE_H + +#include +#include +#include +#include +#include +#include + +kaa_error_t kaa_log_collector_create(kaa_log_collector_t ** log_collector_p, kaa_status_t *status, + kaa_channel_manager_t *channel_manager, kaa_logger_t *logger); +void kaa_log_collector_destroy(kaa_log_collector_t *self); +kaa_error_t kaa_logging_need_logging_resync(kaa_log_collector_t *self, bool *result); + +kaa_error_t kaa_logging_request_get_size(kaa_log_collector_t *self, size_t *expected_size); +kaa_error_t kaa_logging_request_serialize(kaa_log_collector_t *self, + kaa_platform_message_writer_t *writer); +kaa_error_t kaa_logging_handle_server_sync(kaa_log_collector_t *self, kaa_platform_message_reader_t *reader, uint16_t extension_options, size_t extension_length); + +void ext_log_upload_timeout(kaa_log_collector_t *self); +void ext_log_upload_timeout(kaa_log_collector_t *self); +bool ext_log_upload_strategy_is_timeout_strategy(void *strategy); + +kaa_error_t ext_unlimited_log_storage_create(void **log_storage_context_p, kaa_logger_t *logger); +kaa_error_t ext_limited_log_storage_create(void **log_storage_context_p, kaa_logger_t *logger, + size_t storage_size, size_t percent_to_delete); +kaa_error_t ext_log_storage_destroy(void *context); + +#endif /* KAA_LOGGING_PRIVATE_H */ diff --git a/client/client-multi/client-c/src/extensions/logging/test/test_kaa_log.c b/client/client-multi/client-c/src/extensions/logging/test/test_kaa_log.c new file mode 100644 index 0000000000..dbecdfe630 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/logging/test/test_kaa_log.c @@ -0,0 +1,1648 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include "platform/ext_sha.h" + +#include "kaa_logging.h" +#include "kaa_logging_private.h" + +#include "kaa_test.h" + +#include "kaa_context.h" +#include "kaa_platform_protocol.h" +#include "kaa_channel_manager.h" +#include "kaa_platform_utils.h" +#include "kaa_status.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include "platform/sock.h" +#include "platform/ext_log_storage.h" +#include "platform/ext_log_upload_strategy.h" + +#include "kaa_private.h" + +static kaa_context_t kaa_context; +static kaa_logger_t *logger = NULL; +static kaa_status_t *status = NULL; +static kaa_channel_manager_t *channel_manager = NULL; + +static const kaa_extension_id OPERATIONS_SERVICES[] = { + KAA_EXTENSION_PROFILE, + KAA_EXTENSION_USER, + KAA_EXTENSION_CONFIGURATION, + KAA_EXTENSION_EVENT, + KAA_EXTENSION_LOGGING, + KAA_EXTENSION_NOTIFICATION +}; +static const int OPERATIONS_SERVICES_COUNT = sizeof(OPERATIONS_SERVICES) / sizeof(kaa_extension_id); + +#define TEST_LOG_BUFFER "log_record" +#define TEST_TIMEOUT 1 + + +typedef struct { + size_t timeout; + size_t max_parallel_uploads; + int on_timeout_count; + int on_failure_count; + ext_log_upload_decision_t decision; + kaa_error_t timeout_retval; // Return value when timeout event hits +} mock_strategy_context_t; + +typedef struct { + kaa_list_t *logs; + size_t record_count; + size_t total_size; + int on_remove_by_id_count; + int on_unmark_by_id_count; +} mock_storage_context_t; + +typedef struct { + size_t on_sync_count; +} mock_transport_channel_context_t; + + + +/* + * TRANSPORT_CHANNEL INTERFACE + */ +static kaa_error_t test_kaa_init_channel(void *channel_context, + kaa_transport_context_t *transport_context) +{ + (void)channel_context; + (void)transport_context; + return KAA_ERR_NONE; +} + +static kaa_error_t test_kaa_set_access_point(void *channel_context, + kaa_access_point_t *access_point) +{ + (void)channel_context; + (void)access_point; + return KAA_ERR_NONE; +} + +static kaa_error_t test_kaa_get_protocol_id(void *context, + kaa_transport_protocol_id_t *protocol_info) +{ + (void)context; + protocol_info->id = 1; + protocol_info->version = 1; + return KAA_ERR_NONE; +} + +static kaa_error_t test_kaa_get_supported_services(void *context, + const kaa_extension_id **supported_services, + size_t *service_count) +{ + (void)context; + *supported_services = OPERATIONS_SERVICES; + *service_count = OPERATIONS_SERVICES_COUNT; + return KAA_ERR_NONE; +} + +static kaa_error_t test_kaa_sync_handler(void *context, const kaa_extension_id services[], + size_t service_count) +{ + (void)services; + (void)service_count; + ((mock_transport_channel_context_t *)context)->on_sync_count++; + return KAA_ERR_NONE; +} + +static kaa_error_t test_kaa_channel_destroy(void *context) +{ + KAA_FREE(context); + return KAA_ERR_NONE; +} + +static void test_kaa_channel_create(kaa_transport_channel_interface_t *self) +{ + self->context = KAA_CALLOC(1, sizeof(mock_transport_channel_context_t)); + + self->get_protocol_id = test_kaa_get_protocol_id; + self->get_supported_services = test_kaa_get_supported_services; + self->destroy = test_kaa_channel_destroy; + self->sync_handler = test_kaa_sync_handler; + self->init = test_kaa_init_channel; + self->set_access_point = test_kaa_set_access_point; +} + + + +/* + * STRATEGY INTERFACE + */ +ext_log_upload_decision_t ext_log_upload_strategy_decide(void *context, + const void *log_storage_context) +{ + (void)log_storage_context; + return ((mock_strategy_context_t *)context)->decision; +} + +size_t ext_log_upload_strategy_get_timeout(void *context) +{ + return ((mock_strategy_context_t *)context)->timeout; +} + +kaa_error_t ext_log_upload_strategy_on_timeout(void *context) +{ + mock_strategy_context_t *mock_ctx = context; + mock_ctx->on_timeout_count++; + return mock_ctx->timeout_retval; +} + +kaa_error_t ext_log_upload_strategy_on_failure(void *context, + logging_delivery_error_code_t error_code) +{ + (void)error_code; + ((mock_strategy_context_t *)context)->on_failure_count++; + return KAA_ERR_NONE; +} + +size_t ext_log_upload_strategy_get_max_parallel_uploads(void *context) +{ + return ((mock_strategy_context_t *)context)->max_parallel_uploads; +} + +void ext_log_upload_strategy_destroy(void *context) +{ + (void)context; +} + +bool ext_log_upload_strategy_is_timeout_strategy(void *strategy) +{ + (void)strategy; + return false; +} + + +/* + * STORAGE INTERFACE + */ + +typedef struct { + bool processed; + kaa_log_record_t rec; +} test_log_record_t; + +static void destroy_log_record(void *record_p) +{ + if (record_p) { + test_log_record_t *test_rec = record_p; + KAA_FREE(test_rec->rec.data); + KAA_FREE(test_rec); + } +} + +static bool match_unprocessed(void *log_record_p, void *arg) +{ + (void) arg; + test_log_record_t *record = log_record_p; + return !record->processed; +} + +kaa_error_t ext_log_storage_allocate_log_record_buffer(void *context, kaa_log_record_t *record) +{ + (void)context; + record->data = KAA_MALLOC(record->size); + return KAA_ERR_NONE; +} + +kaa_error_t ext_log_storage_deallocate_log_record_buffer(void *context, kaa_log_record_t *record) +{ + (void)context; + KAA_FREE(record->data); + return KAA_ERR_NONE; +} + +kaa_error_t ext_log_storage_add_log_record(void *context, kaa_log_record_t *record) +{ + mock_storage_context_t *self = context; + + test_log_record_t *test_rec = KAA_MALLOC(sizeof(*test_rec)); + if (!test_rec) { + return KAA_ERR_NOMEM; + } + + test_rec->rec = *record; + test_rec->processed = false; + + kaa_list_push_back(self->logs, test_rec); + + self->record_count++; + self->total_size += test_rec->rec.size; + + return KAA_ERR_NONE; +} + +kaa_error_t ext_log_storage_write_next_record(void *context, char *buffer, size_t buffer_len, + uint16_t *bucket_id, size_t *record_len) +{ + mock_storage_context_t *self = context; + KAA_RETURN_IF_NIL2(self, self->logs, KAA_ERR_NOT_FOUND); + + kaa_list_node_t *node = kaa_list_find_next(kaa_list_begin(self->logs), + match_unprocessed, NULL); + + if (!node) { + return KAA_ERR_NOT_FOUND; + } + + test_log_record_t *record = kaa_list_get_data(node); + + if (buffer_len < record->rec.size) { + return KAA_ERR_INSUFFICIENT_BUFFER; + } + + *record_len = record->rec.size; + *bucket_id = record->rec.bucket_id; + memcpy(buffer, record->rec.data, *record_len); + record->processed = true; + return KAA_ERR_NONE; +} + +kaa_error_t ext_log_storage_remove_by_bucket_id(void *context, uint16_t bucket_id) +{ + (void)bucket_id; + mock_storage_context_t *self = context; + self->on_remove_by_id_count++; + + return KAA_ERR_NONE; +} + +kaa_error_t ext_log_storage_unmark_by_bucket_id(void *context, uint16_t bucket_id) +{ + (void)bucket_id; + ((mock_storage_context_t *)context)->on_unmark_by_id_count++; + return KAA_ERR_NONE; +} + +size_t ext_log_storage_get_total_size(const void *context) +{ + return ((mock_storage_context_t *)context)->total_size; +} + +size_t ext_log_storage_get_records_count(const void *context) +{ + return ((mock_storage_context_t *)context)->record_count; +} + +mock_storage_context_t *create_mock_storage(void) +{ + mock_storage_context_t *storage = KAA_CALLOC(1, sizeof(mock_storage_context_t)); + KAA_RETURN_IF_NIL(storage, NULL); + storage->logs = kaa_list_create(); + + return storage; +} + +kaa_error_t ext_log_storage_destroy(void *context) +{ + if (context) { + kaa_list_destroy(((mock_storage_context_t *)context)->logs, &destroy_log_record); + KAA_FREE(context); + } + return KAA_ERR_NONE; +} + + + +void test_create_request(void **state) +{ + (void)state; + + kaa_user_log_record_t *test_log_record = kaa_test_log_record_create(); + test_log_record->data = kaa_string_copy_create(TEST_LOG_BUFFER); + size_t test_log_record_size = test_log_record->get_size(test_log_record); + + kaa_log_collector_t *log_collector = NULL; + kaa_error_t error_code = kaa_log_collector_create(&log_collector, status, + channel_manager, logger); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + mock_strategy_context_t strategy; + memset(&strategy, 0, sizeof(mock_strategy_context_t)); + strategy.decision = NOOP; + strategy.max_parallel_uploads = UINT32_MAX; + + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = 2 * test_log_record_size, + .max_bucket_log_count = UINT32_MAX, + }; + + error_code = kaa_logging_init(log_collector, create_mock_storage(), &strategy, &constraints); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + size_t expected_size = 0; + error_code = kaa_logging_request_get_size(log_collector, &expected_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint8_t buffer[expected_size]; + kaa_platform_message_writer_t *writer = NULL; + error_code = kaa_platform_message_writer_create(&writer, buffer, expected_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_NOT_NULL(writer); + + error_code = kaa_logging_request_serialize(log_collector, writer); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + kaa_platform_message_writer_destroy(writer); + + uint8_t *buf_cursor = buffer; + ASSERT_EQUAL(KAA_EXTENSION_LOGGING, KAA_HTONS(*(uint16_t *)buf_cursor)); + buf_cursor += sizeof(uint16_t); + + uint8_t options[] = { 0x00, 0x01 }; + ASSERT_EQUAL(memcmp(buf_cursor, options, 2), 0); + buf_cursor += 2; + + ASSERT_EQUAL(*(uint32_t *) buf_cursor, KAA_HTONL(20)); + buf_cursor += sizeof(uint32_t); + + uint8_t request_id_records_count[] = { 0x00, 0x01, 0x00, 0x01 }; + ASSERT_EQUAL(memcmp(buf_cursor, request_id_records_count, 4), 0); + buf_cursor += 4; + + uint8_t record_buf[test_log_record_size]; + avro_writer_t avro_writer = avro_writer_memory((char *)record_buf, test_log_record_size); + test_log_record->serialize(avro_writer, test_log_record); + avro_writer_free(avro_writer); + + ASSERT_EQUAL(*(uint32_t *) buf_cursor, KAA_HTONL(test_log_record_size)); + buf_cursor += sizeof(uint32_t); + + ASSERT_EQUAL(memcmp(buf_cursor, record_buf, test_log_record_size), 0); + + kaa_log_collector_destroy(log_collector); + test_log_record->destroy(test_log_record); +} + + + +void test_response(void **state) +{ + (void)state; + + srand(time(NULL)); + + kaa_log_collector_t *log_collector = NULL; + kaa_error_t error_code = kaa_log_collector_create(&log_collector, status, channel_manager, logger); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + mock_strategy_context_t strategy; + memset(&strategy, 0, sizeof(mock_strategy_context_t)); + + mock_storage_context_t *storage = create_mock_storage(); + + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = 1024, + .max_bucket_log_count = UINT32_MAX, + }; + + error_code = kaa_logging_init(log_collector, storage, &strategy, &constraints); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint32_t response_count = 2; + size_t response_buffer_size = sizeof(uint32_t) + sizeof(uint32_t) * response_count; + uint8_t response_buffer[response_buffer_size]; + + uint8_t *response = response_buffer; + *((uint32_t *)response) = KAA_HTONL(response_count); + response += sizeof(uint32_t); + + /* First response */ + *((uint16_t *)response) = KAA_HTONS(rand()); + response += sizeof(uint16_t); + *((uint8_t *)response) = 0x0; // SUCCESS + response += sizeof(uint8_t); + *((uint8_t *)response) = 0; + response += sizeof(uint8_t); + + /* Second response */ + *((uint16_t *)response) = KAA_HTONS(rand()); + response += sizeof(uint16_t); + *((uint8_t *)response) = 0x1; // FAILURE + response += sizeof(uint8_t); + *((uint8_t *)response) = rand() % 4; + response += sizeof(uint8_t); + + kaa_platform_message_reader_t *reader = NULL; + error_code = kaa_platform_message_reader_create(&reader, response_buffer, response_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_NOT_NULL(reader); + + error_code = kaa_logging_handle_server_sync(log_collector, reader, 0, response_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + ASSERT_TRUE(strategy.on_failure_count); + ASSERT_TRUE(storage->on_remove_by_id_count); + ASSERT_TRUE(storage->on_unmark_by_id_count); + + kaa_platform_message_reader_destroy(reader); + kaa_log_collector_destroy(log_collector); +} + + + +void test_timeout(void **state) +{ + (void)state; + + kaa_log_collector_t *log_collector = NULL; + kaa_error_t error_code = kaa_log_collector_create(&log_collector, + status, channel_manager, logger); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_user_log_record_t *test_log_record = kaa_test_log_record_create(); + test_log_record->data = kaa_string_copy_create(TEST_LOG_BUFFER); + size_t test_log_record_size = test_log_record->get_size(test_log_record); + + mock_strategy_context_t strategy; + memset(&strategy, 0, sizeof(mock_strategy_context_t)); + strategy.timeout = TEST_TIMEOUT; + strategy.decision = NOOP; + strategy.max_parallel_uploads = UINT32_MAX; + + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = 2 * test_log_record_size, + .max_bucket_log_count = UINT32_MAX, + }; + + error_code = kaa_logging_init(log_collector, create_mock_storage(), &strategy, &constraints); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + size_t request_buffer_size = 256; + uint8_t request_buffer[request_buffer_size]; + kaa_platform_message_writer_t *writer = NULL; + error_code = kaa_platform_message_writer_create(&writer, request_buffer, request_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_logging_request_serialize(log_collector, writer); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + sleep(TEST_TIMEOUT + 1); + + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + ASSERT_TRUE(strategy.on_timeout_count); + + test_log_record->destroy(test_log_record); + kaa_platform_message_writer_destroy(writer); + kaa_log_collector_destroy(log_collector); +} + +void test_decline_timeout(void **state) +{ + (void)state; + + kaa_log_collector_t *log_collector = NULL; + kaa_error_t error_code = kaa_log_collector_create(&log_collector, status, channel_manager, logger); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_user_log_record_t *test_log_record = kaa_test_log_record_create(); + test_log_record->data = kaa_string_copy_create(TEST_LOG_BUFFER); + size_t test_log_record_size = test_log_record->get_size(test_log_record); + + mock_strategy_context_t strategy; + memset(&strategy, 0, sizeof(mock_strategy_context_t)); + strategy.timeout = TEST_TIMEOUT; + strategy.decision = NOOP; + strategy.max_parallel_uploads = UINT32_MAX; + + mock_storage_context_t *storage = create_mock_storage(); + ASSERT_NOT_NULL(storage); + + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = 2 * test_log_record_size, + .max_bucket_log_count = UINT32_MAX, + }; + + error_code = kaa_logging_init(log_collector, storage, &strategy, &constraints); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + size_t request_buffer_size = 256; + uint8_t request_buffer[request_buffer_size]; + kaa_platform_message_writer_t *writer = NULL; + error_code = kaa_platform_message_writer_create(&writer, request_buffer, request_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_logging_request_serialize(log_collector, writer); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + sleep(TEST_TIMEOUT + 1); + + uint16_t bucket_id = *((uint16_t *)(request_buffer + KAA_EXTENSION_HEADER_SIZE)); + bucket_id = KAA_NTOHS(bucket_id); + + uint32_t response_count = 1; + size_t response_buffer_size = sizeof(uint32_t) + sizeof(uint32_t) * response_count; + uint8_t response_buffer[response_buffer_size]; + + uint8_t *response = response_buffer; + *((uint32_t *)response) = KAA_HTONL(response_count); + response += sizeof(uint32_t); + + /* First response */ + *((uint16_t *)response) = KAA_HTONS(bucket_id); + response += sizeof(uint16_t); + *((uint8_t *)response) = 0x0; // SUCCESS + response += sizeof(uint8_t); + *((uint8_t *)response) = 0; + response += sizeof(uint8_t); + + kaa_platform_message_reader_t *reader = NULL; + error_code = kaa_platform_message_reader_create(&reader, response_buffer, response_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_NOT_NULL(reader); + + error_code = kaa_logging_handle_server_sync(log_collector, reader, 0, response_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_TRUE(storage->on_remove_by_id_count); + + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + ASSERT_FALSE(strategy.on_timeout_count); + + test_log_record->destroy(test_log_record); + kaa_platform_message_writer_destroy(writer); + kaa_platform_message_reader_destroy(reader); + kaa_log_collector_destroy(log_collector); +} + +void test_max_parallel_uploads_with_log_sync(void **state) +{ + (void)state; + + uint32_t channel_id = 0; + kaa_transport_channel_interface_t transport_context; + test_kaa_channel_create(&transport_context); + + kaa_channel_manager_add_transport_channel(channel_manager, &transport_context, &channel_id); + + kaa_log_collector_t *log_collector = NULL; + kaa_error_t error_code = kaa_log_collector_create(&log_collector, status, channel_manager, logger); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_user_log_record_t *test_log_record = kaa_test_log_record_create(); + test_log_record->data = kaa_string_copy_create(TEST_LOG_BUFFER); + size_t test_log_size = test_log_record->get_size(test_log_record); + + mock_strategy_context_t strategy; + memset(&strategy, 0, sizeof(mock_strategy_context_t)); + strategy.timeout = UINT32_MAX; + strategy.decision = UPLOAD; + + mock_storage_context_t *storage = create_mock_storage(); + ASSERT_NOT_NULL(storage); + + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = 2 * test_log_size, + .max_bucket_log_count = UINT32_MAX, + }; + + error_code = kaa_logging_init(log_collector, storage, &strategy, &constraints); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + /* + * Ensure the log delivery is forbidden at all. + */ + strategy.max_parallel_uploads = 0; + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + ASSERT_EQUAL(((mock_transport_channel_context_t *)transport_context.context)->on_sync_count, 0); + + /* + * Ensure the first request is allowed. + */ + strategy.max_parallel_uploads = 1; + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_EQUAL(((mock_transport_channel_context_t *)transport_context.context)->on_sync_count, 1); + + /* + * Do the first request to remember the delivery timeout of the log batch. + */ + size_t request_buffer_size = 256; + uint8_t request_buffer[request_buffer_size]; + kaa_platform_message_writer_t *writer = NULL; + error_code = kaa_platform_message_writer_create(&writer, request_buffer, request_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_logging_request_serialize(log_collector, writer); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + /* + * Ensure the second request is forbidden. + */ + ASSERT_EQUAL(((mock_transport_channel_context_t *)transport_context.context)->on_sync_count, 1); + + /* + * Clean up. + */ + error_code = kaa_channel_manager_remove_transport_channel(channel_manager, channel_id); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + test_log_record->destroy(test_log_record); + kaa_log_collector_destroy(log_collector); +} + +void test_max_parallel_uploads_with_sync_all(void **state) +{ + (void)state; + + uint32_t channel_id = 0; + kaa_transport_channel_interface_t transport_context; + test_kaa_channel_create(&transport_context); + + kaa_channel_manager_add_transport_channel(channel_manager, &transport_context, &channel_id); + + kaa_log_collector_t *log_collector = NULL; + kaa_error_t error_code = kaa_log_collector_create(&log_collector, + status, channel_manager, logger); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + kaa_user_log_record_t *test_log_record = kaa_test_log_record_create(); + test_log_record->data = kaa_string_copy_create(TEST_LOG_BUFFER); + size_t test_log_size = test_log_record->get_size(test_log_record); + + mock_strategy_context_t strategy; + memset(&strategy, 0, sizeof(mock_strategy_context_t)); + strategy.timeout = UINT32_MAX; + strategy.decision = UPLOAD; + + mock_storage_context_t *storage = create_mock_storage(); + ASSERT_NOT_NULL(storage); + + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = 2 * test_log_size, + .max_bucket_log_count = UINT32_MAX, + }; + + error_code = kaa_logging_init(log_collector, storage, &strategy, &constraints); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + /* + * Ensure the log delivery is forbidden at all. + */ + strategy.max_parallel_uploads = 0; + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + size_t expected_size = 0; + error_code = kaa_logging_request_get_size(log_collector, &expected_size); + assert_int_equal(KAA_ERR_NONE, error_code); + ASSERT_FALSE(expected_size); + + /* + * Ensure the first request is allowed. + */ + strategy.max_parallel_uploads = 1; + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + /* + * Do the first request to remember the delivery timeout of the log batch. + */ + error_code = kaa_logging_request_get_size(log_collector, &expected_size); + assert_int_equal(KAA_ERR_NONE, error_code); + ASSERT_TRUE(expected_size); + size_t request_buffer_size = 256; + uint8_t request_buffer[request_buffer_size]; + kaa_platform_message_writer_t *writer = NULL; + error_code = kaa_platform_message_writer_create(&writer, request_buffer, request_buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_logging_request_serialize(log_collector, writer); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + /* + * Ensure the second request is forbidden. + */ + error_code = kaa_logging_request_get_size(log_collector, &expected_size); + assert_int_equal(KAA_ERR_NONE, error_code); + ASSERT_FALSE(expected_size); + + /* + * Clean up. + */ + error_code = kaa_channel_manager_remove_transport_channel(channel_manager, channel_id); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + test_log_record->destroy(test_log_record); + kaa_log_collector_destroy(log_collector); +} + +/* ---------------------------------------------------------------------------*/ +/* Log delivery tests */ +/* ---------------------------------------------------------------------------*/ + +/* Server chunk, managed by a corresponding reader object. + * Perfectly packed. Packed attribute is intentionally avoided. */ +struct response_chunk { + uint8_t bucket_id[2]; /* 16 bits for bucket ID */ + uint8_t resp_code; /* 8 bits for response code. 0 == SUCCESS, 1 == FAILURE */ + // cppcheck-suppress unusedStructMember + uint8_t reserved; /* Should be 0 */ +}; + +struct response_packet { + uint8_t resp_cnt[4]; /* 32 bits for amount of responces in buffer */ + struct response_chunk resps[]; /* Responses itself */ +}; + +#define RESP_PACKETS 2 /* Amount of response packets */ +#define RESP_SUCCESS_IDX 0 /* Index of successfull response */ +#define RESP_FAILURE_IDX 1 /* Index of failed response */ +#define TEST_BUFFER_SIZE 1024 +#define TEST_EXT_OP 0 /* Simple stub */ + + +static mock_strategy_context_t test_strategy1; +static mock_strategy_context_t test_strategy2; +static mock_storage_context_t *test_storage1; +static mock_storage_context_t *test_storage2; +static kaa_log_collector_t *log_collector; +static size_t test_log_record_size = TEST_BUFFER_SIZE; +/* Will contain response_packet. Thus required to be aligned. */ +static uint32_t test_reader_buffer[TEST_BUFFER_SIZE / 4]; +static uint32_t test_writer_buffer[TEST_BUFFER_SIZE / 4]; +/* Portion of the test buffer filled with valid data */ +static size_t test_filled_size; +static kaa_platform_message_reader_t *test_reader; +static kaa_platform_message_writer_t *test_writer; +static kaa_user_log_record_t *test_log_record; + +/* Values to be checked inside mock event function */ +static void *expected_ctx; +static int check_bucket; +static uint16_t expected_bucked_id; + +/* Required to trace generic mock function calls */ +static int call_is_expected; +static int call_completed; + +/* Required to trace on fail mock function calls */ +static int failed_call_is_expected; +static int failed_call_completed; + +/* Required to trace on success mock function calls */ +static int success_call_is_expected; +static int success_call_completed; + +/* Required to trace on timeout mock function calls */ +static int timeout_call_is_expected; +static int timeout_call_completed; + +/* Mock event functions */ + +static void mock_log_event_generic_fn(void *ctx, const kaa_log_bucket_info_t *bucket) +{ + ASSERT_TRUE(call_is_expected); + ASSERT_NOT_NULL(bucket); /* Shouldn't be NULL no matter what */ + + if (check_bucket) { + ASSERT_EQUAL(expected_bucked_id, bucket->bucket_id); + } + + ASSERT_EQUAL(expected_ctx, ctx); + + call_completed++; +} + +static void mock_log_event_failed_fn(void *ctx, const kaa_log_bucket_info_t *bucket) +{ + ASSERT_TRUE(failed_call_is_expected); + /* Make sure that generic function will not fail */ + call_is_expected = 1; + mock_log_event_generic_fn(ctx, bucket); + + failed_call_completed++; +} + +static void mock_log_event_success_fn(void *ctx, const kaa_log_bucket_info_t *bucket) +{ + ASSERT_TRUE(success_call_is_expected); + /* Make sure that generic function will not fail */ + call_is_expected = 1; + mock_log_event_generic_fn(ctx, bucket); + + success_call_completed++; +} + +static void mock_log_event_timeout_fn(void *ctx, const kaa_log_bucket_info_t *bucket) +{ + ASSERT_TRUE(timeout_call_is_expected); + /* Make sure that generic function will not fail */ + call_is_expected = 1; + mock_log_event_generic_fn(ctx, bucket); + + timeout_call_completed++; +} + +/* ---------------------------------------------------------------------------*/ +/* Log setters test group */ +/* ---------------------------------------------------------------------------*/ + +KAA_GROUP_SETUP(log_setters) +{ + (void)state; + kaa_error_t rc; + + rc = kaa_log_collector_create(&log_collector, + status, + channel_manager, + logger); + + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + /* Test objects for strategy test */ + + memset(&test_strategy1, 0, sizeof(test_strategy1)); + memset(&test_strategy2, 0, sizeof(test_strategy2)); + + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = 2 * test_log_record_size, + .max_bucket_log_count = UINT32_MAX, + }; + + /* Test objects for storage tests */ + + test_storage1 = create_mock_storage(); + test_storage2 = create_mock_storage(); + + rc = kaa_logging_init(log_collector, test_storage1, + &test_strategy1, &constraints); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + expected_ctx = NULL; + expected_bucked_id = 0; + call_is_expected = 0; + + return 0; +} + +KAA_GROUP_TEARDOWN(log_setters) +{ + (void)state; + + /* If tests will pass, one of storages will be destroyed. + * Each test in this will decide which one should be deleted. */ + kaa_log_collector_destroy(log_collector); + log_collector = NULL; + + return 0; +} + + +KAA_TEST_CASE_EX(log_setters, set_strategy_invalid_parameters) +{ + (void)state; + kaa_error_t rc; + + rc = kaa_logging_set_strategy(log_collector, NULL); + ASSERT_EQUAL(KAA_ERR_BADPARAM, rc); + + rc = kaa_logging_set_strategy(NULL, &test_strategy2); + ASSERT_EQUAL(KAA_ERR_BADPARAM, rc); + + ext_log_storage_destroy(test_storage2); + ext_log_upload_strategy_destroy(&test_strategy2); +} + + +KAA_TEST_CASE_EX(log_setters, set_storage_invalid_parameters) +{ + (void)state; + kaa_error_t rc; + + rc = kaa_logging_set_storage(log_collector, NULL); + ASSERT_EQUAL(KAA_ERR_BADPARAM, rc); + + rc = kaa_logging_set_storage(NULL, test_storage2); + ASSERT_EQUAL(KAA_ERR_BADPARAM, rc); + + ext_log_storage_destroy(test_storage2); + ext_log_upload_strategy_destroy(&test_strategy2); +} + +KAA_TEST_CASE_EX(log_setters, set_strategy_valid_parameters) +{ + (void)state; + kaa_error_t rc; + + /* First strategy will be internally deleted */ + rc = kaa_logging_set_strategy(log_collector, &test_strategy2); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + ext_log_storage_destroy(test_storage2); + + /* Second strategy will be deleted on test teardown */ +} + + +KAA_TEST_CASE_EX(log_setters, set_storage_valid_parameters) +{ + (void)state; + kaa_error_t rc; + + /* First storage will be internally deleted */ + rc = kaa_logging_set_storage(log_collector, test_storage2); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + ext_log_upload_strategy_destroy(&test_strategy2); + + /* Second storage will be deleted on test teardown */ +} + + +/* ---------------------------------------------------------------------------*/ +/* Log delivery callback basic test group */ +/* ---------------------------------------------------------------------------*/ + + +KAA_GROUP_SETUP(log_callback_basic) +{ + (void)state; + kaa_error_t error_code; + + error_code = kaa_log_collector_create(&log_collector, + status, + channel_manager, + logger); + + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + memset(&test_strategy1, 0, sizeof(test_strategy1)); + + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = 2 * test_log_record_size, + .max_bucket_log_count = UINT32_MAX, + }; + + error_code = kaa_logging_init(log_collector, create_mock_storage(), &test_strategy1, &constraints); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + expected_ctx = NULL; + expected_bucked_id = 0; + call_is_expected = 0; + + return 0; +} + +KAA_GROUP_TEARDOWN(log_callback_basic) +{ + (void)state; + + kaa_log_collector_destroy(log_collector); + log_collector = NULL; + + return 0; +} + +KAA_TEST_CASE_EX(log_callback_basic, invalid_parameters) +{ + (void)state; + + kaa_log_delivery_listener_t listeners = { + .on_success = NULL, + .on_failed = NULL, + .on_timeout = NULL, + .ctx = NULL, + }; + + /* NULL parameters case */ + + kaa_error_t rc = kaa_logging_set_listeners(log_collector, NULL); + ASSERT_EQUAL(KAA_ERR_BADPARAM, rc); + rc = kaa_logging_set_listeners(NULL, &listeners); + ASSERT_EQUAL(KAA_ERR_BADPARAM, rc); + + return; +} + +/* This test is also testing the case when listeners isn't called + * if no logs added */ +KAA_TEST_CASE_EX(log_callback_basic, valid_parameters) +{ + (void)state; + + kaa_error_t rc; + + kaa_log_delivery_listener_t listeners = { + mock_log_event_generic_fn, + mock_log_event_generic_fn, + mock_log_event_generic_fn, + NULL, + }; + + /* Any of listeners can be NULL */ + + rc = kaa_logging_set_listeners(log_collector, &listeners); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + listeners.on_failed = NULL; + rc = kaa_logging_set_listeners(log_collector, &listeners); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + listeners.on_success = NULL; + rc = kaa_logging_set_listeners(log_collector, &listeners); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + listeners.on_timeout = NULL; + rc = kaa_logging_set_listeners(log_collector, &listeners); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + /* Special macro should work too */ + rc = kaa_logging_set_listeners(log_collector, &KAA_LOG_EMPTY_LISTENERS); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + /* Post conditions check */ + + /* No callbacks should be called */ + ASSERT_FALSE(call_completed); +} + +/* ---------------------------------------------------------------------------*/ +/* Log delivery callback extended test group */ +/* ---------------------------------------------------------------------------*/ + +KAA_GROUP_SETUP(log_callback_with_storage) +{ + (void)state; + kaa_error_t error_code; + + error_code = kaa_log_collector_create(&log_collector, + status, + channel_manager, + logger); + + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + memset(&test_strategy1, 0, sizeof(mock_strategy_context_t)); + memset(test_reader_buffer, 0, sizeof(test_reader_buffer)); + memset(test_writer_buffer, 0, sizeof(test_writer_buffer)); + + expected_ctx = NULL; + expected_bucked_id = 0; + call_is_expected = 0; + call_completed = 0; + failed_call_completed = 0; + failed_call_is_expected = 0; + success_call_completed = 0; + success_call_is_expected = 0; + check_bucket = 0; + + test_storage1 = create_mock_storage(); + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = 2 * test_log_record_size, + .max_bucket_log_count = UINT32_MAX, + }; + + error_code = kaa_logging_init(log_collector, test_storage1, + &test_strategy1, &constraints); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + + uint32_t response_count = 2; + struct response_packet *response = (struct response_packet *) test_reader_buffer; + *(uint32_t *) response->resp_cnt = KAA_HTONL(response_count); + + /* First response */ + + /* Later on we'll override that */ + *(uint16_t *) response->resps[RESP_SUCCESS_IDX].bucket_id = 0; + response->resps[RESP_SUCCESS_IDX].resp_code = 0; // SUCCESS + + /* Second response */ + + /* Later on we'll override that */ + *(uint16_t *) response->resps[RESP_FAILURE_IDX].bucket_id = 0; + response->resps[RESP_FAILURE_IDX].resp_code = 1; // FAILURE + + test_filled_size = sizeof(struct response_packet) + + sizeof(struct response_chunk) * 2; + + error_code = kaa_platform_message_reader_create( + &test_reader, + (const uint8_t *)test_reader_buffer, + test_filled_size); + + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_NOT_NULL(test_reader); + + return 0; +} + +KAA_GROUP_TEARDOWN(log_callback_with_storage) +{ + (void)state; + + kaa_platform_message_reader_destroy(test_reader); + test_reader = NULL; + /* Destroys mock storage as well */ + kaa_log_collector_destroy(log_collector); + log_collector = NULL; + + return 0; +} + +KAA_TEST_CASE_EX(log_callback_with_storage, on_fail_called) +{ + (void)state; + + kaa_error_t rc; + int dummy_ctx; + + kaa_log_delivery_listener_t listeners = { + NULL, + mock_log_event_failed_fn, + NULL, + &dummy_ctx, + }; + + /* Notify mocks about test intentions */ + failed_call_is_expected = 1; + expected_ctx = &dummy_ctx; + check_bucket = 1; + expected_bucked_id = 42; + + rc = kaa_logging_set_listeners(log_collector, &listeners); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + /* Response packet is passed internally via test reader */ + struct response_packet *response = (struct response_packet *) test_reader_buffer; + *(uint16_t *) response->resps[RESP_FAILURE_IDX].bucket_id = KAA_HTONS(expected_bucked_id); + + /* Test itself */ + rc = kaa_logging_handle_server_sync(log_collector, test_reader, TEST_EXT_OP, test_filled_size); + + ASSERT_EQUAL(rc, KAA_ERR_NONE); + + /* Post-conditions check */ + + ASSERT_FALSE(success_call_completed); + ASSERT_TRUE(failed_call_completed); + ASSERT_FALSE(timeout_call_completed); +} + +KAA_TEST_CASE_EX(log_callback_with_storage, on_success_called) +{ + (void)state; + + kaa_error_t rc; + int dummy_ctx; + + kaa_log_delivery_listener_t listeners = { + mock_log_event_success_fn, + NULL, + NULL, + &dummy_ctx, + }; + + /* Notify mocks about test intentions */ + check_bucket = 1; + expected_bucked_id = 42; + success_call_is_expected = 1; + expected_ctx = &dummy_ctx; + + rc = kaa_logging_set_listeners(log_collector, &listeners); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + /* Response packet is passed internally via test reader */ + struct response_packet *response = (struct response_packet *) test_reader_buffer; + *(uint16_t *) response->resps[RESP_SUCCESS_IDX].bucket_id = KAA_HTONS(expected_bucked_id); + + /* Test itself */ + rc = kaa_logging_handle_server_sync(log_collector, test_reader, TEST_EXT_OP, test_filled_size); + + ASSERT_EQUAL(rc, KAA_ERR_NONE); + + /* Post-conditions check */ + + ASSERT_TRUE(success_call_completed); + ASSERT_FALSE(failed_call_completed); + ASSERT_FALSE(timeout_call_completed); +} + +KAA_TEST_CASE_EX(log_callback_with_storage, on_fail_and_success_called) +{ + (void)state; + + kaa_error_t rc; + int dummy_ctx; + + kaa_log_delivery_listener_t listeners = { + mock_log_event_success_fn, + mock_log_event_failed_fn, + NULL, + &dummy_ctx, + }; + + /* Notify mocks about test intentions */ + check_bucket = 1; + expected_bucked_id = 42; + failed_call_is_expected = 1; + success_call_is_expected = 1; + expected_ctx = &dummy_ctx; + + rc = kaa_logging_set_listeners(log_collector, &listeners); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + /* Response packet is passed internally via test reader */ + struct response_packet *response = (struct response_packet *) test_reader_buffer; + *(uint16_t *) response->resps[RESP_SUCCESS_IDX].bucket_id + = KAA_HTONS(expected_bucked_id); + *(uint16_t *) response->resps[RESP_FAILURE_IDX].bucket_id + = KAA_HTONS(expected_bucked_id); + + /* Test itself */ + rc = kaa_logging_handle_server_sync(log_collector, test_reader, TEST_EXT_OP, test_filled_size); + + ASSERT_EQUAL(rc, KAA_ERR_NONE); + + /* Post-conditions check */ + + ASSERT_TRUE(success_call_completed); + ASSERT_TRUE(failed_call_completed); + ASSERT_FALSE(timeout_call_completed); +} + +/* ---------------------------------------------------------------------------*/ +/* Log delivery callback group with valid mock strategy. */ +/* The initialized mock strategy is essential for timeouts */ +/* ---------------------------------------------------------------------------*/ + +KAA_GROUP_SETUP(log_callback_with_storage_and_strategy) +{ + (void)state; + kaa_error_t error_code; + size_t test_log_record_size; + + error_code = kaa_log_collector_create(&log_collector, + status, + channel_manager, + logger); + + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + memset(&test_strategy1, 0, sizeof(mock_strategy_context_t)); + test_strategy1.timeout = TEST_TIMEOUT; + test_strategy1.decision = NOOP; + test_strategy1.max_parallel_uploads = UINT32_MAX; + + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = TEST_BUFFER_SIZE, + .max_bucket_log_count = UINT32_MAX, + }; + + expected_ctx = NULL; + expected_bucked_id = 0; + call_is_expected = 0; + call_completed = 0; + failed_call_completed = 0; + failed_call_is_expected = 0; + success_call_completed = 0; + success_call_is_expected = 0; + timeout_call_completed = 0; + check_bucket = 0; + + error_code = kaa_logging_init(log_collector, create_mock_storage(), + &test_strategy1, &constraints); + + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint32_t response_count = 2; + + struct response_packet *response = (struct response_packet *) test_reader_buffer; + *(uint32_t *) response->resp_cnt = KAA_HTONL(response_count); + + /* First response */ + + /* Later on we'll override that */ + *(uint16_t *) response->resps[RESP_SUCCESS_IDX].bucket_id = 0; + response->resps[RESP_SUCCESS_IDX].resp_code = 0; // SUCCESS + + /* Second response */ + + /* Later on we'll override that */ + *(uint16_t *) response->resps[RESP_FAILURE_IDX].bucket_id = 0; + response->resps[RESP_FAILURE_IDX].resp_code = 1; // FAILURE + + test_filled_size = sizeof(struct response_packet) + + sizeof(struct response_chunk) * 2; + + test_log_record = kaa_test_log_record_create(); + test_log_record->data = kaa_string_copy_create(TEST_LOG_BUFFER); + test_log_record_size = test_log_record->get_size(test_log_record); + + error_code = kaa_platform_message_reader_create( + &test_reader, + (const uint8_t *)test_reader_buffer, + test_filled_size); + + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_NOT_NULL(test_reader); + + error_code = kaa_platform_message_writer_create( + &test_writer, + (uint8_t *)test_writer_buffer, + test_log_record_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + return 0; +} + +KAA_GROUP_TEARDOWN(log_callback_with_storage_and_strategy) +{ + (void)state; + + test_log_record->destroy(test_log_record); + + kaa_platform_message_writer_destroy(test_writer); + test_writer = NULL; + kaa_platform_message_reader_destroy(test_reader); + test_reader = NULL; + + /* Destroys mock storage as well */ + kaa_log_collector_destroy(log_collector); + log_collector = NULL; + + return 0; +} + +KAA_TEST_CASE_EX(log_callback_with_storage_and_strategy, on_timeout_called) +{ + (void)state; + + kaa_error_t rc; + int dummy_ctx; + + kaa_log_delivery_listener_t listeners = { + NULL, + NULL, + mock_log_event_timeout_fn, + &dummy_ctx, + }; + + kaa_log_record_info_t bucket; + + timeout_call_is_expected = 1; + expected_ctx = &dummy_ctx; + + rc = kaa_logging_set_listeners(log_collector, &listeners); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + rc = kaa_logging_add_record(log_collector, test_log_record, &bucket); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + /* Notify mocks about bucket */ + check_bucket = 1; + expected_bucked_id = bucket.bucket_id; + + /* Test itself */ + + rc = kaa_logging_request_serialize(log_collector, test_writer); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + sleep(TEST_TIMEOUT + 1); + + rc = kaa_logging_add_record(log_collector, test_log_record, NULL); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + /* Post-conditions check */ + + /* Timeout callback should be called once */ + ASSERT_EQUAL(1, timeout_call_completed); +} + +/* ---------------------------------------------------------------------------*/ +/* Log delivery callback delivery timeout advanced group. */ +/* Special group is dedicated to cases where timeouts occur within different */ +/* buckets. */ +/* This group will require only writer instance to serialize log data. */ +/* ---------------------------------------------------------------------------*/ + +KAA_GROUP_SETUP(log_advanced_timeouts) +{ + (void)state; + kaa_error_t error_code; + size_t test_log_record_size; + + error_code = kaa_log_collector_create(&log_collector, status, channel_manager, logger); + + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + memset(&test_strategy1, 0, sizeof(test_strategy1)); + test_strategy1.timeout = TEST_TIMEOUT; + test_strategy1.decision = NOOP; + test_strategy1.max_parallel_uploads = UINT32_MAX; + + kaa_log_bucket_constraints_t constraints = { + .max_bucket_size = TEST_BUFFER_SIZE, + .max_bucket_log_count = 1, + }; + + expected_ctx = NULL; + expected_bucked_id = 0; + call_is_expected = 0; + call_completed = 0; + failed_call_completed = 0; + failed_call_is_expected = 0; + success_call_completed = 0; + success_call_is_expected = 0; + timeout_call_completed = 0; + check_bucket = 0; + + error_code = kaa_logging_init(log_collector, create_mock_storage(), &test_strategy1, &constraints); + + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + test_log_record = kaa_test_log_record_create(); + test_log_record->data = kaa_string_copy_create(TEST_LOG_BUFFER); + test_log_record_size = test_log_record->get_size(test_log_record); + + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_platform_message_writer_create(&test_writer, + (uint8_t *)test_writer_buffer, + test_log_record_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + return 0; +} + +KAA_GROUP_TEARDOWN(log_advanced_timeouts) +{ + (void)state; + + test_log_record->destroy(test_log_record); + + kaa_platform_message_writer_destroy(test_writer); + test_writer = NULL; + + /* Destroys mock storage as well */ + kaa_log_collector_destroy(log_collector); + log_collector = NULL; + + return 0; +} + +KAA_TEST_CASE_EX(log_advanced_timeouts, few_buckets) +{ + (void)state; + + kaa_error_t rc; + int dummy_ctx; + + kaa_log_delivery_listener_t listeners = { + NULL, + NULL, + mock_log_event_timeout_fn, + &dummy_ctx, + }; + + kaa_log_record_info_t bucket; + + timeout_call_is_expected = 1; + expected_ctx = &dummy_ctx; + + test_strategy1.timeout = 1; + + /* Means that AP will be switched as a result of timeout */ + test_strategy1.timeout_retval = KAA_ERR_EVENT_NOT_ATTACHED; + + /* Test itself */ + + rc = kaa_logging_set_listeners(log_collector, &listeners); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + rc = kaa_logging_add_record(log_collector, test_log_record, &bucket); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + rc = kaa_logging_add_record(log_collector, test_log_record, &bucket); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + rc = kaa_logging_add_record(log_collector, test_log_record, &bucket); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + /* One bucket per serialize request. */ + + rc = kaa_logging_request_serialize(log_collector, test_writer); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + /* Don't care about data written, rewinding buffer. */ + test_writer->current = test_writer->begin; + + /* Following two buckets must expire due to AP change. Large timeout will guarantee that. */ + test_strategy1.timeout = 100; + + rc = kaa_logging_request_serialize(log_collector, test_writer); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + /* Don't care about data written, rewinding buffer. */ + test_writer->current = test_writer->begin; + + rc = kaa_logging_request_serialize(log_collector, test_writer); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + sleep(TEST_TIMEOUT + 2); + + /* Check for timeout */ + ext_log_upload_timeout(log_collector); + + /* Post-conditions check */ + + /* Timeout callback should be called once */ + ASSERT_EQUAL(3, timeout_call_completed); +} + +/* ---------------------------------------------------------------------------*/ +/* End of log delivery test groups */ +/* ---------------------------------------------------------------------------*/ + +int test_init(void) +{ + kaa_error_t error = kaa_log_create(&logger, KAA_MAX_LOG_MESSAGE_LENGTH, KAA_MAX_LOG_LEVEL, NULL); + if (error || !logger) { + return error; + } + + kaa_context.logger = logger; + + error = kaa_status_create(&status); + if (error || !status) { + return error; + } + + error = kaa_channel_manager_create(&channel_manager, &kaa_context); + if (error || !channel_manager) { + return error; + } + return 0; +} + + + +int test_deinit(void) +{ + kaa_channel_manager_destroy(channel_manager); + kaa_status_destroy(status); + kaa_log_destroy(logger); + + return 0; +} + +KAA_SUITE_MAIN(Log, test_init, test_deinit, + KAA_TEST_CASE(create_request, test_create_request) + KAA_TEST_CASE(process_response, test_response) + KAA_TEST_CASE(process_timeout, test_timeout) + KAA_TEST_CASE(decline_timeout, test_decline_timeout) + KAA_TEST_CASE(max_parallel_uploads_with_log_sync, test_max_parallel_uploads_with_log_sync) + KAA_TEST_CASE(max_parallel_uploads_with_sync_all, test_max_parallel_uploads_with_sync_all) + KAA_RUN_TEST(log_setters, set_strategy_invalid_parameters) + KAA_RUN_TEST(log_setters, set_strategy_valid_parameters) + KAA_RUN_TEST(log_setters, set_storage_invalid_parameters) + KAA_RUN_TEST(log_setters, set_storage_valid_parameters) + KAA_RUN_TEST(log_callback_basic, valid_parameters) + KAA_RUN_TEST(log_callback_basic, invalid_parameters) + KAA_RUN_TEST(log_callback_basic, valid_parameters) + KAA_RUN_TEST(log_callback_with_storage, on_success_called) + KAA_RUN_TEST(log_callback_with_storage, on_fail_called) + KAA_RUN_TEST(log_callback_with_storage, on_fail_and_success_called) + KAA_RUN_TEST(log_callback_with_storage_and_strategy, on_timeout_called) + KAA_RUN_TEST(log_advanced_timeouts, few_buckets)) diff --git a/client/client-multi/client-c/src/extensions/notification/CMakeLists.txt b/client/client-multi/client-c/src/extensions/notification/CMakeLists.txt new file mode 100644 index 0000000000..fa7c2f76cb --- /dev/null +++ b/client/client-multi/client-c/src/extensions/notification/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set(EXTENSION_NOTIFICATION_SOURCE_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_notification_manager.c) + +set(EXTENSION_NOTIFICATION_HEADER_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_notification_manager.h) + +add_library(extension_notification ${EXTENSION_NOTIFICATION_SOURCE_FILES}) +target_include_directories(extension_notification PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(extension_notification PUBLIC kaac) + +kaa_add_unit_test(NAME test_kaa_notification_manager + SOURCES + ${CMAKE_CURRENT_LIST_DIR}/test/test_kaa_notification.c + DEPENDS + kaac ${OPENSSL_LIBRARIES}) diff --git a/client/client-multi/client-c/src/extensions/notification/kaa_notification_manager.c b/client/client-multi/client-c/src/extensions/notification/kaa_notification_manager.c new file mode 100644 index 0000000000..038f44e1ac --- /dev/null +++ b/client/client-multi/client-c/src/extensions/notification/kaa_notification_manager.c @@ -0,0 +1,1368 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaa_private.h" + +#include "kaa_notification_manager.h" + +#include +#include + +#include "kaa_status.h" +#include "kaa_platform_common.h" +#include "utilities/kaa_mem.h" +#include "kaa_common.h" +#include "utilities/kaa_log.h" +#include "kaa_platform_utils.h" +#include "kaa_channel_manager.h" +#include "platform-impl/common/kaa_htonll.h" + +#include "platform/sock.h" + +struct kaa_notification_manager_t { + kaa_list_t *mandatory_listeners; + kaa_list_t *topics_listeners; + kaa_list_t *optional_listeners; + kaa_list_t *subscriptions; + kaa_list_t *unsubscriptions; + kaa_list_t *uids; + kaa_list_t *notifications; + size_t extension_payload_size; + + kaa_platform_message_writer_t *writer; + + kaa_status_t *status; + kaa_channel_manager_t *channel_manager; + kaa_logger_t *logger; +}; + +typedef enum { + KAA_CLIENT_WANTS_TO_RECEIVE_NOTIFICATIONS = 0x01, + KAA_SUBSCRIBED_TOPIC_HASH_IS_PRESENT = 0x02, +} kaa_notification_extension_flags_t; + +typedef enum { + KAA_NO_DELTA, + KAA_DELTA, + KAA_RESYNC, +} kaa_sync_response_status; + +typedef enum { + SYSTEM = 0x0, + CUSTOM = 0x1, +} kaa_notification_type; + +typedef struct { + uint16_t length; + void *data; +} kaa_notifications_uid_t; + +#define TOPICS 0x0 +#define NOTIFICATIONS 0x1 +#define TOPICS_STATE_ID 0x0 +#define UID_ID 0x1 +#define SUBSCRIPTION_ID 0x2 +#define UNSUBSCRIPTION_ID 0x3 + +typedef struct { + uint64_t topic_id; + kaa_list_t *listeners; +} kaa_optional_notification_listeners_wrapper_t; + +typedef struct { + uint32_t id; + kaa_notification_listener_t listener; +} kaa_notification_listener_wrapper_t; + +typedef struct { + uint32_t id; + kaa_topic_listener_t listener; +} kaa_topic_listener_wrapper_t; + +static kaa_error_t kaa_notification_manager_create(kaa_notification_manager_t **self, kaa_status_t *status, kaa_channel_manager_t *channel_manager, kaa_logger_t *logger); +void kaa_notification_manager_destroy(kaa_notification_manager_t *self); + +static kaa_error_t kaa_notification_manager_get_size(kaa_notification_manager_t *self, size_t *expected_size); +static kaa_error_t kaa_notification_manager_request_serialize(kaa_notification_manager_t *self, + kaa_platform_message_writer_t *writer); +static kaa_error_t kaa_notification_manager_handle_server_sync(kaa_notification_manager_t *self, kaa_platform_message_reader_t *reader, uint32_t extension_length); + +kaa_error_t kaa_extension_notification_init(kaa_context_t *kaa_context, void **context) +{ + kaa_error_t error = kaa_notification_manager_create(&kaa_context->notification_manager, + kaa_context->status->status_instance, + kaa_context->channel_manager, + kaa_context->logger); + *context = kaa_context->notification_manager; + return error; +} + +kaa_error_t kaa_extension_notification_deinit(void *context) +{ + kaa_notification_manager_destroy(context); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_notification_request_get_size(void *context, size_t *expected_size) +{ + return kaa_notification_manager_get_size(context, expected_size); +} + +kaa_error_t kaa_extension_notification_request_serialize(void *context, uint32_t request_id, + uint8_t *buffer, size_t *size, bool *need_resync) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !size || !need_resync) { + return KAA_ERR_BADPARAM; + } + + *need_resync = true; + + size_t size_needed; + kaa_error_t error = kaa_notification_manager_get_size(context, &size_needed); + if (error) { + return error; + } + + if (!buffer || *size < size_needed) { + *size = size_needed; + return KAA_ERR_BUFFER_IS_NOT_ENOUGH; + } + + *size = size_needed; + + kaa_platform_message_writer_t writer = KAA_MESSAGE_WRITER(buffer, *size); + error = kaa_notification_manager_request_serialize(context, &writer); + if (error) { + return error; + } + + *size = writer.current - buffer; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_notification_server_sync(void *context, uint32_t request_id, + uint16_t extension_options, const uint8_t *buffer, size_t size) +{ + (void)request_id; + (void)extension_options; + + // TODO(KAA-982): Use asserts + if (!context || !buffer) { + return KAA_ERR_BADPARAM; + } + + kaa_platform_message_reader_t reader = KAA_MESSAGE_READER(buffer, size); + return kaa_notification_manager_handle_server_sync(context, &reader, size); +} + +static void shift_and_sub_extension(kaa_platform_message_reader_t *reader, uint32_t *extension_length, size_t size) +{ + KAA_RETURN_IF_NIL2(reader, extension_length,); + reader->current += size; + *extension_length -= size; +} + +typedef struct { + kaa_notification_t *notification; + uint32_t sqn; +} kaa_notification_wrapper_t; + +typedef struct { + uint64_t topic_id; + kaa_list_t *notifications; +} kaa_topic_notifications_node_t; + +static bool find_notifications_by_topic(void *data, void *context) +{ + kaa_topic_notifications_node_t *node = data; + return node->topic_id == *((uint64_t *) context); +} + +static bool sort_topic_by_id(void *node_1, void *node_2) +{ + KAA_RETURN_IF_NIL2(node_1, node_2, false); + kaa_topic_t *wrapper_1 = node_1; + kaa_topic_t *wrapper_2 = node_2; + return wrapper_1->id < wrapper_2->id; +} + +static uint64_t get_topic_id(void *node) +{ + kaa_topic_t *wrapper = node; + return wrapper->id; +} + +static void kaa_destroy_notification_wrapper(void *data) +{ + KAA_RETURN_IF_NIL(data, ); + kaa_notification_wrapper_t *wrapper = data; + if (wrapper->notification) { + wrapper->notification->destroy(wrapper->notification); + } + KAA_FREE(wrapper); +} + +static void kaa_destroy_notification_node(void *data) +{ + KAA_RETURN_IF_NIL(data, ); + kaa_topic_notifications_node_t *node = data; + if (node->notifications) { + kaa_list_destroy(node->notifications, kaa_destroy_notification_wrapper); + } + KAA_FREE(node); +} + +static kaa_error_t kaa_create_topic_notification_node(kaa_topic_notifications_node_t **node, kaa_notification_t *item, uint32_t *sqn, uint64_t topic_id) +{ + KAA_RETURN_IF_NIL(node, KAA_ERR_BADPARAM); + + kaa_topic_notifications_node_t *new_node = KAA_MALLOC(sizeof(*new_node)); + KAA_RETURN_IF_NIL(new_node, KAA_ERR_NOMEM); + + new_node->notifications = kaa_list_create(); + if (!new_node->notifications) { + kaa_destroy_notification_node(new_node); + return KAA_ERR_NOMEM; + } + + kaa_notification_wrapper_t *wrapper = KAA_MALLOC(sizeof(*wrapper)); + if (!wrapper) { + kaa_destroy_notification_node(new_node); + return KAA_ERR_NOMEM; + } + + if (!kaa_list_push_back(new_node->notifications, wrapper)) { + kaa_destroy_notification_node(new_node); + KAA_FREE(wrapper); + return KAA_ERR_NOMEM; + } + + new_node->topic_id = topic_id; + wrapper->notification = item; + wrapper->sqn = *sqn; + *node = new_node; + return KAA_ERR_NONE; +} + +static kaa_error_t kaa_add_notification_to_map(kaa_list_t *notifications, kaa_notification_t *item, uint64_t topic_id, uint32_t *sqn) +{ + KAA_RETURN_IF_NIL3(notifications, item, sqn, KAA_ERR_BADPARAM); + kaa_list_node_t *notification_list_node = kaa_list_find_next(kaa_list_begin(notifications), find_notifications_by_topic, &topic_id); + kaa_topic_notifications_node_t *notification_node = NULL; + if (!notification_list_node) { + kaa_error_t err = kaa_create_topic_notification_node(¬ification_node, item, sqn, topic_id); + if (err) { + kaa_list_destroy(notifications, &kaa_destroy_notification_node); + item->destroy(item); + return err; + } + if (!kaa_list_push_back(notifications, notification_node)) { + kaa_list_destroy(notifications, &kaa_destroy_notification_node); + kaa_destroy_notification_node(notification_node); + item->destroy(item); + return KAA_ERR_NOMEM; + } + } else { + notification_node = kaa_list_get_data(notification_list_node); + kaa_notification_wrapper_t *new_wrapper = KAA_MALLOC(sizeof(*new_wrapper)); + if (!new_wrapper) { + kaa_list_destroy(notifications, &kaa_destroy_notification_node); + item->destroy(item); + return KAA_ERR_NOMEM; + } + if (!kaa_list_push_back(notification_node->notifications, new_wrapper)) { + kaa_list_destroy(notifications, &kaa_destroy_notification_node); + item->destroy(item); + KAA_FREE(new_wrapper); + return KAA_ERR_NOMEM; + } + + new_wrapper->notification = item; + new_wrapper->sqn = *sqn; + } + + return KAA_ERR_NONE; +} + +static bool kaa_predicate_for_notifications(void *notif_1, void *notif_2) +{ + KAA_RETURN_IF_NIL2(notif_1, notif_2, false); + kaa_notification_wrapper_t *wrapper_1 = notif_1; + kaa_notification_wrapper_t *wrapper_2 = notif_2; + return wrapper_1->sqn < wrapper_2->sqn; +} +static void kaa_sort_notifications_by_sqn(void *data, void *context) +{ + (void)context; + kaa_topic_notifications_node_t *node = data; + kaa_list_sort(node->notifications, &kaa_predicate_for_notifications); +} + +static void kaa_sort_notifications(kaa_list_t *notifications) +{ + kaa_list_for_each(kaa_list_begin(notifications), kaa_list_back(notifications), &kaa_sort_notifications_by_sqn, NULL); +} + +static kaa_extension_id notification_sync_services[] = { KAA_EXTENSION_NOTIFICATION }; + +static bool kaa_find_notification_listener_by_id(void *listener, void *context) +{ + KAA_RETURN_IF_NIL2(listener, context, KAA_ERR_NONE); + return ((kaa_notification_listener_wrapper_t *) listener)->id == *(uint32_t *)context; +} +static bool kaa_find_topic_listener_by_id(void *listener, void *context) +{ + KAA_RETURN_IF_NIL2(listener, context, KAA_ERR_NONE); + return ((kaa_topic_listener_wrapper_t *) listener)->id == *(uint32_t *)context; +} + +static bool kaa_find_optional_notification_listener_by_id (void *optional_listener_list, void *topic_id) +{ + KAA_RETURN_IF_NIL2(optional_listener_list, topic_id, KAA_ERR_NONE); + kaa_optional_notification_listeners_wrapper_t *optional_listener_node = optional_listener_list; + return optional_listener_node->topic_id == *(uint64_t *)topic_id; +} + +static bool kaa_find_topic_by_id(void *topic, void *id) +{ + KAA_RETURN_IF_NIL2(topic, id, KAA_ERR_NONE); + kaa_topic_t* notification_topic = topic; + return notification_topic->id == *(uint64_t *)id; +} + +static kaa_error_t kaa_find_topic(kaa_notification_manager_t *self, kaa_topic_t **topic, uint64_t *topic_id) +{ + KAA_RETURN_IF_NIL2(topic_id, topic, KAA_ERR_BADPARAM); + kaa_list_node_t *topic_node = kaa_list_find_next(kaa_list_begin(self->status->topics), &kaa_find_topic_by_id, topic_id); + if (!topic_node) { + return KAA_ERR_NOT_FOUND; + } + + *topic = kaa_list_get_data(topic_node); + return KAA_ERR_NONE; +} + +static bool kaa_find_uid(void *data, void *context) +{ + KAA_RETURN_IF_NIL2(data, context, KAA_ERR_NONE); + kaa_notifications_uid_t *uid = data; + kaa_notifications_uid_t *search_context = context; + if (uid->length != search_context->length) { + return false; + } + + if (!memcmp(uid->data, search_context->data, uid->length)) { + return true; + } + return false; +} + +static bool kaa_find_topic_state_by_id(void *topic_state, void *id) +{ + KAA_RETURN_IF_NIL2(topic_state, id, KAA_ERR_BADPARAM); + kaa_topic_state_t *state = topic_state; + return state->topic_id == *(uint64_t *)id; +} + +kaa_error_t kaa_calculate_topic_listener_id(const kaa_topic_listener_t *listener, uint32_t *listener_id) +{ + KAA_RETURN_IF_NIL2(listener, listener_id, KAA_ERR_BADPARAM); + + const uint32_t prime = 31; + + *listener_id = 1; + *listener_id = prime * (*listener_id) + (ptrdiff_t)listener->context; + *listener_id = prime * (*listener_id) + (ptrdiff_t)listener->callback; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_calculate_notification_listener_id(const kaa_notification_listener_t *listener, uint32_t *listener_id) +{ + KAA_RETURN_IF_NIL2(listener, listener_id, KAA_ERR_BADPARAM); + + const uint32_t prime = 31; + + *listener_id = 1; + *listener_id = prime * (*listener_id) + (ptrdiff_t)listener->context; + *listener_id = prime * (*listener_id) + (ptrdiff_t)listener->callback; + return KAA_ERR_NONE; +} + +static size_t get_subscriptions_size(kaa_list_t *subscriptions) +{ + if (kaa_list_get_size(subscriptions) > 0) { + size_t expected_size = sizeof(uint32_t); //meta data for subscribe commands + expected_size += kaa_list_get_size(subscriptions) * sizeof(uint64_t); + return expected_size; + } + + return 0; +} + +kaa_error_t kaa_notification_manager_get_size(kaa_notification_manager_t *self, size_t *expected_size) +{ + KAA_RETURN_IF_NIL2(self, expected_size, KAA_ERR_BADPARAM); + *expected_size = 0; + *expected_size += sizeof(uint32_t); //state sqn size + + if (kaa_list_get_size(self->status->topic_states) > 0) { + *expected_size += sizeof(uint32_t); //field id + reserved + count + *expected_size += (sizeof(uint64_t) + sizeof(uint32_t)) * kaa_list_get_size(self->status->topic_states); + } + + if (kaa_list_get_size(self->uids) > 0) { + *expected_size += sizeof(uint32_t); //field id + reserved + count + *expected_size += sizeof(uint32_t) * kaa_list_get_size(self->uids); + + kaa_list_node_t *uid_node = kaa_list_begin(self->uids); + while (uid_node) { + kaa_notifications_uid_t *uid = kaa_list_get_data(uid_node); + *expected_size += kaa_aligned_size_get(uid->length); + uid_node = kaa_list_next(uid_node); + } + } + + *expected_size += get_subscriptions_size(self->subscriptions); + *expected_size += get_subscriptions_size(self->unsubscriptions); + + self->extension_payload_size = *expected_size; + *expected_size += KAA_EXTENSION_HEADER_SIZE; + return KAA_ERR_NONE; +} + +static void serialize_topic_state(kaa_topic_state_t *state, kaa_notification_manager_t *self) +{ + KAA_RETURN_IF_NIL3(state, self, self->writer, ); + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Serializing topic state: topic id '%llu', sqn '%lu'", state->topic_id, state->sqn_number); + *(uint64_t *)self->writer->current = KAA_HTONLL(state->topic_id); + self->writer->current += sizeof(uint64_t); + *(uint32_t *)self->writer->current = KAA_HTONL((uint32_t)state->sqn_number); + self->writer->current += sizeof(uint32_t); +} + +static void serialize_notifications_uid(kaa_notifications_uid_t * uid, kaa_platform_message_writer_t *writer) +{ + KAA_RETURN_IF_NIL2(uid, writer, ); + *(uint32_t *)writer->current = KAA_HTONL(uid->length); + writer->current += sizeof(uint32_t); + kaa_platform_message_write_aligned(writer, uid->data, uid->length); +} + +static void serialize_subscription(uint64_t *topic_id, kaa_notification_manager_t *self) +{ + KAA_RETURN_IF_NIL3(topic_id, self, self->writer, ); + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Topic id '%llu'", *topic_id); + *(uint64_t *)self->writer->current = KAA_HTONLL(*topic_id); + self->writer->current += sizeof(uint64_t); +} + +static void serialize_subscriptions(kaa_notification_manager_t *self, uint8_t subscription_type) +{ + KAA_RETURN_IF_NIL(self, ); + + kaa_list_t *subscriptions = NULL; + + if (subscription_type == SUBSCRIPTION_ID) { + KAA_RETURN_IF_NIL(kaa_list_get_size(self->subscriptions), ); + subscriptions = self->subscriptions; + } + if (subscription_type == UNSUBSCRIPTION_ID) { + KAA_RETURN_IF_NIL(kaa_list_get_size(self->unsubscriptions), ); + subscriptions = self->unsubscriptions; + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to serialize %s", subscription_type == SUBSCRIPTION_ID ? "subscriptions" : "unsubscriptions"); + + *(uint8_t *)self->writer->current = subscription_type; + self->writer->current += sizeof(uint16_t); + *(uint16_t *)self->writer->current = KAA_HTONS((uint16_t)kaa_list_get_size(subscriptions)); + self->writer->current += sizeof(uint16_t); + + + kaa_list_for_each(kaa_list_begin(subscriptions), kaa_list_back(subscriptions), + (process_data)serialize_subscription, self); +} + +kaa_error_t kaa_notification_manager_request_serialize(kaa_notification_manager_t *self, kaa_platform_message_writer_t *writer) +{ + KAA_RETURN_IF_NIL2(self, writer, KAA_ERR_BADPARAM); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to serialize client notification sync"); + + self->writer = writer; + + kaa_error_t err = kaa_platform_message_write_extension_header(writer, + KAA_EXTENSION_NOTIFICATION, KAA_CLIENT_WANTS_TO_RECEIVE_NOTIFICATIONS, + self->extension_payload_size); + if (err) { + KAA_LOG_ERROR(self->logger, KAA_ERR_BADPARAM, "Failed to write notification header"); + return KAA_ERR_BADPARAM; + } + + *(int32_t *)writer->current = KAA_HTONL((int32_t)self->status->topic_list_hash); + writer->current += sizeof(int32_t); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to serialize %zu topic states", kaa_list_get_size(self->status->topic_states)); + if (kaa_list_get_size(self->status->topic_states) > 0) { + *(uint8_t *)writer->current = (uint8_t)TOPICS_STATE_ID; + writer->current += sizeof(uint16_t); + *(uint16_t *)writer->current = KAA_HTONS((uint16_t)kaa_list_get_size(self->status->topic_states)); + writer->current += sizeof(uint16_t); + + kaa_list_for_each(kaa_list_begin(self->status->topic_states), + kaa_list_back(self->status->topic_states), + (process_data)serialize_topic_state, self); + } + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to serialize %zu uids", kaa_list_get_size(self->uids)); + if (kaa_list_get_size(self->uids) > 0) { + *(uint8_t *)writer->current = (uint8_t)UID_ID; + writer->current += sizeof(uint16_t); + *(uint16_t *)writer->current = KAA_HTONS(kaa_list_get_size(self->uids)); + writer->current += sizeof(uint16_t); + + kaa_list_for_each(kaa_list_begin(self->uids), kaa_list_back(self->uids), + (process_data)serialize_notifications_uid, writer); + } + + serialize_subscriptions(self, SUBSCRIPTION_ID); + serialize_subscriptions(self, UNSUBSCRIPTION_ID); + + return KAA_ERR_NONE; +} + +static void destroy_notifications_uid(void *data) +{ + if (!data) { + return; + } + + KAA_RETURN_IF_NIL(data,); + kaa_notifications_uid_t *uid = data; + KAA_FREE(uid->data); + KAA_FREE(uid); +} + +static void destroy_topic(void *data) +{ + if (!data) { + return; + } + + kaa_topic_t *topic = data; + KAA_FREE(topic->name); + KAA_FREE(data); +} + +static void destroy_optional_listeners_wrapper(void *data) +{ + KAA_RETURN_IF_NIL(data,); + kaa_optional_notification_listeners_wrapper_t *wrapper = data; + kaa_list_destroy(wrapper->listeners, NULL); + KAA_FREE(wrapper); +} + +/** @deprecated Use kaa_extension_notification_deinit(). */ +void kaa_notification_manager_destroy(kaa_notification_manager_t *self) +{ + if (!self) { + return; + } + + kaa_list_destroy(self->mandatory_listeners, kaa_data_destroy); + kaa_list_destroy(self->topics_listeners, kaa_data_destroy); + kaa_list_destroy(self->subscriptions, kaa_data_destroy); + kaa_list_destroy(self->unsubscriptions, kaa_data_destroy); + kaa_list_destroy(self->optional_listeners, destroy_optional_listeners_wrapper); + kaa_list_destroy(self->uids, destroy_notifications_uid); + kaa_list_destroy(self->notifications, kaa_destroy_notification_node); + + KAA_FREE(self); +} + +/** @deprecated Use kaa_extension_notification_init(). */ +kaa_error_t kaa_notification_manager_create(kaa_notification_manager_t **self, kaa_status_t *status, + kaa_channel_manager_t *channel_manager, kaa_logger_t *logger) +{ + if (!self || !status || !channel_manager || !logger) { + return KAA_ERR_BADPARAM; + } + + kaa_notification_manager_t *manager = KAA_MALLOC(sizeof(*manager)); + if (!manager) { + return KAA_ERR_NOMEM; + } + + manager->mandatory_listeners = kaa_list_create(); + manager->topics_listeners = kaa_list_create(); + manager->optional_listeners = kaa_list_create(); + manager->subscriptions = kaa_list_create(); + manager->unsubscriptions = kaa_list_create(); + manager->notifications = kaa_list_create(); + manager->uids = kaa_list_create(); + + if (!manager->mandatory_listeners || !manager->topics_listeners + || !manager->optional_listeners ||!manager->subscriptions + || !manager->unsubscriptions || !manager->uids) + { + kaa_notification_manager_destroy(manager); + return KAA_ERR_NOMEM; + } + + manager->extension_payload_size = 0; + + manager->writer = NULL; + manager->status = status; + manager->channel_manager = channel_manager; + manager->logger = logger; + + *self = manager; + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_add_notification_listener(kaa_notification_manager_t *self, kaa_notification_listener_t *listener, uint32_t* listener_id) +{ + KAA_RETURN_IF_NIL2(self, listener, KAA_ERR_BADPARAM); + + if (!listener->callback) { + KAA_LOG_WARN(self->logger, KAA_ERR_BADPARAM, "Failed to add mandatory notification listener: NULL callback"); + return KAA_ERR_BADPARAM; + } + + uint32_t id; + kaa_error_t err = kaa_calculate_notification_listener_id(listener, &id); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to calculate mandatory listener id"); + return err; + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to add mandatory notification listener, id '%lu'", id); + + if (kaa_list_find_next(kaa_list_begin(self->mandatory_listeners), &kaa_find_notification_listener_by_id, &id)) { + KAA_LOG_WARN(self->logger, KAA_ERR_ALREADY_EXISTS, "Failed to add mandatory listener: listener is already subscribed"); + return KAA_ERR_ALREADY_EXISTS; + } + + kaa_notification_listener_wrapper_t* wrapper = KAA_MALLOC(sizeof(*wrapper)); + KAA_RETURN_IF_NIL(wrapper, KAA_ERR_NOMEM); + + if (!kaa_list_push_front(self->mandatory_listeners, wrapper)) { + KAA_FREE(wrapper); + KAA_LOG_WARN(self->logger, KAA_ERR_NOMEM, "Failed to add mandatory listener"); + return KAA_ERR_NOMEM; + } + + wrapper->listener = *listener; + wrapper->id = id; // for user to have convenient way to address notification listener + + if (listener_id) { + *listener_id = id; + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Added mandatory notification listener id '%lu'", id); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_remove_notification_listener(kaa_notification_manager_t *self, uint32_t *listener_id) +{ + KAA_RETURN_IF_NIL2(self, listener_id, KAA_ERR_BADPARAM); + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to remove mandatory notification listener, id '%lu'", *listener_id); + + kaa_error_t err = kaa_list_remove_first(self->mandatory_listeners, + kaa_find_notification_listener_by_id, listener_id, &kaa_data_destroy); + if (err) { + KAA_LOG_TRACE(self->logger, err, + "Failed to remove mandatory notification listener: the listener with id '%lu' is not found", *listener_id); + } else { + KAA_LOG_TRACE(self->logger, err, + "Removed mandatory notification listener, id '%lu'", *listener_id); + } + + return err; +} + +kaa_error_t kaa_add_optional_notification_listener(kaa_notification_manager_t *self, + kaa_notification_listener_t *listener, uint64_t *topic_id, uint32_t *listener_id) +{ + KAA_RETURN_IF_NIL4(self, listener, listener->callback, topic_id, KAA_ERR_BADPARAM); + + if (!kaa_list_find_next(kaa_list_begin(self->status->topics), &kaa_find_topic_by_id, topic_id)) { + KAA_LOG_WARN(self->logger, KAA_ERR_NOT_FOUND, + "Failed to add optional notification listener: topic with id '%llu' not found", + *topic_id); + return KAA_ERR_NOT_FOUND; + } + + uint32_t id; + kaa_error_t err = kaa_calculate_notification_listener_id(listener, &id); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to calculate optional listener id"); + return err; + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to add optional notification listener: id '%lu', topic id '%llu'", id, *topic_id); + + kaa_notification_listener_wrapper_t *wrapper = KAA_MALLOC(sizeof(*wrapper)); + KAA_RETURN_IF_NIL(wrapper, KAA_ERR_NOMEM); + + kaa_list_node_t *opt_listeners_node = kaa_list_find_next( + kaa_list_begin(self->optional_listeners), kaa_find_optional_notification_listener_by_id, + topic_id); + if (!opt_listeners_node) { + kaa_optional_notification_listeners_wrapper_t* optional_wrapper = KAA_MALLOC(sizeof(*optional_wrapper)); + if (!optional_wrapper) { + KAA_FREE(wrapper); + return KAA_ERR_NOMEM; + } + + optional_wrapper->listeners = kaa_list_create(); + if (!kaa_list_push_front(optional_wrapper->listeners, wrapper)) { + KAA_FREE(wrapper); + destroy_optional_listeners_wrapper(optional_wrapper); + return KAA_ERR_NOMEM; + } + + if (!kaa_list_push_front(self->optional_listeners, optional_wrapper)) { + KAA_FREE(wrapper); + destroy_optional_listeners_wrapper(optional_wrapper); + return KAA_ERR_NOMEM; + } + + optional_wrapper->topic_id = *topic_id; + } else { + kaa_optional_notification_listeners_wrapper_t* optional_wrapper = kaa_list_get_data(opt_listeners_node); + + if (kaa_list_find_next(kaa_list_begin(optional_wrapper->listeners), &kaa_find_notification_listener_by_id, &id)) { + KAA_LOG_WARN(self->logger, KAA_ERR_ALREADY_EXISTS, "Failed to add the optional listener: the listener is already subscribed"); + KAA_FREE(wrapper); + return KAA_ERR_ALREADY_EXISTS; + } + + if (!kaa_list_push_front(optional_wrapper->listeners, wrapper)) { + KAA_FREE(wrapper); + return KAA_ERR_NOMEM; + } + } + + wrapper->listener = *listener; + wrapper->id = id; // for user to have convenient way to address notification listener + if (listener_id) { + *listener_id = id; + } + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Added optional notification listener: id '%lu', topic id '%llu'", id, *topic_id); + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_remove_optional_notification_listener(kaa_notification_manager_t *self, uint64_t *topic_id, uint32_t *listener_id) +{ + KAA_RETURN_IF_NIL3(self, topic_id, listener_id, KAA_ERR_BADPARAM); + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to remove optional notification listener: id '%u', topic id '%u'", *listener_id, *topic_id); + + kaa_list_node_t *opt_listeners_node = kaa_list_find_next( + kaa_list_begin(self->optional_listeners), + &kaa_find_optional_notification_listener_by_id, topic_id); + if (!opt_listeners_node) { + KAA_LOG_WARN(self->logger, KAA_ERR_NOT_FOUND, "Failed to remove the optional listener: there is no listeners subscribed on this topic (topic id '%llu').", *topic_id); + return KAA_ERR_NOT_FOUND; + } + + kaa_optional_notification_listeners_wrapper_t *optional_wrapper = kaa_list_get_data(opt_listeners_node); + + kaa_error_t error = kaa_list_remove_first(optional_wrapper->listeners, + &kaa_find_notification_listener_by_id, listener_id, &kaa_data_destroy); + if (error) { + KAA_LOG_WARN(self->logger, KAA_ERR_NOT_FOUND, "Failed to remove the optional listener: the listener with id '%lu' is not found", *listener_id); + return error; + } else { + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Removed optional notification listener id: '%lu', topic id '%llu'", *listener_id, *topic_id); + if (!kaa_list_get_size(optional_wrapper->listeners)) { + kaa_list_remove_at(self->optional_listeners, opt_listeners_node, &destroy_optional_listeners_wrapper); + } + return KAA_ERR_NONE; + } +} + +kaa_error_t kaa_add_topic_list_listener(kaa_notification_manager_t *self, kaa_topic_listener_t *listener, uint32_t *topic_listener_id) +{ + KAA_RETURN_IF_NIL3(self, listener, listener->callback, KAA_ERR_BADPARAM); + + + uint32_t id; + kaa_error_t err = kaa_calculate_topic_listener_id(listener, &id); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to calculate mandatory listener's id"); + return err; + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to add topic list listener, id '%lu'", id); + + kaa_topic_listener_wrapper_t *wrapper = (kaa_topic_listener_wrapper_t *) KAA_MALLOC(sizeof(kaa_topic_listener_wrapper_t)); + KAA_RETURN_IF_NIL(wrapper, KAA_ERR_NOMEM); + + if (kaa_list_find_next(kaa_list_begin(self->topics_listeners), &kaa_find_topic_listener_by_id, &id)) { + KAA_LOG_WARN(self->logger, KAA_ERR_ALREADY_EXISTS, "Failed to add topic the listener: the listener is already subscribed"); + KAA_FREE(wrapper); + return KAA_ERR_ALREADY_EXISTS; + } + + if (!kaa_list_push_front(self->topics_listeners, wrapper)) { + KAA_LOG_ERROR(self->logger, KAA_ERR_NOMEM, "Failed to add the topic listener"); + KAA_FREE(wrapper); + return KAA_ERR_NOMEM; + } + + wrapper->listener = *listener; + wrapper->id = id;// for user to have convenient way to address notification listener + if (topic_listener_id) { + *topic_listener_id = id; + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Added topic list listener, id '%lu'", id); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_remove_topic_list_listener(kaa_notification_manager_t *self, uint32_t *topic_listener_id) +{ + KAA_RETURN_IF_NIL2(self, topic_listener_id, KAA_ERR_BADPARAM); + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to remove topic list listener, id '%lu'", *topic_listener_id); + + kaa_error_t err = kaa_list_remove_first(self->topics_listeners, + &kaa_find_topic_listener_by_id, topic_listener_id, &kaa_data_destroy); + if (err) { + KAA_LOG_WARN(self->logger, err, + "Failed to remove topic list listener: listener id '%lu' not found", + *topic_listener_id); + return err; + } + KAA_LOG_TRACE(self->logger, err, "Removed topic list listener, id '%lu'", *topic_listener_id); + return err; +} + +kaa_error_t kaa_get_topics(kaa_notification_manager_t *self, kaa_list_t **topics) +{ + KAA_RETURN_IF_NIL2(self, topics, KAA_ERR_BADPARAM); + *topics = self->status->topics; + return KAA_ERR_NONE; +} + +static void notify_topic_update_subscriber(kaa_topic_listener_wrapper_t *wrapper, kaa_list_t *topics) +{ + KAA_RETURN_IF_NIL2(wrapper, topics, ); + wrapper->listener.callback(wrapper->listener.context, topics); +} + +static kaa_error_t kaa_notify_topic_update_subscribers(kaa_notification_manager_t *self, kaa_list_t *topics) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_BADPARAM); + kaa_list_for_each(kaa_list_begin(self->topics_listeners), kaa_list_back(self->topics_listeners), + (process_data)notify_topic_update_subscriber, topics); + return KAA_ERR_NONE; +} + +static kaa_error_t kaa_notify_mandatory_notification_subscribers(kaa_notification_manager_t *self, + uint64_t topic_id, kaa_notification_t *notification) +{ + KAA_RETURN_IF_NIL2(self, notification, KAA_ERR_BADPARAM); + kaa_list_node_t *current_listener_node = kaa_list_begin(self->mandatory_listeners); + while (current_listener_node) { + kaa_notification_listener_wrapper_t *wrapper = kaa_list_get_data(current_listener_node); + wrapper->listener.callback(wrapper->listener.context, &topic_id, notification); + current_listener_node = kaa_list_next(current_listener_node); + } + return KAA_ERR_NONE; +} + +static kaa_error_t kaa_notify_optional_notification_subscribers(kaa_notification_manager_t *self, + uint64_t topic_id, kaa_notification_t *notification) +{ + KAA_RETURN_IF_NIL3(self, topic_id, notification, KAA_ERR_BADPARAM); + kaa_list_node_t *opt_listeners_node = kaa_list_find_next( + kaa_list_begin(self->optional_listeners), + kaa_find_optional_notification_listener_by_id, &topic_id); + KAA_RETURN_IF_NIL(opt_listeners_node, KAA_ERR_NOT_FOUND); + + kaa_optional_notification_listeners_wrapper_t* optional_wrapper = + kaa_list_get_data(opt_listeners_node); + + kaa_list_node_t *optional_listener_node = kaa_list_begin(optional_wrapper->listeners); + while (optional_listener_node) { + kaa_notification_listener_wrapper_t *wrapper = kaa_list_get_data(optional_listener_node); + wrapper->listener.callback(wrapper->listener.context, &topic_id, notification); + optional_listener_node = kaa_list_next(optional_listener_node); + } + return KAA_ERR_NONE; +} + +static kaa_error_t kaa_add_subscribtion_or_unsubscribtion(kaa_list_t *target, uint64_t *topic_id) +{ + KAA_RETURN_IF_NIL2(target, topic_id, KAA_ERR_BADPARAM); + uint64_t *subs_topic_id = (uint64_t *) KAA_MALLOC(sizeof(uint64_t)); + KAA_RETURN_IF_NIL(subs_topic_id, KAA_ERR_NOMEM); + *subs_topic_id = *topic_id; + if (!kaa_list_push_front(target, subs_topic_id)) { + KAA_FREE(subs_topic_id); + return KAA_ERR_NOMEM; + } + + return KAA_ERR_NONE; +} + +static kaa_error_t kaa_add_subscribtions_or_unsubscribtions(kaa_notification_manager_t *self, uint64_t *topic_ids, size_t size, uint8_t subscription_id) +{ + KAA_RETURN_IF_NIL2(self, topic_ids, KAA_ERR_BADPARAM); + + kaa_list_t *new_subscriptions = kaa_list_create(); + KAA_RETURN_IF_NIL(new_subscriptions, KAA_ERR_NOMEM); + while (size--) { + uint64_t *subs_topic_id = KAA_MALLOC(sizeof(*subs_topic_id)); + KAA_RETURN_IF_NIL(subs_topic_id, KAA_ERR_NOMEM); + *subs_topic_id = topic_ids[size]; + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to add %s to topic with id '%llu'", + subscription_id == SUBSCRIPTION_ID ? "subscription" : "unsubscription", + *subs_topic_id); + if (!kaa_list_push_front(new_subscriptions, subs_topic_id)) { + KAA_FREE(subs_topic_id); + kaa_list_destroy(new_subscriptions, &kaa_data_destroy); + return KAA_ERR_NOMEM; + } + } + kaa_list_t *subscriptions = subscription_id == SUBSCRIPTION_ID ? self->subscriptions : self->unsubscriptions; + kaa_lists_merge(subscriptions, new_subscriptions); + kaa_list_destroy(new_subscriptions, NULL); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_subscribe_to_topic(kaa_notification_manager_t *self, uint64_t *topic_id, bool force_sync) +{ + KAA_RETURN_IF_NIL2(self, topic_id, KAA_ERR_BADPARAM); + kaa_topic_t* topic = NULL; + kaa_error_t err = kaa_find_topic(self, &topic, topic_id); + if (err) { + KAA_LOG_WARN(self->logger, KAA_ERR_BADPARAM, "Failed to subscribe to the topic with id '%llu'.", *topic_id); + return err; + } + if (topic->subscription_type == MANDATORY_SUBSCRIPTION) { + KAA_LOG_WARN(self->logger, KAA_ERR_BADPARAM, "Failed to subscribe to the topic with id '%llu'. Topic isn't optional.", *topic_id); + return KAA_ERR_BADPARAM; + } + err = kaa_add_subscribtion_or_unsubscribtion(self->subscriptions, topic_id); + KAA_RETURN_IF_ERR(err); + + if (force_sync) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to subscribe to topic '%llu'", *topic_id); + kaa_sync_topic_subscriptions(self); + } else { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Subscription to topic '%llu' is postponed till sync", *topic_id); + } + return KAA_ERR_NONE; +} + +kaa_error_t kaa_subscribe_to_topics(kaa_notification_manager_t *self, uint64_t *topic_ids, size_t size, bool force_sync) +{ + KAA_RETURN_IF_NIL2(self, topic_ids, KAA_ERR_BADPARAM); + kaa_topic_t *topic = NULL; + kaa_error_t err = KAA_ERR_NONE; + size_t size_copy = size; + while (size_copy--) { + err = kaa_find_topic(self, &topic, topic_ids + size_copy); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to subscribe to the topic: topic not found, id '%llu'", topic_ids[size_copy]); + return err; + } else { + if (topic->subscription_type == MANDATORY_SUBSCRIPTION) { + KAA_LOG_WARN(self->logger, KAA_ERR_BADPARAM, "Failed to subscribe to the topics. Topic with id '%llu'. Topic isn't optional.", topic_ids[size_copy]); + return KAA_ERR_BADPARAM; + } + } + } + if (force_sync) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to subscribe to the topics"); + } else { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Subscription to topics is postponed till sync"); + } + err = kaa_add_subscribtions_or_unsubscribtions(self, topic_ids, size, SUBSCRIPTION_ID); + KAA_RETURN_IF_ERR(err); + + if (force_sync) { + kaa_sync_topic_subscriptions(self); + } + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_unsubscribe_from_topic(kaa_notification_manager_t *self, uint64_t *topic_id, bool force_sync) +{ + KAA_RETURN_IF_NIL2(self, topic_id, KAA_ERR_BADPARAM); + kaa_topic_t* topic = NULL; + kaa_error_t err = kaa_find_topic(self, &topic, topic_id); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to unsubscribe from the topic with id '%llu'.", *topic_id); + return err; + } + if (topic->subscription_type == MANDATORY_SUBSCRIPTION) { + KAA_LOG_WARN(self->logger, KAA_ERR_BADPARAM, "Failed to unsubscribe from the topic with id '%llu'. Topic isn't optional.", *topic_id); + return KAA_ERR_BADPARAM; + } + err = kaa_add_subscribtion_or_unsubscribtion(self->unsubscriptions, topic_id); + KAA_RETURN_IF_ERR(err); + + if (force_sync) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to unsubscribe from the topic '%llu'", *topic_id); + kaa_sync_topic_subscriptions(self); + } else { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Unsubscription from the topic '%llu' is postponed till sync", *topic_id); + } + return KAA_ERR_NONE; +} + +kaa_error_t kaa_unsubscribe_from_topics(kaa_notification_manager_t *self, uint64_t *topic_ids, size_t size, bool force_sync) +{ + KAA_RETURN_IF_NIL2(self, topic_ids, KAA_ERR_BADPARAM); + kaa_topic_t *topic = NULL; + kaa_error_t err = KAA_ERR_NONE; + size_t size_copy = size; + + while (size_copy--) { + err = kaa_find_topic(self, &topic, topic_ids + size_copy); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to unsubscribe from the topic: topic not found, id '%llu'", topic_ids[size_copy]); + return err; + } else { + if (topic->subscription_type == MANDATORY_SUBSCRIPTION) { + KAA_LOG_WARN(self->logger, KAA_ERR_BADPARAM, "Failed to unsubscribe from the topics. Topic with id '%llu'. Topic isn't optional.", topic_ids[size_copy]); + return KAA_ERR_BADPARAM; + } + } + } + + if (force_sync) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to unsubscribe from the topics"); + } else { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Unsubscription from the topics is postponed till sync"); + } + + err = kaa_add_subscribtions_or_unsubscribtions(self, topic_ids, size, UNSUBSCRIPTION_ID); + KAA_RETURN_IF_ERR(err); + + if (force_sync) { + kaa_sync_topic_subscriptions(self); + } + return KAA_ERR_NONE; +} + +static kaa_error_t do_sync(kaa_notification_manager_t *self) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_BADPARAM); + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, + notification_sync_services[0]); + if (channel) { + channel->sync_handler(channel->context, notification_sync_services, 1); + } else { + KAA_LOG_ERROR(self->logger, KAA_ERR_NOT_FOUND, "Failed to sync: transport channel not found"); + } + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_sync_topic_subscriptions(kaa_notification_manager_t *self) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_BADPARAM); + return do_sync(self); +} + +static kaa_error_t kaa_topic_list_update(kaa_notification_manager_t *self, kaa_list_t *new_topics) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_BADPARAM); + kaa_topic_t *topic = NULL; + kaa_list_node_t *topic_node = kaa_list_begin(new_topics); + + // "Substracts" self topics from new topics based on IDs + while (topic_node && kaa_list_get_size(self->status->topics) > 0) { + topic = kaa_list_get_data(topic_node); + KAA_RETURN_IF_NIL(topic, KAA_ERR_NOMEM); + kaa_list_remove_first(self->status->topics, kaa_find_topic_by_id, &topic->id, destroy_topic); + topic_node = kaa_list_next(topic_node); + } + + if (kaa_list_get_size(self->status->topics)) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, + "Going to remove optional listener(s) from obsolete %zu topics", + kaa_list_get_size(self->status->topics)); + + kaa_list_node_t *outdated_topics = kaa_list_begin(self->status->topics); + while (outdated_topics) { + topic = kaa_list_get_data(outdated_topics); + kaa_list_remove_first(self->optional_listeners, + kaa_find_optional_notification_listener_by_id, &topic->id, + destroy_optional_listeners_wrapper); + outdated_topics = kaa_list_next(outdated_topics); + } + } + + kaa_list_destroy(self->status->topics, &destroy_topic); + kaa_list_sort(new_topics, &sort_topic_by_id); + self->status->topic_list_hash = kaa_list_hash(new_topics, &get_topic_id); + self->status->topics = new_topics; + return kaa_notify_topic_update_subscribers(self, new_topics); +} + +static kaa_error_t kaa_notification_received(kaa_notification_manager_t *self, kaa_notification_t *notification, uint64_t topic_id) +{ + KAA_RETURN_IF_NIL2(self, notification, KAA_ERR_BADDATA); + if (kaa_notify_optional_notification_subscribers(self, topic_id, notification)) { + kaa_notify_mandatory_notification_subscribers(self, topic_id, notification); + } + return KAA_ERR_NONE; +} + +static kaa_error_t update_sequence_number(kaa_notification_manager_t *self, uint64_t topic_id, uint32_t sqn_number) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_BADPARAM); + + kaa_topic_state_t *state; + kaa_list_node_t *it = kaa_list_find_next(kaa_list_begin(self->status->topic_states), + kaa_find_topic_state_by_id, &topic_id); + if (!it) { + state = KAA_MALLOC(sizeof(*state)); + KAA_RETURN_IF_NIL(state, KAA_ERR_NOMEM); + + if (!kaa_list_push_front(self->status->topic_states, state)) { + KAA_FREE(state); + return KAA_ERR_NOMEM; + } + + state->topic_id = topic_id; + state->sqn_number = sqn_number; + self->status->has_update = true; + } else { + state = kaa_list_get_data(it); + if (sqn_number > state->sqn_number) { + state->sqn_number = sqn_number; + self->status->has_update = true; + } + } + return KAA_ERR_NONE; +} + +static void kaa_notify_notification_listeners(void *data, void* context) +{ + KAA_RETURN_IF_NIL2(data, context, ); + kaa_error_t err = KAA_ERR_NONE; + kaa_topic_notifications_node_t *node = data; + kaa_list_node_t *notification_list_node = kaa_list_begin(node->notifications); + kaa_notification_manager_t *self = context; + + while(notification_list_node) { + kaa_notification_wrapper_t *wrapper = kaa_list_get_data(notification_list_node); + kaa_list_node_t *it = kaa_list_find_next(kaa_list_begin(self->status->topic_states), + kaa_find_topic_state_by_id, &node->topic_id); + if (it) { + kaa_topic_state_t *state = kaa_list_get_data(it); + if (wrapper->sqn > state->sqn_number) { + err = kaa_notification_received(self, wrapper->notification, node->topic_id); + } + } else { + err = kaa_notification_received(self, wrapper->notification, node->topic_id); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to notify notification listener"); + return; + } + } + + err = update_sequence_number(self, node->topic_id, wrapper->sqn); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to update notification sequence number for topic '%llu'", node->topic_id); + } + + notification_list_node = kaa_list_next(notification_list_node); + } +} + +kaa_error_t kaa_notification_manager_handle_server_sync(kaa_notification_manager_t *self, + kaa_platform_message_reader_t *reader, uint32_t extension_length) +{ + KAA_RETURN_IF_NIL2(self, reader, KAA_ERR_BADPARAM); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received notification server sync: options 0, payload size %lu", extension_length); + + kaa_list_clear(self->subscriptions, &kaa_data_destroy); + kaa_list_clear(self->unsubscriptions, &kaa_data_destroy); + + if (extension_length > 0) { + kaa_error_t err = KAA_ERR_NONE; + if (KAA_NTOHL(*((uint32_t *) reader->current)) == KAA_NO_DELTA) { + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Received delta response: NO DELTA. Going to clear uids list..."); + kaa_list_clear(self->uids, destroy_notifications_uid); + } + shift_and_sub_extension(reader, &extension_length, sizeof(uint32_t)); + while (extension_length > 0) { + uint8_t field_id = *(uint8_t *)reader->current; //field id + shift_and_sub_extension(reader, &extension_length, sizeof(uint16_t)); // + reserved + + switch (field_id) { + case NOTIFICATIONS: { + kaa_notification_t *notification = NULL; + uint32_t seq_number = 0; + uint16_t notifications_count = KAA_NTOHS(*((uint16_t *) reader->current)); // notifications count + shift_and_sub_extension(reader, &extension_length, sizeof(uint16_t)); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received notifications. Notifications count is %u", notifications_count); + + while (notifications_count--) { + seq_number = KAA_NTOHL(*((uint32_t *) reader->current)); // sqn of the last received notification + shift_and_sub_extension(reader, &extension_length, sizeof(uint16_t) + sizeof(uint32_t)); // + notification type + uint16_t uid_length = KAA_NTOHS(*((uint16_t *) reader->current)); // uid length + shift_and_sub_extension(reader, &extension_length, sizeof(uint16_t)); + uint32_t notification_size = KAA_NTOHL(*((uint32_t *) reader->current)); // notification body size + shift_and_sub_extension(reader, &extension_length, sizeof(uint32_t)); + uint64_t topic_id = KAA_NTOHLL(*((uint64_t *) reader->current)); // topic id + shift_and_sub_extension(reader, &extension_length, sizeof(uint64_t)); + + kaa_topic_t *topic_found; + err = kaa_find_topic(self, &topic_found, &topic_id); + if (err) { + KAA_LOG_WARN(self->logger, err, "Topic with id %llu is not found. Skipping notification...", topic_id); + size_t skiped_size = kaa_aligned_size_get(uid_length) + kaa_aligned_size_get(notification_size); + shift_and_sub_extension(reader, &extension_length, skiped_size); + continue; + } + if (uid_length) { + kaa_notifications_uid_t *uid = KAA_MALLOC(sizeof(*uid)); + KAA_RETURN_IF_NIL(uid, KAA_ERR_NOMEM); + + uid->length = uid_length; + uid->data = KAA_MALLOC(uid_length); + if (!uid->data) { + destroy_notifications_uid(uid); + return KAA_ERR_NOMEM; + } + err = kaa_platform_message_read_aligned(reader, uid->data, uid->length); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to read UID body, topic id %llu", topic_id); + return err; + } + extension_length -= kaa_aligned_size_get(uid->length); + + kaa_list_node_t *has_been_already_received = kaa_list_find_next(kaa_list_begin(self->uids), &kaa_find_uid, uid); + if (has_been_already_received) { + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "This unicast notification has been already received"); + shift_and_sub_extension(reader, &extension_length, kaa_aligned_size_get(notification_size)); + destroy_notifications_uid(uid); + continue; + } + + if (!kaa_list_push_front(self->uids, uid)) { + destroy_notifications_uid(uid); + return KAA_ERR_NOMEM; + } + } + if (notification_size) { + avro_reader_t avro_reader = avro_reader_memory((const char *)reader->current, notification_size); + if (!avro_reader) { + return KAA_ERR_NOMEM; + } + notification = KAA_NOTIFICATION_DESERIALIZE(avro_reader); + avro_reader_free(avro_reader); + if (!notification) { + KAA_LOG_WARN(self->logger, KAA_ERR_NOMEM, "Failed to deserialize notification"); + return KAA_ERR_NOMEM; + } + } + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Notification's sqn '%lu', topic id '%llu', type '%s', size '%lu'" + , seq_number, topic_id, uid_length ? "unicast" : "multicast", notification_size); + shift_and_sub_extension(reader, &extension_length, kaa_aligned_size_get(notification_size)); + if (uid_length == 0) { + err = kaa_add_notification_to_map(self->notifications, notification, topic_id, &seq_number); + } else { + err = kaa_notification_received(self, notification, topic_id); + notification->destroy(notification); + } + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to add notification to map"); + return KAA_ERR_NOMEM; + } + } + break; + } + + case TOPICS: { + kaa_list_t *new_topics = kaa_list_create(); + KAA_RETURN_IF_NIL(new_topics, KAA_ERR_NOMEM); + + uint16_t topic_count = KAA_NTOHS(*((uint16_t *) reader->current)); // topics count + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received topics list. Topics count is %u", topic_count); + shift_and_sub_extension(reader, &extension_length, sizeof(uint16_t)); + + while (topic_count--) { + kaa_topic_t *topic = (kaa_topic_t *) KAA_MALLOC(sizeof(kaa_topic_t)); + KAA_RETURN_IF_NIL(topic, KAA_ERR_NOMEM); + topic->id = KAA_NTOHLL(*((uint64_t *) reader->current)); // topic id + shift_and_sub_extension(reader, &extension_length, sizeof(uint64_t)); + topic->subscription_type = (*((uint8_t *) reader->current) == MANDATORY_SUBSCRIPTION) ? MANDATORY_SUBSCRIPTION : OPTIONAL_SUBSCRIPTION; + shift_and_sub_extension(reader, &extension_length, sizeof(uint16_t)); // + reserved + topic->name_length = KAA_NTOHS(*((uint16_t *) reader->current)); // name length + shift_and_sub_extension(reader, &extension_length, sizeof(uint16_t)); + + topic->name = (char *) KAA_MALLOC(topic->name_length + 1); // Topic name + if (!topic->name) { + destroy_topic(topic); + return KAA_ERR_NOMEM; + } + + err = kaa_platform_message_read_aligned(reader, topic->name, topic->name_length); + if (err) { + KAA_LOG_WARN(self->logger, KAA_ERR_BADDATA, "Failed to read topic's name"); + destroy_topic(topic); + return err; + } + + topic->name[topic->name_length] = '\0'; + extension_length -= kaa_aligned_size_get(topic->name_length); + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Topic Id '%llu', subscription type '%s', name '%s'" + , topic->id, (topic->subscription_type == MANDATORY_SUBSCRIPTION) ? "mandatory": "optional", topic->name); + + if (!kaa_list_push_front(new_topics, topic)) { + destroy_topic(topic); + return KAA_ERR_NOMEM; + } + } + + err = kaa_topic_list_update(self, new_topics); + if (err) { + KAA_LOG_WARN(self->logger, err, "Failed to notify topic list listeners"); + } + break; + } + + default: + KAA_LOG_ERROR(self->logger, KAA_ERR_NONE, "Bad field ID type"); + return KAA_ERR_BADDATA; + } + } + } + + if (kaa_list_get_size(self->notifications)) { + kaa_sort_notifications(self->notifications); + kaa_list_for_each(kaa_list_begin(self->notifications), kaa_list_back(self->notifications), kaa_notify_notification_listeners, self); + kaa_list_clear(self->notifications, &kaa_destroy_notification_node); + if (!self->notifications) { + return KAA_ERR_NOMEM; + } + } + + return do_sync(self); +} diff --git a/client/client-multi/client-c/src/extensions/notification/kaa_notification_manager.h b/client/client-multi/client-c/src/extensions/notification/kaa_notification_manager.h new file mode 100644 index 0000000000..5e8c75887b --- /dev/null +++ b/client/client-multi/client-c/src/extensions/notification/kaa_notification_manager.h @@ -0,0 +1,199 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KAA_KAA_NOTIFICATION_H_ +#define KAA_KAA_NOTIFICATION_H_ + +#include +#include +#include + +#include "kaa_error.h" +#include "platform/ext_notification_receiver.h" +#include "collections/kaa_list.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef KAA_NOTIFICATION_MANAGER_T +# define KAA_NOTIFICATION_MANAGER_T + /** + * @brief Kaa notification manager structure. + */ + typedef struct kaa_notification_manager_t kaa_notification_manager_t; +#endif + + + +/** +* @brief Calculates the topic listener id to manage this topic listener. +* +* @param[in] listener The pointer to the listener whose id is calculated. +* @param[out] listener_id The pointer to the variable which is initialized with the calculated id. +* +* @return The error code. +*/ +kaa_error_t kaa_calculate_topic_listener_id(const kaa_topic_listener_t *listener, uint32_t *listener_id); + +/** +* @brief Calculates the notification listener id to manage this notification listener. +* +* @param[in] listener The pointer to the listener whose id is calculated. +* @param[out] listener_id The pointer to the variable which is initialized with the calculated id. +* +* @return The error code. +*/ +kaa_error_t kaa_calculate_notification_listener_id(const kaa_notification_listener_t *listener, uint32_t *listener_id); + +/** +* @brief Adds a mandatory notification listener to receive notifications on mandatory topics. +* +* @param[in] self The pointer to the notification manager instance. +* @param[in] listener The pointer to the listener whose callback is called as soon as a notification is received. +* @param[out] listener_id The pointer to the variable which is initialized with the calculated id. +* +* @return The error code. +*/ +kaa_error_t kaa_add_notification_listener(kaa_notification_manager_t *self, kaa_notification_listener_t *listener, uint32_t *listener_id); + +/** +* @brief Adds an optional notification listener to receive notifications on optional topics. +* +* @param[in] self The pointer to the notification manager instance. +* @param[in] listener The pointer to the listener whose callback is called as soon as a notification is received. +* @param[in] topic_id The pointer to the id of the topic about which the listener is notified. +* @param[out] listener_id The pointer to the variable which is initialized with the calculated id. If @c NULL, @p listener_id is not initialized. +* +* @return The error code. +*/ +kaa_error_t kaa_add_optional_notification_listener(kaa_notification_manager_t *self, kaa_notification_listener_t *listener + , uint64_t *topic_id, uint32_t *listener_id); + +/** +* @brief Removes the mandatory notification listener. +* +* @param[in] self The pointer to the notification manager instance. +* @param[in] listener_id The pointer to the listener id which is used to find the listener that should be removed from the notification listeners list. If @c NULL, @p listener_id is not initialized +* +* @return The error code. +*/ +kaa_error_t kaa_remove_notification_listener(kaa_notification_manager_t *self, uint32_t *listener_id); + +/** +* @brief Removes the optional notification listener. +* +* @param[in] self The pointer to the notification manager instance. +* @param[in] topic_id The pointer to the id of the topic the listener should not be notified about anymore. +* @param[in] listener_id The pointer to the variable which is used to find the listener that should be removed from the notification listeners list. +* +* @return The error code. +*/ +kaa_error_t kaa_remove_optional_notification_listener(kaa_notification_manager_t *self, uint64_t *topic_id, uint32_t *listener_id); + +/** +* @brief Adds a topic list listener. +* +* @param[in] self The pointer to the notification manager instance. +* @param[in] listener The pointer to the listener whose callback is called as soon as a notification is received. +* @param[out] topic_listener_id The pointer to the variable which is initialized with the calculated id. +* +* @return The error code. +*/ +kaa_error_t kaa_add_topic_list_listener(kaa_notification_manager_t *self, kaa_topic_listener_t *listener, uint32_t *topic_listener_id); + +/** +* @brief Removes the topic list listener. +* +* @param[in] self The pointer to the notification manager instance. +* @param[in] topic_listener_id The pointer to the variable which is used to find the listener that should be removed from the topic listeners list. If @c NULL, @p topic_listener_id is not initialized. +* +* @return The error code. +*/ +kaa_error_t kaa_remove_topic_list_listener(kaa_notification_manager_t *self, uint32_t *topic_listener_id); + +/** +* @brief Retrieves the topic list. +* +* @param[in] self The pointer to the notification manager instance. +* @param[out] topics The pointer to the pointer that is initialized with the topic list. +* +* @return The error code. +*/ +kaa_error_t kaa_get_topics(kaa_notification_manager_t *self, kaa_list_t **topics); + +/** +* @brief Subscribes to the topic. +* +* @param[in] self The pointer to the notification manager instance. +* @param[in] topic_id The pointer to the id of the topic to which the endpoint should be subscribed. +* @param[in] force_sync Indicates whether subscription should be performed immediately (true) or should be postponed (false). +* +* @return The error code. +*/ +kaa_error_t kaa_subscribe_to_topic(kaa_notification_manager_t *self, uint64_t *topic_id, bool force_sync); + +/** +* @brief Subscribes to the topics. +* +* @param[in] self The pointer to the notification manager instance. +* @param[in] topic_ids An array of the ids of the topics to which the endpoint should be subscribed. +* @param[in] size The size of the topic ids array. +* @param[in] force_sync Indicates whether subscription should be performed immediately (true) or should be postponed (false). +* +* @return The error code. +*/ +kaa_error_t kaa_subscribe_to_topics(kaa_notification_manager_t *self, uint64_t *topic_ids, size_t size, bool force_sync); + +/** +* @brief Unsubscribes from the topic. +* +* @param[in] self The pointer to the notification manager instance. +* @param[in] topic_id The pointer to the id of the topic from which the endpoint should be unsubscribed. +* @param[in] force_sync Sync topic unsubscription +* +* @return The error code. +*/ +kaa_error_t kaa_unsubscribe_from_topic(kaa_notification_manager_t *self, uint64_t *topic_id, bool force_sync); + +/** +* @brief Unsubscribes from the topics. +* +* @param[in] self The pointer to the notification manager instance. +* @param[in] topic_ids An array of the ids of the topics from which the endpoint should be unsubscribed. +* @param[in] size The size of the topic ids array. +* @param[in] force_sync Indicates whether unsubscription should be performed immediately (true) or should be postponed (false). +* +* @return The error code. +*/ +kaa_error_t kaa_unsubscribe_from_topics(kaa_notification_manager_t *self, uint64_t *topic_ids, size_t size, bool force_sync); + +/** +* @brief Sends the sync request to the server. +* +* @param[in] self The pointer to the notification manager instance. +* +* @return The error code. +*/ +kaa_error_t kaa_sync_topic_subscriptions(kaa_notification_manager_t *self); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* KAA_KAA_NOTIFICATION_H_ */ diff --git a/client/client-multi/client-c/src/extensions/notification/test/test_kaa_notification.c b/client/client-multi/client-c/src/extensions/notification/test/test_kaa_notification.c new file mode 100644 index 0000000000..191ae387ae --- /dev/null +++ b/client/client-multi/client-c/src/extensions/notification/test/test_kaa_notification.c @@ -0,0 +1,336 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "kaa_test.h" + +#include "kaa_notification_manager.h" +#include "kaa.h" +#include +#include "kaa_error.h" +#include "kaa_common.h" +#include "kaa_context.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include "avro_src/avro/io.h" +#include "platform/sock.h" +#include "kaa_platform_common.h" +#include "kaa_platform_utils.h" +#include "kaa_platform_protocol.h" + +//---------------------------------------------------------------------------------------------- +kaa_context_t *context = NULL; + +kaa_notification_listener_t listener; +kaa_notification_listener_t listener_2; +kaa_logger_t* logger; + +uint8_t *buffer_pointer = NULL; +kaa_list_t *topics = NULL; +uint64_t topic_id; + +uint32_t id,id2; +kaa_error_t err = 0; +size_t size = 0; + +uint8_t *pointer_to_sqn = NULL; + +bool listener_has_been_notified = false; +kaa_topic_listener_t topic_listener; +kaa_topic_listener_t topic_listener_2; +//----------------------------------------------------------------------------------------------- +void on_notification(void* contextmock, uint64_t *topic_id, kaa_notification_t *notif) +{ + (void)contextmock; + (void)topic_id; + (void)notif; + printf("\nNotification listener got his Notification\n\n"); + listener_has_been_notified = true; +} + +void on_topic(void* contextmock, kaa_list_t *topics) +{ + (void)contextmock; + (void)topics; + printf("\nTopic list listener got his topic list.\n\n"); +} + +uint8_t *buffer = NULL; +size_t buffer_size = 0; + +/* We should remove status file after running the test, because + * client persists notification sequence number and if the test is run + * two times in a row, notification manager will throw out the notification + * which is serialized in test, because it considers that the notification + * has already been received -> client won't received the notification and + * the test will be failed. + */ +#define KAA_STATUS_FILE_NAME "./kaa_status.bin" +static void remove_state_file(const char* filename) +{ + int res = remove(filename); + if (!res) { + printf("Status file has been successfully removed\n"); + } else { + printf("Error: Can't remove status file!\n"); + } +} + + +int test_init(void) +{ + err = KAA_ERR_NONE; + + err = kaa_init(&context); + if (err) { + return err; + } + listener.callback = &on_notification; + listener.context = NULL; + listener_2.callback = &on_notification; + listener_2.context = &listener; + + topic_listener.callback = &on_topic; + topic_listener.context = NULL; + topic_listener_2.callback = &on_topic; + topic_listener_2.context = &topic_listener; + + topic_id = 22; + return 0; +} + +int test_deinit(void) +{ + if (context) { + kaa_deinit(context); + } + if (buffer_pointer) { + KAA_FREE(buffer_pointer); + } + + remove_state_file(KAA_STATUS_FILE_NAME); + return 0; +} + +void test_deserializing(void **state) +{ + (void)state; + + kaa_notification_t *notification = kaa_notification_notification_create(); + const char *message = "Hello World!!!\n"; + notification->message = kaa_string_copy_create(message); + size = (sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) /*that was header*/+ sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) /*extension options + and payload length*/ + sizeof(uint32_t) + sizeof(uint32_t) /*state sqn and delta status*/ + sizeof(uint16_t) + sizeof(uint16_t) /*field id and topic count*/ + sizeof(uint64_t) /*topic ID*/ + + sizeof(uint16_t) + sizeof(uint16_t) /*subscriptions type + topic name length*/ + sizeof(uint32_t) /*topic name + padding */ + sizeof(uint16_t) + sizeof(uint16_t) /*field id and notifications count*/ + + sizeof(uint32_t) /* Notification sqn */ + sizeof(uint16_t) + sizeof(uint16_t) /* Notification type + uid length */+ sizeof (uint32_t) /* notification body size*/ + sizeof (uint64_t) /*Topic Id*/ + + /*Not unicast notifications*/ + kaa_aligned_size_get(notification->get_size(notification))); + + uint8_t *unserialized_buffer = KAA_MALLOC(size); + ASSERT_NOT_NULL(unserialized_buffer); + buffer_pointer = unserialized_buffer; + memset(unserialized_buffer, 0, size); + *(uint32_t *)unserialized_buffer = KAA_HTONL((uint32_t) KAA_PLATFORM_PROTOCOL_ID); //KAA_HTONL(KAA_PLATFORM_PROTOCOL_ID); + unserialized_buffer += sizeof(uint32_t); + *(uint16_t *)unserialized_buffer = KAA_HTONS((uint16_t)1); + unserialized_buffer += sizeof(uint16_t); + *(uint16_t *)unserialized_buffer = KAA_HTONS((uint16_t)1); // extension count + unserialized_buffer += sizeof(uint16_t); + + *(uint16_t *)unserialized_buffer = KAA_HTONS((uint16_t)KAA_EXTENSION_NOTIFICATION); + unserialized_buffer += sizeof(uint16_t); + unserialized_buffer += sizeof(uint16_t); // pass by extension options + + uint32_t payload_info = sizeof(uint32_t) /*delta status*/ + sizeof(uint16_t) + sizeof(uint16_t) /*field id and topic count*/ + sizeof(uint64_t) /*topic ID*/ + + sizeof(uint16_t) + sizeof(uint16_t) /*subscriptions type + topic name length*/ + sizeof(uint32_t) /*topic name + padding */ + sizeof(uint16_t) + sizeof(uint16_t) /*field id and notifications count*/ + + sizeof(uint32_t) /* Notification sqn */ + sizeof(uint16_t) + sizeof(uint16_t) /* Notification type + uid length */+ sizeof (uint32_t) /* notification body size*/ + sizeof (uint64_t) /*Topic Id*/ + + kaa_aligned_size_get(notification->get_size(notification)); + + *(uint32_t *)unserialized_buffer = KAA_HTONL((uint32_t) payload_info); + unserialized_buffer += sizeof(uint32_t); + *(uint32_t *)unserialized_buffer = KAA_HTONL((uint32_t)2); // Delta status + unserialized_buffer += sizeof(uint32_t); + //TOPICS + *(uint8_t *)unserialized_buffer = (uint8_t)0; + unserialized_buffer += sizeof(uint16_t); + *(uint16_t *)unserialized_buffer = KAA_HTONS ((uint16_t)1); // topics count + unserialized_buffer += sizeof(uint16_t); + *(uint64_t *)unserialized_buffer = KAA_HTONLL((uint64_t)22); //topic id + unserialized_buffer += sizeof(uint64_t); + *(uint8_t *)unserialized_buffer = (uint8_t)OPTIONAL_SUBSCRIPTION; + unserialized_buffer += sizeof(uint16_t); + *(uint16_t *)unserialized_buffer = KAA_HTONS((uint16_t)4); //KAA + unserialized_buffer += sizeof(uint16_t); + *unserialized_buffer++ = 'K';*unserialized_buffer++ = 'A'; // topic name + padding + *unserialized_buffer++ = 'A';*unserialized_buffer++ = 'A'; + unserialized_buffer += (4 - kaa_aligned_size_get(4)); + //----------------------------------------------------------------------- + *(uint8_t *)unserialized_buffer = (uint8_t)1; //Notification field ID + unserialized_buffer += sizeof(uint16_t); + *(uint16_t *)unserialized_buffer = KAA_HTONS ((uint16_t)1); // notitifications count + unserialized_buffer += sizeof(uint16_t); + *(uint32_t *)unserialized_buffer = KAA_HTONL((uint32_t)99); //sqn + pointer_to_sqn = unserialized_buffer; // To have the possibility to change sqn. + unserialized_buffer += sizeof(uint32_t); + *(uint8_t *)unserialized_buffer = (uint8_t)0x1; //notification type + unserialized_buffer += sizeof(uint16_t); + *(uint16_t *)unserialized_buffer = KAA_HTONS ((uint16_t) 0); //uid length + unserialized_buffer += sizeof(uint16_t); + *(uint32_t *)unserialized_buffer = KAA_HTONL ((uint32_t)notification->get_size(notification)); + unserialized_buffer += sizeof(uint32_t); + *(uint64_t *)unserialized_buffer = KAA_HTONLL((uint64_t)22); + unserialized_buffer += sizeof(uint64_t); + + avro_writer_t avro_writer = avro_writer_memory((const char *)unserialized_buffer, notification->get_size(notification)); + notification->serialize(avro_writer, notification); + err = kaa_platform_protocol_process_server_sync(context->platform_protocol, buffer_pointer, size); + avro_writer_free(avro_writer); + + ASSERT_EQUAL(err, KAA_ERR_NONE); + + notification->destroy(notification); +} + +void test_notification_listeners_adding_and_removing(void **state) +{ + (void)state; + + err = kaa_add_notification_listener(context->notification_manager, &listener, &id); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_add_notification_listener(context->notification_manager, &listener, &id); + ASSERT_NOT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_add_notification_listener(context->notification_manager, &listener_2, &id2); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_remove_notification_listener(context->notification_manager, &id); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_remove_notification_listener(context->notification_manager, &id); + ASSERT_NOT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_remove_notification_listener(context->notification_manager, &id2); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_add_optional_notification_listener(context->notification_manager, &listener, &topic_id, &id); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_add_optional_notification_listener(context->notification_manager, &listener, &topic_id, &id); + ASSERT_NOT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_add_optional_notification_listener(context->notification_manager, &listener_2, &topic_id, &id2); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + *(uint32_t *)pointer_to_sqn = KAA_HTONL((uint32_t) 100); // Need to change sqn + err = kaa_platform_protocol_process_server_sync(context->platform_protocol, buffer_pointer, size); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + ASSERT_EQUAL(listener_has_been_notified, true); // whether callback has been called + + err = kaa_remove_optional_notification_listener(context->notification_manager, &topic_id, &id); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_remove_optional_notification_listener(context->notification_manager, &topic_id, &id); + ASSERT_NOT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_remove_optional_notification_listener(context->notification_manager, &topic_id, &id2); + ASSERT_EQUAL(err, KAA_ERR_NONE); +} + +void test_topic_list_listeners_adding_and_removing(void **state) +{ + (void)state; + + err = kaa_get_topics(context->notification_manager, &topics); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_add_topic_list_listener(context->notification_manager, &topic_listener, &id); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_add_topic_list_listener(context->notification_manager, &topic_listener, &id); + ASSERT_NOT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_add_topic_list_listener(context->notification_manager, &topic_listener_2, &id2); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_remove_topic_list_listener(context->notification_manager, &id); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_remove_topic_list_listener(context->notification_manager, &id); + ASSERT_NOT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_remove_topic_list_listener(context->notification_manager, &id2); + ASSERT_EQUAL(err, KAA_ERR_NONE); +} + +void test_retrieving_topic_list(void **state) +{ + (void)state; + + err = kaa_get_topics(context->notification_manager, &topics); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + size_t topics_size = kaa_list_get_size(topics); + ASSERT_EQUAL(topics_size, 1); +} + +void test_serializing(void **state) +{ + (void)state; + + kaa_extension_id service[] = { KAA_EXTENSION_NOTIFICATION }; + + err = kaa_platform_protocol_alloc_serialize_client_sync(context->platform_protocol, service, 1, + &buffer, &buffer_size); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + KAA_FREE(buffer); +} + +void test_subscriptions(void **state) +{ + (void)state; + + err = kaa_subscribe_to_topic(context->notification_manager, &topic_id, false); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_unsubscribe_from_topic(context->notification_manager, &topic_id, false); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + uint64_t fake_ids[2] = { 22, 11 }; + err = kaa_subscribe_to_topics(context->notification_manager, fake_ids, 2, false); + ASSERT_NOT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_unsubscribe_from_topics(context->notification_manager, fake_ids, 2, false); + ASSERT_NOT_EQUAL(err, KAA_ERR_NONE); + + uint64_t existing_ids[1] = { 22 }; + err = kaa_subscribe_to_topics(context->notification_manager, existing_ids, 1, false); + ASSERT_EQUAL(err, KAA_ERR_NONE); + + err = kaa_unsubscribe_from_topics(context->notification_manager, existing_ids, 1, false); + ASSERT_EQUAL(err, KAA_ERR_NONE); +} + +KAA_SUITE_MAIN(Notification, test_init, test_deinit, + KAA_TEST_CASE(deserializing, test_deserializing) + KAA_TEST_CASE(removing_and_adding_notifications_listeners, test_notification_listeners_adding_and_removing) + KAA_TEST_CASE(removing_and_adding_topic_list_listeners, test_topic_list_listeners_adding_and_removing) + KAA_TEST_CASE(topic_list_retrieving, test_retrieving_topic_list) + KAA_TEST_CASE(serializing, test_serializing) + KAA_TEST_CASE(subscriptions, test_subscriptions)) diff --git a/client/client-multi/client-c/src/extensions/profile/CMakeLists.txt b/client/client-multi/client-c/src/extensions/profile/CMakeLists.txt new file mode 100644 index 0000000000..3522c16016 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/profile/CMakeLists.txt @@ -0,0 +1,33 @@ +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set(EXTENSION_PROFILE_SOURCE_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_profile.c) + +set(EXTENSION_PROFILE_HEADER_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_profile.h) + +add_library(extension_profile ${EXTENSION_PROFILE_SOURCE_FILES}) +target_include_directories(extension_profile PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(extension_profile PUBLIC kaac) + +kaa_add_unit_test(NAME test_profile + SOURCES + ${CMAKE_CURRENT_LIST_DIR}/test/test_kaa_profile.c + test/kaa_test_external.c + ${KAA_SRC_FOLDER}/gen/kaa_profile_gen.c + DEPENDS + kaac ${OPENSSL_LIBRARIES}) diff --git a/client/client-multi/client-c/src/extensions/profile/kaa_profile.c b/client/client-multi/client-c/src/extensions/profile/kaa_profile.c new file mode 100644 index 0000000000..60cc6755d7 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/profile/kaa_profile.c @@ -0,0 +1,470 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "kaa_profile_private.h" + +#include "kaa_private.h" + +#include +#include +#include +#include +#include "platform/sock.h" +#include "avro_src/avro/io.h" +#include "platform/ext_sha.h" +#include "platform/ext_key_utils.h" +#include "kaa_status.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include "kaa_defaults.h" +#include "kaa_channel_manager.h" +#include "kaa_platform_common.h" +#include "kaa_platform_utils.h" + + + +#define KAA_PROFILE_RESYNC_OPTION 0x1 + +static kaa_extension_id profile_sync_services[] = { KAA_EXTENSION_PROFILE }; + +typedef struct { + size_t payload_size; + kaa_bytes_t public_key; + kaa_bytes_t access_token; +} kaa_profile_extension_data_t; + +struct kaa_profile_manager_t { + bool need_resync; + kaa_bytes_t profile_body; + kaa_digest profile_hash; + kaa_channel_manager_t *channel_manager; + kaa_status_t *status; + kaa_logger_t *logger; + kaa_profile_extension_data_t *extension_data; +}; + +kaa_error_t kaa_extension_profile_init(kaa_context_t *kaa_context, void **context) +{ + kaa_error_t result = kaa_profile_manager_create(&kaa_context->profile_manager, + kaa_context->status->status_instance, + kaa_context->channel_manager, kaa_context->logger); + *context = kaa_context->profile_manager; + return result; +} + +kaa_error_t kaa_extension_profile_deinit(void *context) +{ + kaa_profile_manager_destroy(context); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_profile_request_get_size(void *context, + size_t *expected_size) +{ + return kaa_profile_request_get_size(context, expected_size); +} + +static bool resync_is_required(kaa_profile_manager_t *self) +{ + return self->need_resync || self->status->profile_needs_resync; +} + +kaa_error_t kaa_extension_profile_request_serialize(void *context, uint32_t request_id, + uint8_t *buffer, size_t *size, bool *need_resync) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !size || !need_resync) { + return KAA_ERR_BADPARAM; + } + + *need_resync = resync_is_required(context); + if (!*need_resync) { + *size = 0; + return KAA_ERR_NONE; + } + + size_t size_needed; + kaa_error_t error = kaa_profile_request_get_size(context, &size_needed); + if (error) { + return error; + } + + if (!buffer || *size < size_needed) { + *size = size_needed; + return KAA_ERR_BUFFER_IS_NOT_ENOUGH; + } + + *size = size_needed; + + kaa_platform_message_writer_t writer = KAA_MESSAGE_WRITER(buffer, *size); + error = kaa_profile_request_serialize(context, &writer); + if (error) { + return error; + } + + *size = writer.current - buffer; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_profile_server_sync(void *context, uint32_t request_id, + uint16_t extension_options, const uint8_t *buffer, size_t size) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !buffer) { + return KAA_ERR_BADPARAM; + } + + kaa_platform_message_reader_t reader = KAA_MESSAGE_READER(buffer, size); + return kaa_profile_handle_server_sync(context, &reader, extension_options, size); +} + +/* + * PUBLIC FUNCTIONS + */ +/** @deprecated Use kaa_extension_profile_init(). */ +kaa_error_t kaa_profile_manager_create(kaa_profile_manager_t **profile_manager_p, kaa_status_t *status + , kaa_channel_manager_t *channel_manager, kaa_logger_t *logger) +{ + if (!profile_manager_p || !channel_manager || !status) { + return KAA_ERR_BADPARAM; + } + + kaa_profile_manager_t *profile_manager = KAA_MALLOC(sizeof(kaa_profile_manager_t)); + if (!profile_manager) { + return KAA_ERR_NOMEM; + } + + /** + * KAA_CALLOC is really needed. + */ + profile_manager->extension_data = KAA_CALLOC(1, sizeof(kaa_profile_extension_data_t)); + if (!profile_manager->extension_data) { + KAA_FREE(profile_manager); + return KAA_ERR_NOMEM; + } + + profile_manager->need_resync = true; + + profile_manager->profile_body.size = 0; + profile_manager->profile_body.buffer = NULL; + profile_manager->channel_manager = channel_manager; + profile_manager->status = status; + profile_manager->logger = logger; + + ext_calculate_sha_hash(NULL, 0, profile_manager->profile_hash); + ext_copy_sha_hash(profile_manager->status->profile_hash, profile_manager->profile_hash); + + *profile_manager_p = profile_manager; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_profile_force_sync(kaa_profile_manager_t *self) +{ + if (!self) + return KAA_ERR_BADPARAM; + + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel( + self->channel_manager, KAA_EXTENSION_PROFILE); + if (!channel) { + return KAA_ERR_NOT_FOUND; + } + + channel->sync_handler(channel->context, profile_sync_services, 1); + return KAA_ERR_NONE; +} + + +bool kaa_profile_manager_is_profile_set(kaa_profile_manager_t *self) +{ +#if KAA_PROFILE_SCHEMA_VERSION > 0 + return self->profile_body.buffer != NULL && self->profile_body.size != 0; +#else + return true; +#endif +} + + + +/** @deprecated Use kaa_extension_profile_deinit(). */ +void kaa_profile_manager_destroy(kaa_profile_manager_t *self) +{ + if (self) { + if (self->profile_body.buffer && self->profile_body.size > 0) { + KAA_FREE(self->profile_body.buffer); + } + if (self->extension_data) { + if (self->extension_data->public_key.buffer && self->extension_data->public_key.destroy) { + self->extension_data->public_key.destroy(self->extension_data->public_key.buffer); + } + KAA_FREE(self->extension_data); + } + KAA_FREE(self); + } +} + + + +kaa_error_t kaa_profile_need_profile_resync(kaa_profile_manager_t *self, bool *result) +{ + KAA_RETURN_IF_NIL2(self, result, KAA_ERR_BADPARAM); + *result = resync_is_required(self); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_profile_request_get_size(kaa_profile_manager_t *self, size_t *expected_size) +{ + // TODO(KAA-982): Use asserts + if (!self || !expected_size) { + return KAA_ERR_BADPARAM; + } + + if (!resync_is_required(self)) { + *expected_size = 0; + return KAA_ERR_NONE; + } + + *expected_size = KAA_EXTENSION_HEADER_SIZE; + *expected_size += sizeof(uint32_t); // profile body size +#if KAA_PROFILE_SCHEMA_VERSION > 0 + if (resync_is_required(self)) + *expected_size += kaa_aligned_size_get(self->profile_body.size); // profile data +#endif + + if (!self->status->is_registered) { + bool need_deallocation = false; + + if (!self->extension_data->public_key.buffer) { + ext_get_endpoint_public_key((char **)&self->extension_data->public_key.buffer + , (size_t *)&self->extension_data->public_key.size + , &need_deallocation); + } + + if (self->extension_data->public_key.buffer && self->extension_data->public_key.size > 0) { + *expected_size += sizeof(uint32_t); // public key size + *expected_size += kaa_aligned_size_get(self->extension_data->public_key.size); // public key + + if (need_deallocation) { + self->extension_data->public_key.destroy = kaa_data_destroy; + } + } else { + return KAA_ERR_BADDATA; + } + } + + self->extension_data->access_token.buffer = (uint8_t *) self->status->endpoint_access_token; + if (self->extension_data->access_token.buffer) { + self->extension_data->access_token.size = strlen((const char*)self->extension_data->access_token.buffer); + *expected_size += sizeof(uint32_t); // access token length + *expected_size += kaa_aligned_size_get(self->extension_data->access_token.size); // access token + } + self->extension_data->payload_size = *expected_size - KAA_EXTENSION_HEADER_SIZE; + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_profile_request_serialize(kaa_profile_manager_t *self, kaa_platform_message_writer_t *writer) +{ + KAA_RETURN_IF_NIL2(self, writer, KAA_ERR_BADPARAM); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to compile profile client sync"); + + kaa_error_t error_code = kaa_platform_message_write_extension_header(writer + , KAA_EXTENSION_PROFILE + , 0 + , self->extension_data->payload_size); + KAA_RETURN_IF_ERR(error_code); + + uint32_t network_order_32 = KAA_HTONL(0); +#if KAA_PROFILE_SCHEMA_VERSION > 0 + if (resync_is_required(self)) { + network_order_32 = KAA_HTONL(self->profile_body.size); + error_code = kaa_platform_message_write(writer, &network_order_32, sizeof(uint32_t)); + KAA_RETURN_IF_ERR(error_code); + if (self->profile_body.size) { + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Writing profile body (size %u)...", self->profile_body.size); + error_code = kaa_platform_message_write_aligned(writer, self->profile_body.buffer, self->profile_body.size); + if (error_code) { + KAA_LOG_ERROR(self->logger, error_code, "Failed to write profile body"); + return error_code; + } + } + } else { + error_code = kaa_platform_message_write(writer, &network_order_32, sizeof(uint32_t)); + KAA_RETURN_IF_ERR(error_code); + } +#else + error_code = kaa_platform_message_write(writer, &network_order_32, sizeof(uint32_t)); + KAA_RETURN_IF_ERR(error_code); +#endif + + uint16_t network_order_16 = 0; + uint16_t field_number_with_reserved = 0; + + if (!self->status->is_registered) { + field_number_with_reserved = KAA_HTONS(PUB_KEY_VALUE << 8); + error_code = kaa_platform_message_write(writer + , &field_number_with_reserved + , sizeof(uint16_t)); + KAA_RETURN_IF_ERR(error_code); + + network_order_16 = KAA_HTONS(self->extension_data->public_key.size); + error_code = kaa_platform_message_write(writer, &network_order_16, sizeof(uint16_t)); + KAA_RETURN_IF_ERR(error_code); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Writing public key (size %u)...", self->extension_data->public_key.size); + error_code = kaa_platform_message_write_aligned(writer + , (char*)self->extension_data->public_key.buffer + , self->extension_data->public_key.size); + if (error_code) { + KAA_LOG_ERROR(self->logger, error_code, "Failed to write public key"); + return error_code; + } + } + + if (self->extension_data->access_token.buffer) { + field_number_with_reserved = KAA_HTONS(ACCESS_TOKEN_VALUE << 8); + error_code = kaa_platform_message_write(writer + , &field_number_with_reserved + , sizeof(uint16_t)); + KAA_RETURN_IF_ERR(error_code); + + network_order_16 = KAA_HTONS(self->extension_data->access_token.size); + error_code = kaa_platform_message_write(writer, &network_order_16, sizeof(uint16_t)); + KAA_RETURN_IF_ERR(error_code); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Writing access token (size %u)...", self->extension_data->access_token.size); + error_code = kaa_platform_message_write_aligned(writer + , (char*)self->extension_data->access_token.buffer + , self->extension_data->access_token.size); + if (error_code) { + KAA_LOG_ERROR(self->logger, error_code, "Failed to write access token"); + return error_code; + } + } + + return error_code; +} + + + +kaa_error_t kaa_profile_handle_server_sync(kaa_profile_manager_t *self + , kaa_platform_message_reader_t *reader + , uint16_t extension_options + , size_t extension_length) +{ + // Only used for logging + (void)extension_options; + (void)extension_length; + KAA_RETURN_IF_NIL2(self, reader, KAA_ERR_BADPARAM); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received profile server sync: options %u, payload size %zu", extension_options, extension_length); + + kaa_error_t error_code = KAA_ERR_NONE; + + self->need_resync = false; + if (extension_options & KAA_PROFILE_RESYNC_OPTION) { + self->need_resync = true; + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to resync profile..."); + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, profile_sync_services[0]); + if (channel) + channel->sync_handler(channel->context, profile_sync_services, 1); + } + + + if (!self->status->is_registered) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Endpoint has been registered"); + self->status->is_registered = true; + } + + return error_code; +} + +kaa_error_t kaa_profile_manager_update_profile(kaa_profile_manager_t *self, kaa_profile_t *profile_body) +{ +#if KAA_PROFILE_SCHEMA_VERSION > 0 + KAA_RETURN_IF_NIL2(self, profile_body, KAA_ERR_BADPARAM); + + size_t serialized_profile_size = profile_body->get_size(profile_body); + if (!serialized_profile_size) { + KAA_LOG_ERROR(self->logger, KAA_ERR_BADDATA, + "Failed to update profile: serialize profile size is null. Maybe profile schema is empty"); + return KAA_ERR_BADDATA; + } + + char *serialized_profile = (char *) KAA_MALLOC(serialized_profile_size * sizeof(char)); + KAA_RETURN_IF_NIL(serialized_profile, KAA_ERR_NOMEM); + + avro_writer_t writer = avro_writer_memory(serialized_profile, serialized_profile_size); + if (!writer) { + KAA_FREE(serialized_profile); + return KAA_ERR_NOMEM; + } + profile_body->serialize(writer, profile_body); + avro_writer_free(writer); + + kaa_digest new_hash; + ext_calculate_sha_hash(serialized_profile, serialized_profile_size, new_hash); + + if (!memcmp(new_hash, self->status->profile_hash, SHA_1_DIGEST_LENGTH)) { + self->need_resync = false; + KAA_FREE(serialized_profile); + return KAA_ERR_NONE; + } + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Endpoint profile is updated"); + + if (ext_copy_sha_hash(self->status->profile_hash, new_hash)) { + KAA_FREE(serialized_profile); + return KAA_ERR_BAD_STATE; + } + + if (self->profile_body.size > 0) { + KAA_FREE(self->profile_body.buffer); + self->profile_body.buffer = NULL; + } + + self->profile_body.buffer = (uint8_t*)serialized_profile; + self->profile_body.size = serialized_profile_size; + + self->need_resync = true; + + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, profile_sync_services[0]); + if (channel) + channel->sync_handler(channel->context, profile_sync_services, 1); + +#endif + return KAA_ERR_NONE; +} + +kaa_error_t kaa_profile_manager_set_endpoint_access_token(kaa_profile_manager_t *self, const char *token) +{ + KAA_RETURN_IF_NIL2(self, token, KAA_ERR_BADPARAM); + return kaa_status_set_endpoint_access_token(self->status, token); +} + +kaa_error_t kaa_profile_manager_get_endpoint_id(kaa_profile_manager_t *self, kaa_endpoint_id_p result_id) +{ + KAA_RETURN_IF_NIL2(self, result_id, KAA_ERR_BADPARAM); + return ext_copy_sha_hash((kaa_digest_p) result_id, self->status->endpoint_public_key_hash); +} diff --git a/client/client-multi/client-c/src/extensions/profile/kaa_profile.h b/client/client-multi/client-c/src/extensions/profile/kaa_profile.h new file mode 100644 index 0000000000..554a90fae6 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/profile/kaa_profile.h @@ -0,0 +1,89 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file kaa_profile.h + * @brief Kaa profile reporting API + * + * Supplies API to report endpoint profile to Operations server. + */ + +#ifndef KAA_PROFILE_H_ +#define KAA_PROFILE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "kaa_error.h" +#include "kaa_common.h" +#include "gen/kaa_profile_definitions.h" + + + +#ifndef KAA_PROFILE_MANAGER_T +# define KAA_PROFILE_MANAGER_T + /** + * Private profile manager data structure + */ + typedef struct kaa_profile_manager_t kaa_profile_manager_t; +#endif + + + +/** + * @brief Updates user profile. + * + * After a new profile is set a sync request to Operations server will be sent. + * The profile must be set prior to the endpoint registration. + * + * @param[in] self Profile manager instance. + * @param[in] profile Filled in user-defined profile data structure. + * + * @return Error code. + */ +kaa_error_t kaa_profile_manager_update_profile(kaa_profile_manager_t *self, kaa_profile_t *profile); + + + +/** + * @brief Updates user's access token. + * + * @param[in] self Profile manager instance. + * @param[in] token New user access token. + * + * @return Error code. + */ +kaa_error_t kaa_profile_manager_set_endpoint_access_token(kaa_profile_manager_t *self, const char *token); + + + +/** + * @brief Retrieves the endpoint ID. + * + * @param[in] self Profile manager instance. + * @param[out] result_id The buffer of size @link KAA_ENDPOINT_ID_LENGTH @endlink where the result will be stored. + * + * @return Error code. + */ +kaa_error_t kaa_profile_manager_get_endpoint_id(kaa_profile_manager_t *self, kaa_endpoint_id_p result_id); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* KAA_PROFILE_H_ */ diff --git a/client/client-multi/client-c/src/extensions/profile/kaa_profile_private.h b/client/client-multi/client-c/src/extensions/profile/kaa_profile_private.h new file mode 100644 index 0000000000..f0db1156a4 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/profile/kaa_profile_private.h @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef KAA_PROFILE_PRIVATE_H +#define KAA_PROFILE_PRIVATE_H + +#include +#include +#include +#include +#include + +kaa_error_t kaa_profile_manager_create(kaa_profile_manager_t **profile_manager_p, kaa_status_t *status, + kaa_channel_manager_t *channel_manager, kaa_logger_t *logger); +void kaa_profile_manager_destroy(kaa_profile_manager_t *self); +bool kaa_profile_manager_is_profile_set(kaa_profile_manager_t *self); + +kaa_error_t kaa_profile_need_profile_resync(kaa_profile_manager_t *kaa_context, bool *result); + +kaa_error_t kaa_profile_force_sync(kaa_profile_manager_t *self); +kaa_error_t kaa_profile_request_get_size(kaa_profile_manager_t *self, size_t *expected_size); +kaa_error_t kaa_profile_request_serialize(kaa_profile_manager_t *self, + kaa_platform_message_writer_t* writer); +kaa_error_t kaa_profile_handle_server_sync(kaa_profile_manager_t *self, kaa_platform_message_reader_t *reader, uint16_t extension_options, size_t extension_length); + +#endif /* KAA_PROFILE_PRIVATE_H */ diff --git a/client/client-multi/client-c/src/extensions/profile/test/test_kaa_profile.c b/client/client-multi/client-c/src/extensions/profile/test/test_kaa_profile.c new file mode 100644 index 0000000000..25bae9de86 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/profile/test/test_kaa_profile.c @@ -0,0 +1,432 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "kaa_profile.h" +#include "kaa_profile_private.h" + +#include "kaa_status.h" +#include "kaa_test.h" + +#include "kaa_context.h" +#include "kaa_status.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include "kaa_context.h" +#include "kaa_defaults.h" +#include "gen/kaa_profile_gen.h" +#include "kaa_platform_utils.h" +#include "platform/ext_status.h" +#include "platform/ext_sha.h" +#include "platform/ext_key_utils.h" +#include "platform/sock.h" +#include "platform/ext_transport_channel.h" +#include "kaa_channel_manager.h" + +#include "kaa_private.h" + +static kaa_context_t kaa_context; +static kaa_logger_t *logger = NULL; +static kaa_status_t *status = NULL; +static kaa_channel_manager_t *channel_manager = NULL; +static kaa_profile_manager_t *profile_manager = NULL; + + +#define TEST_PUB_KEY_SIZE 20 +static const uint8_t test_ep_key[TEST_PUB_KEY_SIZE] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 0x14}; + + + +void kaa_read_status_ext(char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ + (void)buffer; + (void)buffer_size; + (void)needs_deallocation; +} + +void kaa_store_status_ext(const char *buffer, size_t buffer_size) +{ + (void)buffer; + (void)buffer_size; +} + +void kaa_get_endpoint_public_key(char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ + *buffer = (char *) KAA_MALLOC(TEST_PUB_KEY_SIZE * sizeof(char)); + if (*buffer) { + memcpy(*buffer, test_ep_key, TEST_PUB_KEY_SIZE); + *buffer_size = TEST_PUB_KEY_SIZE; + *needs_deallocation = true; + } else { + *buffer_size = 0; + *needs_deallocation = false; + } +} + + +/*----------------------------------------------------------------------------*/ +/* Mock transport channel */ + +/* Flag to check that sync handler is actually called */ +static int mock_sync_handler_called; + +static kaa_error_t init_channel(void *ctx, kaa_transport_context_t *tctx) +{ + (void)ctx; + (void)tctx; + return KAA_ERR_NONE; +} +static kaa_error_t set_access_point(void *ctx, kaa_access_point_t *ap) +{ + (void)ctx; + (void)ap; + return KAA_ERR_NONE; +} +static kaa_error_t get_protocol_id(void *ctx, kaa_transport_protocol_id_t *id) +{ + (void)ctx; + id->id = 0; + id->version = 0; + return KAA_ERR_NONE; +} +static kaa_error_t get_services(void *ctx, + const kaa_extension_id **supported_list, + size_t *count) +{ + (void)ctx; + /* Only profile service is "supported" by this mock */ + static const kaa_extension_id services[] = { KAA_EXTENSION_PROFILE }; + *supported_list = services; + *count = 1; + return KAA_ERR_NONE; +} + +static kaa_error_t sync_handler(void *ctx, + const kaa_extension_id services[], + size_t count) +{ + (void)ctx; + + ASSERT_EQUAL(1, count); + ASSERT_EQUAL(KAA_EXTENSION_PROFILE, services[0]); + + mock_sync_handler_called = 1; + + return KAA_ERR_NONE; +} + +static kaa_transport_channel_interface_t channel = { + .context = NULL, + .destroy = NULL, + .sync_handler = sync_handler, + .init = init_channel, + .set_access_point = set_access_point, + .get_protocol_id = get_protocol_id, + .get_supported_services = get_services, +}; + +/*----------------------------------------------------------------------------*/ + +void test_profile_is_set(void **state) +{ + (void)state; + +#if KAA_PROFILE_SCHEMA_VERSION > 0 + ASSERT_FALSE(kaa_profile_manager_is_profile_set(profile_manager)); + kaa_profile_t *profile = kaa_profile_basic_endpoint_profile_test_create(); + profile->profile_body = kaa_string_copy_create("test"); + kaa_error_t error = kaa_profile_manager_update_profile(profile_manager, profile); + profile->destroy(profile); + ASSERT_EQUAL(error, KAA_ERR_NONE); + ASSERT_TRUE(kaa_profile_manager_is_profile_set(profile_manager)); +#else + ASSERT_TRUE(kaa_profile_manager_is_profile_set(profile_manager)); +#endif +} + +void test_profile_update(void **state) +{ + (void)state; + + kaa_profile_t *profile1 = kaa_profile_basic_endpoint_profile_test_create(); + profile1->profile_body = kaa_string_copy_create("dummy"); + kaa_error_t error = kaa_profile_manager_update_profile(profile_manager, profile1); + ASSERT_EQUAL(error, KAA_ERR_NONE); + + bool need_resync = false; + error = kaa_profile_need_profile_resync(profile_manager, &need_resync); + ASSERT_EQUAL(error, KAA_ERR_NONE); + ASSERT_TRUE(need_resync); + + error = kaa_profile_manager_update_profile(profile_manager, profile1); + ASSERT_EQUAL(error, KAA_ERR_NONE); + + error = kaa_profile_need_profile_resync(profile_manager, &need_resync); + ASSERT_EQUAL(error, KAA_ERR_NONE); + ASSERT_FALSE(need_resync); + + profile1->destroy(profile1); + + kaa_profile_t *profile2 = kaa_profile_basic_endpoint_profile_test_create(); + profile2->profile_body = kaa_string_copy_create("new_dummy"); + error = kaa_profile_manager_update_profile(profile_manager, profile2); + ASSERT_EQUAL(error, KAA_ERR_NONE); + + error = kaa_profile_need_profile_resync(profile_manager, &need_resync); + ASSERT_EQUAL(error, KAA_ERR_NONE); + ASSERT_TRUE(need_resync); + + profile2->destroy(profile2); +} + +void test_profile_sync_get_size(void **state) +{ + (void)state; + + kaa_profile_t *profile = kaa_profile_basic_endpoint_profile_test_create(); + profile->profile_body = kaa_string_copy_create("dummy"); + + size_t serialized_profile_size = profile->get_size(profile); + char *serialized_profile = (char *) KAA_MALLOC(serialized_profile_size * sizeof(char)); + avro_writer_t writer = avro_writer_memory(serialized_profile, serialized_profile_size); + profile->serialize(writer, profile); + + size_t expected_size = KAA_EXTENSION_HEADER_SIZE + + sizeof(uint32_t) // profile size + + kaa_aligned_size_get(serialized_profile_size); + + size_t profile_sync_size = 0; + + kaa_error_t error_code = kaa_profile_manager_update_profile(profile_manager, profile); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + status->is_registered = true; + + error_code = kaa_profile_request_get_size(profile_manager, &profile_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_EQUAL(expected_size, profile_sync_size); + + status->is_registered = false; + + expected_size += sizeof(uint32_t) + + TEST_PUB_KEY_SIZE; + + error_code = kaa_profile_request_get_size(profile_manager, &profile_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_EQUAL(expected_size, profile_sync_size); + + const char *access_token = "access token"; + error_code = kaa_profile_manager_set_endpoint_access_token(profile_manager, access_token); + assert_int_equal(KAA_ERR_NONE, error_code); + + expected_size += sizeof(uint32_t) + + strlen(access_token); + + error_code = kaa_profile_request_get_size(profile_manager, &profile_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_EQUAL(expected_size, profile_sync_size); + + avro_writer_free(writer); + KAA_FREE(serialized_profile); + profile->destroy(profile); +} + +void test_profile_sync_serialize(void **state) +{ + (void)state; + + kaa_error_t error_code; + kaa_platform_message_writer_t *manual_writer; + kaa_platform_message_writer_t *auto_writer; + + const char *access_token = "access token"; + const size_t access_token_size = strlen(access_token); + kaa_profile_t *profile = kaa_profile_basic_endpoint_profile_test_create(); + profile->profile_body = kaa_string_copy_create("dummy"); + size_t serialized_profile_size = profile->get_size(profile); + char *serialized_profile = (char *) KAA_MALLOC(serialized_profile_size * sizeof(char)); + avro_writer_t avro_writer = avro_writer_memory(serialized_profile, serialized_profile_size); + + profile->serialize(avro_writer, profile); + + error_code = kaa_profile_manager_update_profile(profile_manager, profile); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + status->is_registered = false; + error_code = kaa_profile_manager_set_endpoint_access_token(profile_manager, access_token); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + size_t profile_sync_size; + error_code = kaa_profile_request_get_size(profile_manager, &profile_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint8_t buffer[profile_sync_size]; + error_code = kaa_platform_message_writer_create(&manual_writer, buffer, profile_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint32_t network_order_32; + + error_code = kaa_platform_message_write_extension_header(manual_writer + , KAA_EXTENSION_PROFILE + , 0 + , profile_sync_size - KAA_EXTENSION_HEADER_SIZE); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + bool need_resync = true; + ASSERT_EQUAL(kaa_profile_need_profile_resync(profile_manager, &need_resync), KAA_ERR_NONE); + + network_order_32 = KAA_HTONL(0); + if (need_resync) + network_order_32 = KAA_HTONL(serialized_profile_size); + error_code = kaa_platform_message_write(manual_writer, &network_order_32, sizeof(uint32_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + if (need_resync) { + error_code = kaa_platform_message_write_aligned(manual_writer, serialized_profile, serialized_profile_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + } + + network_order_32 = KAA_HTONS(TEST_PUB_KEY_SIZE) << 16 | PUB_KEY_VALUE; + error_code = kaa_platform_message_write(manual_writer, &network_order_32, sizeof(uint32_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_platform_message_write_aligned(manual_writer, test_ep_key, TEST_PUB_KEY_SIZE); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + network_order_32 = KAA_HTONS(access_token_size) << 16 | ACCESS_TOKEN_VALUE; + error_code = kaa_platform_message_write(manual_writer, &network_order_32, sizeof(uint32_t)); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_platform_message_write_aligned(manual_writer, access_token, access_token_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + uint8_t buffer2[profile_sync_size]; + error_code = kaa_platform_message_writer_create(&auto_writer, buffer2, profile_sync_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_profile_request_serialize(profile_manager, auto_writer); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = (memcmp(buffer, buffer2, profile_sync_size) == 0 ? KAA_ERR_NONE : KAA_ERR_BADDATA); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + KAA_FREE(serialized_profile); + avro_writer_free(avro_writer); + profile->destroy(profile); + kaa_platform_message_writer_destroy(auto_writer); + kaa_platform_message_writer_destroy(manual_writer); +} + +void test_profile_handle_sync(void **state) +{ + (void)state; + + bool need_resync = false; + uint16_t extension_options = 0x1; /* Need resync */ + + const size_t buffer_size = 6; + uint8_t buffer[buffer_size]; + kaa_platform_message_reader_t *reader; + kaa_error_t error_code = kaa_platform_message_reader_create(&reader, buffer, buffer_size); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + + error_code = kaa_profile_handle_server_sync(profile_manager, reader, extension_options, 0); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + error_code = kaa_profile_need_profile_resync(profile_manager, &need_resync); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_TRUE(need_resync); + + extension_options = 0x0; /* Need resync */ + error_code = kaa_profile_handle_server_sync(profile_manager, reader, extension_options, 0); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + error_code = kaa_profile_need_profile_resync(profile_manager, &need_resync); + ASSERT_EQUAL(error_code, KAA_ERR_NONE); + ASSERT_FALSE(need_resync); + + kaa_platform_message_reader_destroy(reader); +} + +static void test_profile_force_sync(void **state) +{ + (void)state; + kaa_error_t rc = kaa_profile_force_sync(profile_manager); + ASSERT_EQUAL(KAA_ERR_NONE, rc); + + ASSERT_TRUE(mock_sync_handler_called); +} + +int test_init(void **state) +{ + (void)state; + + kaa_error_t error = kaa_log_create(&logger, + KAA_MAX_LOG_MESSAGE_LENGTH, + KAA_MAX_LOG_LEVEL, + NULL); + if (error || !logger) { + return error; + } + + kaa_context.logger = logger; + + error = kaa_status_create(&status); + if (error || !status) { + return error; + } + + error = kaa_channel_manager_create(&channel_manager, &kaa_context); + if (error || !channel_manager) { + return error; + } + + /* Add channel will fail due to absent access point, but it is expected */ + kaa_channel_manager_add_transport_channel(channel_manager, &channel, NULL); + + error = kaa_profile_manager_create(&profile_manager, + status, channel_manager, logger); + if (error || !profile_manager) { + return error; + } + + return 0; +} + +int test_deinit(void **state) +{ + (void)state; + + kaa_profile_manager_destroy(profile_manager); + kaa_channel_manager_destroy(channel_manager); + kaa_status_destroy(status); + kaa_log_destroy(logger); + return 0; +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_profile_is_set, test_init, test_deinit), + cmocka_unit_test_setup_teardown(test_profile_update, test_init, test_deinit), + cmocka_unit_test_setup_teardown(test_profile_sync_get_size, test_init, test_deinit), + cmocka_unit_test_setup_teardown(test_profile_sync_serialize, test_init, test_deinit), + cmocka_unit_test_setup_teardown(test_profile_handle_sync, test_init, test_deinit), + cmocka_unit_test_setup_teardown(test_profile_force_sync, test_init, test_deinit), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/client/client-multi/client-c/src/extensions/user/CMakeLists.txt b/client/client-multi/client-c/src/extensions/user/CMakeLists.txt new file mode 100644 index 0000000000..db462a6475 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/user/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set(EXTENSION_USER_SOURCE_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_user.c) + +set(EXTENSION_USER_HEADER_FILES + ${CMAKE_CURRENT_LIST_DIR}/kaa_user.h) + +add_library(extension_user ${EXTENSION_USER_SOURCE_FILES}) +target_include_directories(extension_user PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(extension_user PUBLIC kaac) + +kaa_add_unit_test(NAME test_user_extension + SOURCES + ${CMAKE_CURRENT_LIST_DIR}/test/test_kaa_user.c + test/kaa_test_external.c + DEPENDS + kaac ${OPENSSL_LIBRARIES}) diff --git a/client/client-multi/client-c/src/extensions/user/kaa_user.c b/client/client-multi/client-c/src/extensions/user/kaa_user.c new file mode 100644 index 0000000000..972b614c3a --- /dev/null +++ b/client/client-multi/client-c/src/extensions/user/kaa_user.c @@ -0,0 +1,767 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kaa_private.h" + +#include "kaa_user.h" +#include "kaa_user_private.h" + +#include +#include +#include "platform/stdio.h" +#include "platform/sock.h" +#include "kaa_defaults.h" +#include "platform/ext_sha.h" +#include "kaa_status.h" +#include "kaa_channel_manager.h" +#include "kaa_platform_common.h" +#include "kaa_platform_utils.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include "collections/kaa_list.h" +#include "kaa_common.h" + + + +#define KAA_USER_RECEIVE_UPDATES_FLAG 0x01 + +#define EXTERNAL_SYSTEM_AUTH_FIELD 0x00 +#define EXTERNAL_SYSTEM_ENDPOINT_ATTACH_FIELD 0x01 +#define EXTERNAL_SYSTEM_ENDPOINT_DETACH_FIELD 0x02 + +#define USER_SYNC_ENDPOINT_ID_OPTION 0x01 + + +typedef struct { + char *user_external_id; + size_t user_external_id_len; + char *user_access_token; + size_t user_access_token_len; + char *user_verifier_token; + size_t user_verifier_token_len; +} user_info_t; + +typedef struct { + uint16_t request_id; + char *access_token; + size_t access_token_length; + kaa_endpoint_id endpoint_token; + bool is_waiting_response; + kaa_endpoint_status_listener_t *listener; +} kaa_endpoint_info_t; + +struct kaa_user_manager_t { + kaa_attachment_status_listeners_t attachment_listeners; /*!< Client code-defined user attachment listeners */ + user_info_t *user_info; /*!< User credentials */ + bool is_waiting_user_attach_response; + kaa_list_t *attach_endpoints; /*!< Endpoints attach list*/ + kaa_list_t *detach_endpoints; /*!< Endpoints detach list */ + uint16_t endpoint_request_counter; /*!< Endpoints counter of request id*/ + kaa_status_t *status; /*!< Reference to global status */ + kaa_channel_manager_t *channel_manager; /*!< Reference to global channel manager */ + kaa_logger_t *logger; +}; + +typedef enum { + USER_RESULT_SUCCESS = 0x00, + USER_RESULT_FAILURE = 0x01 +} user_sync_result_t; + +typedef enum { + USER_ATTACH_RESPONSE_FIELD = 0, + USER_ATTACH_NOTIFICATION_FIELD = 1, + USER_DETACH_NOTIFICATION_FIELD = 2, + ENDPOINT_ATTACH_RESPONSES_FIELD = 3, + ENDPOINT_DETACH_RESPONSES_FIELD = 4 +} user_server_sync_field_t; + + + +static kaa_extension_id user_sync_services[1] = {KAA_EXTENSION_USER}; + +kaa_error_t kaa_extension_user_init(kaa_context_t *kaa_context, void **context) +{ + kaa_error_t error = kaa_user_manager_create(&kaa_context->user_manager, kaa_context->status->status_instance, + kaa_context->channel_manager, kaa_context->logger); + *context = kaa_context->user_manager; + return error; +} + +kaa_error_t kaa_extension_user_deinit(void *context) +{ + kaa_user_manager_destroy(context); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_user_request_get_size(void *context, size_t *expected_size) +{ + return kaa_user_request_get_size(context, expected_size); +} + +kaa_error_t kaa_extension_user_request_serialize(void *context, uint32_t request_id, + uint8_t *buffer, size_t *size, bool *need_resync) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !size || !need_resync) { + return KAA_ERR_BADPARAM; + } + + *need_resync = true; + + size_t size_needed; + kaa_error_t error = kaa_user_request_get_size(context, &size_needed); + if (error) { + return error; + } + + if (!buffer || *size < size_needed) { + *size = size_needed; + return KAA_ERR_BUFFER_IS_NOT_ENOUGH; + } + + *size = size_needed; + + kaa_platform_message_writer_t writer = KAA_MESSAGE_WRITER(buffer, *size); + error = kaa_user_request_serialize(context, &writer); + if (error) { + return error; + } + + *size = writer.current - buffer; + return KAA_ERR_NONE; +} + +kaa_error_t kaa_extension_user_server_sync(void *context, uint32_t request_id, + uint16_t extension_options, const uint8_t *buffer, size_t size) +{ + (void)request_id; + + // TODO(KAA-982): Use asserts + if (!context || !buffer) { + return KAA_ERR_BADPARAM; + } + + kaa_platform_message_reader_t reader = KAA_MESSAGE_READER(buffer, size); + return kaa_user_handle_server_sync(context, &reader, extension_options, size); +} + +static void dtor_endpoint_info(void *data) +{ + KAA_RETURN_IF_NIL(data, ); + kaa_endpoint_info_t *endpoint_info = (kaa_endpoint_info_t*)data; + if(endpoint_info->access_token) + KAA_FREE(endpoint_info->access_token); + KAA_FREE(endpoint_info); +} + +static bool match_predicate_endpoint_info(void *data, void *context) +{ + KAA_RETURN_IF_NIL2(data, context, false); + + kaa_endpoint_info_t *endpoint_item = (kaa_endpoint_info_t*)data; + uint16_t request_id = *(uint16_t*)context; + + return request_id == endpoint_item->request_id; +} + +static void destroy_user_info(user_info_t *user_info) +{ + KAA_RETURN_IF_NIL(user_info, ); + + if (user_info->user_external_id) + KAA_FREE(user_info->user_external_id); + if (user_info->user_access_token) + KAA_FREE(user_info->user_access_token); + if (user_info->user_verifier_token) + KAA_FREE(user_info->user_verifier_token); + + KAA_FREE(user_info); +} + +static user_info_t *create_user_info(const char *external_id, const char *user_access_token, const char *user_verifier_token) +{ + KAA_RETURN_IF_NIL3(external_id, user_access_token, user_verifier_token, NULL); + + user_info_t *user_info = (user_info_t *) KAA_CALLOC(1, sizeof(user_info_t)); + KAA_RETURN_IF_NIL(user_info, NULL); + + user_info->user_external_id_len = strlen(external_id); + user_info->user_access_token_len = strlen(user_access_token); + user_info->user_verifier_token_len = strlen(user_verifier_token); + + user_info->user_external_id = (char *) KAA_MALLOC((user_info->user_external_id_len + 1) * sizeof(char)); + if (!user_info->user_external_id) { + destroy_user_info(user_info); + return NULL; + } + strcpy(user_info->user_external_id, external_id); + + user_info->user_access_token = (char *) KAA_MALLOC((user_info->user_access_token_len + 1) * sizeof(char)); + if (!user_info->user_access_token) { + destroy_user_info(user_info); + return NULL; + } + strcpy(user_info->user_access_token, user_access_token); + + user_info->user_verifier_token = (char *) KAA_MALLOC((user_info->user_verifier_token_len + 1) * sizeof(char)); + if (!user_info->user_verifier_token) { + destroy_user_info(user_info); + return NULL; + } + strcpy(user_info->user_verifier_token, user_verifier_token); + + return user_info; +} + +/** @deprecated Use kaa_extension_user_init(). */ +kaa_error_t kaa_user_manager_create(kaa_user_manager_t **user_manager_p + , kaa_status_t *status + , kaa_channel_manager_t *channel_manager + , kaa_logger_t *logger) +{ + KAA_RETURN_IF_NIL2(user_manager_p, status, KAA_ERR_BADPARAM); + + *user_manager_p = (kaa_user_manager_t *) KAA_MALLOC(sizeof(kaa_user_manager_t)); + KAA_RETURN_IF_NIL((*user_manager_p), KAA_ERR_NOMEM); + + (*user_manager_p)->attachment_listeners.on_attached = NULL; + (*user_manager_p)->attachment_listeners.on_detached = NULL; + (*user_manager_p)->attachment_listeners.on_attach_success = NULL; + (*user_manager_p)->attachment_listeners.on_attach_failed = NULL; + (*user_manager_p)->user_info = NULL; + (*user_manager_p)->is_waiting_user_attach_response = false; + (*user_manager_p)->attach_endpoints = kaa_list_create(); + (*user_manager_p)->detach_endpoints = kaa_list_create(); + (*user_manager_p)->endpoint_request_counter = 0; + (*user_manager_p)->status = status; + (*user_manager_p)->channel_manager = channel_manager; + (*user_manager_p)->logger = logger; + + return KAA_ERR_NONE; +} + +/** @deprecated Use kaa_extension_user_deinit(). */ +void kaa_user_manager_destroy(kaa_user_manager_t *self) +{ + if (self) { + kaa_list_destroy(self->attach_endpoints, dtor_endpoint_info); + kaa_list_destroy(self->detach_endpoints, dtor_endpoint_info); + destroy_user_info(self->user_info); + KAA_FREE(self); + } +} + +bool kaa_user_manager_is_attached_to_user(kaa_user_manager_t *self) +{ + KAA_RETURN_IF_NIL2(self, self->status, false); + return self->status->is_attached; +} + +kaa_error_t kaa_user_manager_attach_to_user(kaa_user_manager_t *self + , const char *user_external_id + , const char *access_token + , const char *user_verifier_token) +{ + KAA_RETURN_IF_NIL3(self, user_external_id, access_token, KAA_ERR_BADPARAM); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to attach to user " + "(external id = \"%s\", access token = \"%s\", verifier token = \"%s\")" + , user_external_id, access_token, user_verifier_token); + + if (self->is_waiting_user_attach_response) { + destroy_user_info(self->user_info); + self->user_info = NULL; + self->is_waiting_user_attach_response = false; + } + + self->user_info = create_user_info(user_external_id, access_token, user_verifier_token); + if (!self->user_info) + return KAA_ERR_NOMEM; + + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, user_sync_services[0]); + if (channel) + channel->sync_handler(channel->context, user_sync_services, 1); + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_user_manager_attach_endpoint(kaa_user_manager_t *self, const char *endpoint_access_token, kaa_endpoint_status_listener_t *listener) +{ + KAA_RETURN_IF_NIL2(self, endpoint_access_token, KAA_ERR_BADPARAM); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to attach endpoint by access token " + "(endpoint_access_token = \"%s\")" + , endpoint_access_token); + + kaa_endpoint_info_t *info = KAA_CALLOC(1, sizeof(kaa_endpoint_info_t)); + KAA_RETURN_IF_NIL(info, KAA_ERR_NOMEM); + + + info->access_token_length = strlen(endpoint_access_token); + info->access_token = KAA_MALLOC(info->access_token_length); + + if (!info->access_token) { + dtor_endpoint_info((void*)info); + return KAA_ERR_NOMEM; + } + + memcpy(info->access_token, endpoint_access_token, info->access_token_length); + + if (listener) + info->listener = listener; + + info->request_id = ++self->endpoint_request_counter; + + if (!kaa_list_push_back(self->attach_endpoints, (void*)info)) { + dtor_endpoint_info((void*)info); + return KAA_ERR_NOMEM; + } + + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, user_sync_services[0]); + if (channel) + channel->sync_handler(channel->context, user_sync_services, 1); + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_user_manager_detach_endpoint(kaa_user_manager_t *self, const kaa_endpoint_id_p endpoint_hash_key, kaa_endpoint_status_listener_t *listener) +{ + KAA_RETURN_IF_NIL2(self, endpoint_hash_key, KAA_ERR_BADPARAM); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Going to detach endpoint"); + + + kaa_endpoint_info_t *info = KAA_CALLOC(1, sizeof(kaa_endpoint_info_t)); + KAA_RETURN_IF_NIL(info, KAA_ERR_NOMEM); + + memcpy(info->endpoint_token, endpoint_hash_key, KAA_ENDPOINT_ID_LENGTH); + + if (listener) + info->listener = listener; + + info->request_id = ++self->endpoint_request_counter; + + if (!kaa_list_push_back(self->detach_endpoints, (void*)info)) { + dtor_endpoint_info((void*)info); + return KAA_ERR_NOMEM; + } + + kaa_transport_channel_interface_t *channel = + kaa_channel_manager_get_transport_channel(self->channel_manager, user_sync_services[0]); + if (channel) + channel->sync_handler(channel->context, user_sync_services, 1); + + + return KAA_ERR_NONE; +} + +#ifdef DEFAULT_USER_VERIFIER_TOKEN +kaa_error_t kaa_user_manager_default_attach_to_user(kaa_user_manager_t *self + , const char *user_external_id + , const char *access_token) +{ + KAA_RETURN_IF_NIL3(self, user_external_id, access_token, KAA_ERR_BADPARAM); + return kaa_user_manager_attach_to_user(self, user_external_id, access_token, DEFAULT_USER_VERIFIER_TOKEN); +} +#endif + +kaa_error_t kaa_user_manager_set_attachment_listeners(kaa_user_manager_t *self + , const kaa_attachment_status_listeners_t *listeners) +{ + KAA_RETURN_IF_NIL(self, KAA_ERR_BADPARAM); + self->attachment_listeners = *listeners; + return KAA_ERR_NONE; +} + +static size_t kaa_user_request_get_size_no_header(kaa_user_manager_t *self) +{ + size_t expected_size = 0; + if (self->user_info && !self->is_waiting_user_attach_response) { + expected_size += sizeof(uint32_t) // field id + user external ID length + user access token length + + sizeof(uint32_t); // verifier id length + reserved + expected_size += kaa_aligned_size_get(self->user_info->user_external_id_len); + expected_size += kaa_aligned_size_get(self->user_info->user_access_token_len); + expected_size += kaa_aligned_size_get(self->user_info->user_verifier_token_len); + } + + if (kaa_list_get_size(self->attach_endpoints)) { + + expected_size += sizeof(uint32_t); // field id + reserved + endpoint attach requests count + + kaa_list_node_t *node = kaa_list_begin(self->attach_endpoints); + while (node) { + kaa_endpoint_info_t *info = (kaa_endpoint_info_t*)kaa_list_get_data(node); + expected_size += sizeof(uint32_t) + kaa_aligned_size_get(info->access_token_length); //request id + endpoint access token length + node = kaa_list_next(node); + } + } + + if (kaa_list_get_size(self->detach_endpoints)) { + //field id + reserved + endpoint detach requests count + expected_size += sizeof(uint32_t) + kaa_list_get_size(self->detach_endpoints) * (sizeof(uint32_t)/*request id + reserved*/ + KAA_ENDPOINT_ID_LENGTH); + } + + return expected_size; +} + +kaa_error_t kaa_user_request_get_size(kaa_user_manager_t *self, size_t *expected_size) +{ + KAA_RETURN_IF_NIL2(self, expected_size, KAA_ERR_BADPARAM); + *expected_size = KAA_EXTENSION_HEADER_SIZE + kaa_user_request_get_size_no_header(self); + return KAA_ERR_NONE; +} + +kaa_error_t kaa_user_request_serialize(kaa_user_manager_t *self, kaa_platform_message_writer_t* writer) +{ + KAA_RETURN_IF_NIL2(self, writer, KAA_ERR_BADPARAM); + + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Going to serialize client user sync"); + + size_t size = kaa_user_request_get_size_no_header(self); + if (kaa_platform_message_write_extension_header(writer, KAA_EXTENSION_USER, KAA_USER_RECEIVE_UPDATES_FLAG, size)) { + KAA_LOG_ERROR(self->logger, KAA_ERR_WRITE_FAILED, "Failed to write the user extension header"); + return KAA_ERR_WRITE_FAILED; + } + + if (self->user_info && !self->is_waiting_user_attach_response) { + *(writer->current++) = EXTERNAL_SYSTEM_AUTH_FIELD; + *(writer->current++) = (uint8_t) self->user_info->user_external_id_len; + *((uint16_t *) writer->current) = KAA_HTONS((uint16_t) self->user_info->user_access_token_len); + writer->current += sizeof(uint16_t); + *((uint16_t *) writer->current) = KAA_HTONS((uint16_t) self->user_info->user_verifier_token_len); + writer->current += sizeof(uint32_t); /* verifier token length + reserved */ + KAA_LOG_TRACE(self->logger, KAA_ERR_NONE, "Serializing external system authentication parameters: user external id '%s', access token '%s', verifier token '%s'" + , self->user_info->user_external_id, self->user_info->user_access_token, self->user_info->user_verifier_token); + + if (self->user_info->user_external_id_len) { + if (kaa_platform_message_write_aligned(writer + , self->user_info->user_external_id + , self->user_info->user_external_id_len)) + { + KAA_LOG_ERROR(self->logger, KAA_ERR_WRITE_FAILED, "Failed to write the user external id \"%s\"" + , self->user_info->user_external_id); + return KAA_ERR_WRITE_FAILED; + } + } + if (self->user_info->user_access_token_len) { + if (kaa_platform_message_write_aligned(writer + , self->user_info->user_access_token + , self->user_info->user_access_token_len)) + { + KAA_LOG_ERROR(self->logger, KAA_ERR_WRITE_FAILED, "Failed to write the user access token \"%s\"" + , self->user_info->user_access_token); + return KAA_ERR_WRITE_FAILED; + } + } + if (self->user_info->user_verifier_token_len) { + if (kaa_platform_message_write_aligned(writer + , self->user_info->user_verifier_token + , self->user_info->user_verifier_token_len)) + { + KAA_LOG_ERROR(self->logger, KAA_ERR_WRITE_FAILED, "Failed to write the user verifier token \"%s\"" + , self->user_info->user_verifier_token); + return KAA_ERR_WRITE_FAILED; + } + } + + self->is_waiting_user_attach_response = true; + } + + if (kaa_list_get_size(self->attach_endpoints)) { + kaa_list_node_t *node = kaa_list_begin(self->attach_endpoints); + *(writer->current) = EXTERNAL_SYSTEM_ENDPOINT_ATTACH_FIELD; + writer->current += sizeof(uint16_t); + *((uint16_t*)writer->current) = KAA_HTONS((uint16_t)kaa_list_get_size(self->attach_endpoints)); + writer->current += sizeof(uint16_t); + + while (node) { + kaa_endpoint_info_t *info = (kaa_endpoint_info_t*)kaa_list_get_data(node); + + if (!info->is_waiting_response) { + *((uint16_t*)writer->current) = KAA_HTONS(info->request_id); + writer->current += sizeof(uint16_t); + *((uint16_t*)writer->current) = KAA_HTONS((uint16_t)info->access_token_length); + writer->current += sizeof(uint16_t); + if (kaa_platform_message_write_aligned(writer + , info->access_token + , info->access_token_length)) + { + KAA_LOG_ERROR(self->logger, KAA_ERR_WRITE_FAILED, "Failed to write the user Endpoint access token"); + return KAA_ERR_WRITE_FAILED; + } + + info->is_waiting_response = true; + } + + node = kaa_list_next(node); + } + } + + if (kaa_list_get_size(self->detach_endpoints)) { + kaa_list_node_t *node = kaa_list_begin(self->detach_endpoints); + *(writer->current) = EXTERNAL_SYSTEM_ENDPOINT_DETACH_FIELD; + writer->current += sizeof(uint16_t); + *((uint16_t*)writer->current) = KAA_HTONS((uint16_t)kaa_list_get_size(self->detach_endpoints)); + writer->current += sizeof(uint16_t); + + while (node) { + kaa_endpoint_info_t *info = (kaa_endpoint_info_t*)kaa_list_get_data(node); + + if (!info->is_waiting_response) { + *((uint16_t*)writer->current) = KAA_HTONS(info->request_id); + writer->current += sizeof(uint32_t); + + memcpy(writer->current, info->endpoint_token, KAA_ENDPOINT_ID_LENGTH); + writer->current += KAA_ENDPOINT_ID_LENGTH; + + info->is_waiting_response = true; + } + + node = kaa_list_next(node); + } + } + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_user_handle_server_sync(kaa_user_manager_t *self + , kaa_platform_message_reader_t *reader + , uint16_t extension_options + , size_t extension_length) +{ + // Only used for logging + (void)extension_options; + (void)extension_length; + KAA_RETURN_IF_NIL2(self, reader, KAA_ERR_BADPARAM); + + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received user server sync: options %u, payload size %u", extension_options, extension_length); + + size_t remaining_length = extension_length; + + while (remaining_length > 0) { + uint32_t field_header = KAA_NTOHL(*(uint32_t *)reader->current); + reader->current += sizeof(uint32_t); + remaining_length -= sizeof(uint32_t); + + user_server_sync_field_t field = (field_header >> 24) & 0xFF; + switch (field) { + case USER_ATTACH_RESPONSE_FIELD: { + user_sync_result_t result = (uint8_t) (field_header >> 8) & 0xFF; + destroy_user_info(self->user_info); + self->user_info = NULL; + self->is_waiting_user_attach_response = false; + + if (result == USER_RESULT_SUCCESS) { + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Endpoint was successfully attached to user"); + kaa_status_set_attached(self->status, true); + if (self->attachment_listeners.on_attach_success) + (self->attachment_listeners.on_attach_success)(self->attachment_listeners.context); + } else { + + uint16_t user_verifier_error_code; + if (kaa_platform_message_read(reader, &user_verifier_error_code, sizeof(uint16_t))) { + KAA_LOG_ERROR(self->logger, KAA_ERR_READ_FAILED, "Failed to read user verifier error code"); + return KAA_ERR_READ_FAILED; + } + + user_verifier_error_code = KAA_NTOHS(user_verifier_error_code); + remaining_length -= sizeof(uint16_t); + + uint16_t reason_length; + if (kaa_platform_message_read(reader, &reason_length, sizeof(uint16_t))) { + KAA_LOG_ERROR(self->logger, KAA_ERR_READ_FAILED, "Failed to read reason length"); + return KAA_ERR_READ_FAILED; + } + + reason_length = KAA_NTOHS(reason_length); + remaining_length -= sizeof(uint16_t); + + if (reason_length) { + char reason[reason_length + 1]; + if (kaa_platform_message_read_aligned(reader, reason, reason_length)) { + KAA_LOG_ERROR(self->logger, KAA_ERR_READ_FAILED, "Failed to read error reason"); + return KAA_ERR_READ_FAILED; + } + + reason[reason_length] = '\0'; + remaining_length -= kaa_aligned_size_get(reason_length); + + KAA_LOG_ERROR(self->logger, KAA_ERR_EVENT_NOT_ATTACHED, "Failed to attach to user: error %d, reason '%s'", user_verifier_error_code, reason); + + if (self->attachment_listeners.on_attach_failed) { + (self->attachment_listeners.on_attach_failed)(self->attachment_listeners.context + , (user_verifier_error_code_t)user_verifier_error_code + , reason); + } + } else { + KAA_LOG_ERROR(self->logger, KAA_ERR_EVENT_NOT_ATTACHED, "Failed to attach to user: error %d, reason 'unknown'", user_verifier_error_code); + if (self->attachment_listeners.on_attach_failed) { + (self->attachment_listeners.on_attach_failed)(self->attachment_listeners.context + , (user_verifier_error_code_t)user_verifier_error_code + , NULL); + } + + } + } + break; + } + case USER_ATTACH_NOTIFICATION_FIELD: { + uint8_t external_id_length = (field_header >> 16) & 0xFF; + uint16_t access_token_length = (field_header) & 0xFFFF; + if (external_id_length + access_token_length > remaining_length) + return KAA_ERR_INVALID_BUFFER_SIZE; + + char external_id[external_id_length + 1]; + char access_token[access_token_length + 1]; + + if (kaa_platform_message_read_aligned(reader, external_id, external_id_length)) { + KAA_LOG_ERROR(self->logger, KAA_ERR_READ_FAILED, "Failed to read the external ID (length %u)" + , external_id_length); + return KAA_ERR_READ_FAILED; + } + external_id[external_id_length] = '\0'; + remaining_length -= kaa_aligned_size_get(external_id_length); + + if (kaa_platform_message_read_aligned(reader, access_token, access_token_length)) { + KAA_LOG_ERROR(self->logger, KAA_ERR_READ_FAILED, "Failed to read the access token (length %u)" + , access_token_length); + return KAA_ERR_READ_FAILED; + } + access_token[access_token_length] = '\0'; + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received user attach notification: endpoint external id '%s' (length %u), access token '%s' (length %u)" + , external_id, external_id_length, access_token, access_token_length); + remaining_length -= kaa_aligned_size_get(access_token_length); + + kaa_status_set_attached(self->status, true); + + if (self->attachment_listeners.on_attached) + (self->attachment_listeners.on_attached)(self->attachment_listeners.context + , external_id, access_token); + break; + } + case USER_DETACH_NOTIFICATION_FIELD: { + uint16_t access_token_length = (field_header) & 0xFFFF; + if (access_token_length > remaining_length) + return KAA_ERR_INVALID_BUFFER_SIZE; + + char access_token[access_token_length + 1]; + if (kaa_platform_message_read_aligned(reader, access_token, access_token_length)) { + KAA_LOG_ERROR(self->logger, KAA_ERR_READ_FAILED, "Failed to read the access token (length %u)" + , access_token_length); + return KAA_ERR_READ_FAILED; + } + access_token[access_token_length] = '\0'; + KAA_LOG_INFO(self->logger, KAA_ERR_NONE, "Received user detach notification: endpoint access token '%s' (length %u)" + , access_token, access_token_length); + remaining_length -= kaa_aligned_size_get(access_token_length); + + kaa_status_set_attached(self->status, false); + + if (self->attachment_listeners.on_detached) + (self->attachment_listeners.on_detached)(self->attachment_listeners.context, access_token); + break; + } + case ENDPOINT_ATTACH_RESPONSES_FIELD: { + uint16_t attach_responses_count = (field_header) & 0xFFFF; + + if (sizeof(uint32_t) > remaining_length) { + return KAA_ERR_INVALID_BUFFER_SIZE; + } + + for (uint32_t i = 0; i < attach_responses_count; ++i) { + uint8_t result_code = *(reader->current++); + uint8_t options = *(reader->current++); + uint16_t request_id = KAA_NTOHS(*(uint16_t*)reader->current); + + reader->current += sizeof(uint16_t); + remaining_length -= sizeof(uint32_t); + + if (result_code == USER_RESULT_FAILURE) { + kaa_list_node_t *node = kaa_list_find_next(kaa_list_begin(self->attach_endpoints), match_predicate_endpoint_info, (void*)&request_id); + if (node) { + kaa_endpoint_info_t *info = (kaa_endpoint_info_t*)kaa_list_get_data(node); + if (info->listener && info->listener->on_attach_failed) + info->listener->on_attach_failed(info->listener->context); + kaa_list_remove_at(self->attach_endpoints, node, dtor_endpoint_info); + } + continue; + } + + if (options & USER_SYNC_ENDPOINT_ID_OPTION) { + kaa_endpoint_id endpoint_id; + memcpy(endpoint_id, reader->current, KAA_ENDPOINT_ID_LENGTH); + reader->current += KAA_ENDPOINT_ID_LENGTH; + remaining_length -= KAA_ENDPOINT_ID_LENGTH; + + kaa_list_node_t *node = kaa_list_find_next(kaa_list_begin(self->attach_endpoints), match_predicate_endpoint_info, (void*)&request_id); + if (node) { + kaa_endpoint_info_t *info = (kaa_endpoint_info_t*)kaa_list_get_data(node); + if (info->listener && info->listener->on_attached) + info->listener->on_attached(info->listener->context, endpoint_id); + kaa_list_remove_at(self->attach_endpoints, node, dtor_endpoint_info); + } + } + } + + break; + } + case ENDPOINT_DETACH_RESPONSES_FIELD: { + uint16_t detach_responses_count = field_header & 0xFFFF; + + if (sizeof(uint32_t) > remaining_length) { + return KAA_ERR_INVALID_BUFFER_SIZE; + } + + for (uint32_t i = 0; i < detach_responses_count; ++i) { + uint8_t result_code = *(reader->current++); + reader->current++; + uint16_t request_id = KAA_NTOHS(*(uint16_t*)reader->current); + reader->current += sizeof(uint16_t); + remaining_length -= sizeof(uint32_t); + + if (result_code == USER_RESULT_FAILURE) { + kaa_list_node_t *node = kaa_list_find_next(kaa_list_begin(self->detach_endpoints), match_predicate_endpoint_info, (void*)&request_id); + if (node) { + kaa_endpoint_info_t *info = (kaa_endpoint_info_t*)kaa_list_get_data(node); + if (info->listener && info->listener->on_detach_failed) + info->listener->on_detach_failed(info->listener->context); + kaa_list_remove_at(self->detach_endpoints, node, dtor_endpoint_info); + } + continue; + } + + kaa_list_node_t *node = kaa_list_find_next(kaa_list_begin(self->detach_endpoints), match_predicate_endpoint_info, (void*)&request_id); + if (node) { + kaa_endpoint_info_t *info = (kaa_endpoint_info_t*)kaa_list_get_data(node); + if (info->listener && info->listener->on_detached) + info->listener->on_detached(info->listener->context); + kaa_list_remove_at(self->detach_endpoints, node, dtor_endpoint_info); + } + } + + break; + } + default: + KAA_LOG_ERROR(self->logger, KAA_ERR_READ_FAILED, "Invalid field %u", field); + return KAA_ERR_READ_FAILED; + } + } + return KAA_ERR_NONE; +} diff --git a/client/client-multi/client-c/src/extensions/user/kaa_user.h b/client/client-multi/client-c/src/extensions/user/kaa_user.h new file mode 100644 index 0000000000..530d2d46c7 --- /dev/null +++ b/client/client-multi/client-c/src/extensions/user/kaa_user.h @@ -0,0 +1,136 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file kaa_user.h + * @brief Endpoint-to-user association management for Kaa C SDK. + * + * Manages endpoint's association with a user entity in Kaa. + */ + +#ifndef KAA_USER_H_ +#define KAA_USER_H_ + +#include + +#include "kaa_error.h" +#include "kaa_defaults.h" +#include "platform/ext_user_callback.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +/** + * @brief Kaa user manager structure. + */ +#ifndef KAA_USER_MANAGER_T +# define KAA_USER_MANAGER_T + typedef struct kaa_user_manager_t kaa_user_manager_t; +#endif + + + +/** + * @brief Attaches the endpoint to a user entity. The user verification is carried out by the default verifier. + * + * Use this function to request attachment of the endpoint to a user entity using the specified external authentication + * credentials. Only endpoints associated with the same user entity can exchange events. + * + * @param[in] self The user manager instance. + * @param[in] user_external_id Null-terminated string representing external user ID. + * @param[in] access_token Null-terminated string representing external access token. + * + * @return Error code. + */ +#ifdef DEFAULT_USER_VERIFIER_TOKEN +kaa_error_t kaa_user_manager_default_attach_to_user(kaa_user_manager_t *self + , const char *user_external_id + , const char *access_token); +#endif + + + +/** + * @brief Attaches the endpoint to a user entity. The user verification is carried out by the specified verifier. + * + * Use this function to request attachment of the endpoint to a user entity using the specified external authentication + * credentials. Only endpoints associated with the same user entity can exchange events. + * + * @param[in] self The user manager instance. + * @param[in] user_external_id Null-terminated string representing external user ID. + * @param[in] access_token Null-terminated string representing external access token. + * @param[in] user_verifier_token Null-terminated string representing user verifier token. + * + * @return Error code. + */ +kaa_error_t kaa_user_manager_attach_to_user(kaa_user_manager_t *self + , const char *user_external_id + , const char *access_token + , const char *user_verifier_token); + + + +/** + * @brief Checks if current endpoint is attached to user. + * + * @param[in] self The user manager instance. + * @retval true The endpoint is attached to user + * @retval false Otherwise + */ +bool kaa_user_manager_is_attached_to_user(kaa_user_manager_t *self); + + +/** + * @brief Sets callback functions to receive notifications when the endpoint gets attached or detached to (from) user. + * + * @param[in] self The user manager instance. + * @param[in] listeners A filled in @link kaa_attachment_status_listeners_t @endlink structure. + * + * @return Error code. + */ +kaa_error_t kaa_user_manager_set_attachment_listeners(kaa_user_manager_t *self + , const kaa_attachment_status_listeners_t *listeners); + +/** + * @brief Attaches external endpoint by its access token. + * + * @param[in] self The user manager instance. + * @param[in] endpoint_access_token Null-terminated string representing endpoint access token. + * @param[in] listener Status listener to set. + * + * @return Error code. + */ +kaa_error_t kaa_user_manager_attach_endpoint(kaa_user_manager_t *self, const char *endpoint_access_token, kaa_endpoint_status_listener_t *listener); + + +/** + * @brief Detaches external endpoint by its access token. + * + * @param[in] self The user manager instance. + * @param[in] endpoint_hash_key Unique endpoint id. + * @param[in] listener + * + * @return Error code. + */ +kaa_error_t kaa_user_manager_detach_endpoint(kaa_user_manager_t *self, const kaa_endpoint_id_p endpoint_hash_key, kaa_endpoint_status_listener_t *listener); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* KAA_USER_H_ */ diff --git a/client/client-multi/client-c/src/extensions/user/kaa_user_private.h b/client/client-multi/client-c/src/extensions/user/kaa_user_private.h new file mode 100644 index 0000000000..c4d376f4aa --- /dev/null +++ b/client/client-multi/client-c/src/extensions/user/kaa_user_private.h @@ -0,0 +1,35 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KAA_USER_PRIVATE_H +#define KAA_USER_PRIVATE_H + +#include +#include +#include +#include +#include + +kaa_error_t kaa_user_manager_create(kaa_user_manager_t **user_manager_p, kaa_status_t *status, + kaa_channel_manager_t *channel_manager, kaa_logger_t *logger); + +void kaa_user_manager_destroy(kaa_user_manager_t *user_manager); + +kaa_error_t kaa_user_request_get_size(kaa_user_manager_t *self, size_t *expected_size); +kaa_error_t kaa_user_request_serialize(kaa_user_manager_t *self, kaa_platform_message_writer_t* writer); +kaa_error_t kaa_user_handle_server_sync(kaa_user_manager_t *self, kaa_platform_message_reader_t *reader, uint16_t extension_options, size_t extension_length); + +#endif /* KAA_USER_PRIVATE_H */ diff --git a/client/client-multi/client-c/src/extensions/user/test/test_kaa_user.c b/client/client-multi/client-c/src/extensions/user/test/test_kaa_user.c new file mode 100644 index 0000000000..36c0ac898b --- /dev/null +++ b/client/client-multi/client-c/src/extensions/user/test/test_kaa_user.c @@ -0,0 +1,254 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "platform/ext_sha.h" + +#include "kaa_user.h" +#include "kaa_user_private.h" + +#include "kaa_test.h" + +#include "kaa.h" +#include "kaa_context.h" +#include "kaa_platform_protocol.h" +#include "kaa_channel_manager.h" +#include "kaa_status.h" +#include "kaa_platform_utils.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include "platform/sock.h" + +#include "kaa_private.h" + +#define USER_EXTERNAL_ID "user@id" +#define ACCESS_TOKEN "token" +#define USER_VERIFIER "user_verifier" + +#define ATTACH_ERROR_REASON "Bad user credentials" + +static kaa_context_t kaa_context; +static kaa_user_manager_t *user_manager = NULL; +static kaa_logger_t *logger = NULL; +static kaa_status_t *status = NULL; +static kaa_channel_manager_t *channel_manager = NULL; + +static bool is_on_attached_invoked = false; +static bool is_on_detached_invoked = false; +static bool is_attach_success_invoked = false; +static bool is_attach_failed_invoked = false; +static bool last_is_attached_result = false; + + + +static kaa_error_t on_attached(void *context, const char *user_external_id, const char *endpoint_access_token) +{ + (void)context; + + ASSERT_EQUAL(strcmp(ACCESS_TOKEN, endpoint_access_token), 0); + ASSERT_EQUAL(strcmp(USER_EXTERNAL_ID, user_external_id), 0); + is_on_attached_invoked = true; + return KAA_ERR_NONE; +} + +static kaa_error_t on_detached(void *context, const char *endpoint_access_token) +{ + (void)context; + + ASSERT_EQUAL(strcmp(ACCESS_TOKEN, endpoint_access_token), 0); + is_on_detached_invoked = true; + return KAA_ERR_NONE; +} + +static kaa_error_t on_attach_success(void *context) +{ + (void)context; + + last_is_attached_result = true; + is_attach_success_invoked = true; + return KAA_ERR_NONE; +} + +static kaa_error_t on_attach_failed(void *context, user_verifier_error_code_t error_code, const char *reason) +{ + (void)context; + is_attach_failed_invoked = true; + ASSERT_EQUAL(error_code, CONNECTION_ERROR); + ASSERT_EQUAL(memcmp(reason, ATTACH_ERROR_REASON, strlen(ATTACH_ERROR_REASON)), 0); + return KAA_ERR_NONE; +} + +void test_specified_user_verifier(void **state) +{ + (void)state; + + ASSERT_EQUAL(kaa_user_manager_attach_to_user(user_manager, USER_EXTERNAL_ID, ACCESS_TOKEN, USER_VERIFIER), KAA_ERR_NONE); + + size_t expected_size = 0; + ASSERT_EQUAL(kaa_user_request_get_size(user_manager, &expected_size), KAA_ERR_NONE); + + uint8_t buffer[expected_size]; + kaa_platform_message_writer_t *writer = NULL; + ASSERT_EQUAL(kaa_platform_message_writer_create(&writer, buffer, expected_size), KAA_ERR_NONE); + ASSERT_NOT_NULL(writer); + + ASSERT_EQUAL(kaa_user_request_serialize(user_manager, writer), KAA_ERR_NONE); + + uint8_t *buf_cursor = buffer; + ASSERT_EQUAL(KAA_EXTENSION_USER, KAA_HTONS(*(uint16_t*)buf_cursor)); + buf_cursor += sizeof(uint16_t); + + char options[] = { 0x00, 0x01 }; + ASSERT_EQUAL(memcmp(buf_cursor, options, 2), 0); + buf_cursor += 2; + + ASSERT_EQUAL(*(uint32_t * ) buf_cursor, KAA_HTONL(2 * sizeof(uint32_t) + + kaa_aligned_size_get(strlen(USER_EXTERNAL_ID)) + + kaa_aligned_size_get(strlen(ACCESS_TOKEN) + + kaa_aligned_size_get(strlen(USER_VERIFIER))))); + buf_cursor += sizeof(uint32_t); + + ASSERT_EQUAL(0, *buf_cursor); + ++buf_cursor; + + ASSERT_EQUAL(strlen(USER_EXTERNAL_ID), *buf_cursor); + ++buf_cursor; + + ASSERT_EQUAL(KAA_HTONS(strlen(ACCESS_TOKEN)), *(uint16_t *) buf_cursor); + buf_cursor += sizeof(uint16_t); + + ASSERT_EQUAL(KAA_HTONS(strlen(USER_VERIFIER)), *(uint16_t *) buf_cursor); + buf_cursor += sizeof(uint32_t); // + reserved 16B + + ASSERT_EQUAL(memcmp(buf_cursor, USER_EXTERNAL_ID, strlen(USER_EXTERNAL_ID)), 0); + buf_cursor += kaa_aligned_size_get(strlen(USER_EXTERNAL_ID)); + + ASSERT_EQUAL(memcmp(buf_cursor, ACCESS_TOKEN, strlen(ACCESS_TOKEN)), 0); + buf_cursor += kaa_aligned_size_get(strlen(ACCESS_TOKEN)); + + ASSERT_EQUAL(memcmp(buf_cursor, USER_VERIFIER, strlen(USER_VERIFIER)), 0); + buf_cursor += kaa_aligned_size_get(strlen(USER_VERIFIER)); + + kaa_platform_message_writer_destroy(writer); +} + +void test_success_response(void **state) +{ + (void)state; + + uint8_t success_response[] = { + /* bit 0 */ 0x00, 0x00, 0x00, 0x00, /* User attach response field. Result - success */ + /* bit 32 */ 0x01, 0x07, 0x00, 0x05, /* User attach notification field */ + /* bit 64 */ 'u', 's', 'e', 'r', + /* bit 96 */ '@', 'i', 'd', 0x00, + /* bit 128 */ 't', 'o', 'k', 'e', + /* bit 160 */ 'n', 0x00, 0x00, 0x00, + /* bit 192 */ 0x02, 0x00, 0x00, 0x05, /* User detach notification field */ + /* bit 224 */ 't', 'o', 'k', 'e', + /* bit 256 */ 'n', 0x00, 0x00, 0x00 + + }; + + kaa_platform_message_reader_t *reader = NULL; + ASSERT_EQUAL(kaa_platform_message_reader_create(&reader, success_response, 36), KAA_ERR_NONE); + ASSERT_NOT_NULL(reader); + + ASSERT_EQUAL(kaa_user_handle_server_sync(user_manager, reader, 0, 36), KAA_ERR_NONE); + ASSERT_TRUE(is_on_attached_invoked); + ASSERT_TRUE(is_on_detached_invoked); + ASSERT_TRUE(is_attach_success_invoked); + ASSERT_TRUE(last_is_attached_result); + + kaa_platform_message_reader_destroy(reader); +} + +void test_failed_response(void **state) +{ + (void)state; + + uint8_t failed_response[] = { + /* bit 0 */ 0x00, 0x00, 0x01, 0x00, /* User attach response field. Result - success */ + /* bit 32 */ 0x00, 0x04, 0x00, 0x14, /* User attach notification field */ + /* bit 64 */ 'B', 'a', 'd', ' ', + /* bit 96 */ 'u', 's', 'e', 'r', + /* bit 128 */ ' ', 'c', 'r', 'e', + /* bit 160 */ 'd', 'e', 'n', 't', + /* bit 192 */ 'i', 'a', 'l', 's' + }; + + kaa_platform_message_reader_t *reader = NULL; + ASSERT_EQUAL(kaa_platform_message_reader_create(&reader, failed_response, 28), KAA_ERR_NONE); + ASSERT_NOT_NULL(reader); + + ASSERT_EQUAL(kaa_user_handle_server_sync(user_manager, reader, 0, 28), KAA_ERR_NONE); + ASSERT_TRUE(is_attach_failed_invoked); + + kaa_platform_message_reader_destroy(reader); +} + + + +int test_init(void) +{ + kaa_error_t error = kaa_log_create(&logger, KAA_MAX_LOG_MESSAGE_LENGTH, KAA_MAX_LOG_LEVEL, NULL); + if (error || !logger) { + return error; + } + + kaa_context.logger = logger; + + error = kaa_status_create(&status); + if (error || !status) { + return error; + } + + error = kaa_channel_manager_create(&channel_manager, &kaa_context); + if (error || !channel_manager) { + return error; + } + + error = kaa_user_manager_create(&user_manager, status, channel_manager, logger); + if (error || !user_manager) { + return error; + } + + kaa_attachment_status_listeners_t listeners = { NULL, &on_attached, &on_detached, &on_attach_success, &on_attach_failed }; + error = kaa_user_manager_set_attachment_listeners(user_manager, &listeners); + if (error) { + return error; + } + + return 0; +} + +int test_deinit(void) +{ + kaa_user_manager_destroy(user_manager); + kaa_channel_manager_destroy(channel_manager); + kaa_status_destroy(status); + kaa_log_destroy(logger); + + return 0; +} + +KAA_SUITE_MAIN(Log, test_init, test_deinit + , + KAA_TEST_CASE(specified_user_verifier, test_specified_user_verifier) + KAA_TEST_CASE(process_success_response, test_success_response) + KAA_TEST_CASE(process_failed_response, test_failed_response) + ) diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/configuration_persistence.c b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/configuration_persistence.c new file mode 100644 index 0000000000..d2daaed8d1 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/configuration_persistence.c @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include + +#define KAA_CONFIGURATION_STORAGE "kaa_configuration.bin" + +void ext_configuration_read(char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ + cc32xx_binary_file_read(KAA_CONFIGURATION_STORAGE, buffer, buffer_size, needs_deallocation); +} + +void ext_configuration_store(const char *buffer, size_t buffer_size) +{ + cc32xx_binary_file_store(KAA_CONFIGURATION_STORAGE, buffer, buffer_size); +} + +void ext_configuration_delete(void) +{ + cc32xx_binary_file_delete(KAA_CONFIGURATION_STORAGE); +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/file_utils.c b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/file_utils.c new file mode 100644 index 0000000000..7643c2e932 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/file_utils.c @@ -0,0 +1,192 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "utilities/kaa_mem.h" +#include "kaa_common.h" + +#include + +//Driverlib includes +#include "hw_types.h" +#include "hw_ints.h" +#include "rom.h" +#include "rom_map.h" +#include "interrupt.h" +#include "prcm.h" + +//Common interface includes +#include "common.h" +#include "uart_if.h" + +#define MAX_FILE_SIZE 8L*1024L + +// TODO: KAA-845 +// There is no file consistency check when parsing persistence data. This causes +// bugs when data inside a file is misinterpreted. +#if 0 +static bool file_is_exist(const char *filename) +{ + uint32_t ul_token; + int32_t l_file_handle; + int32_t l_ret_val = sl_FsOpen((unsigned char *)filename, FS_MODE_OPEN_WRITE, &ul_token, &l_file_handle); + + if (l_ret_val < 0) { + sl_FsClose(l_file_handle, 0, 0, 0); + return false; + } + + sl_FsClose(l_file_handle, 0, 0, 0); + return true; +} + +static bool create_file(const char *filename) +{ + uint32_t ul_token; + int32_t l_file_handle; + int32_t l_ret_val; + + l_ret_val = sl_FsOpen((unsigned char *)filename,FS_MODE_OPEN_CREATE(MAX_FILE_SIZE, _FS_FILE_OPEN_FLAG_COMMIT|_FS_FILE_PUBLIC_WRITE|_FS_FILE_PUBLIC_READ), &ul_token, &l_file_handle); + if (l_ret_val < 0) { + sl_FsClose(l_file_handle, 0, 0, 0); + return false; + } else { + l_ret_val = sl_FsClose(l_file_handle, 0, 0, 0); + if (SL_RET_CODE_OK != l_ret_val) + return false; + } + return true; +} +#endif + +int cc32xx_binary_file_read(const char *file_name, char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ +// TODO: KAA-845 +// There is no file consistency check when parsing persistence data. This causes +// bugs when data inside a file is misinterpreted. +#if 0 + KAA_RETURN_IF_NIL4(file_name, buffer, buffer_size, needs_deallocation, -1); + *buffer = NULL; + *buffer_size = 0; + + int32_t ret = -1; + uint32_t ul_token; + int32_t l_file_handle; + SlFsFileInfo_t file_info; + + memset(&file_info, 0, sizeof(file_info)); + + ret = sl_FsOpen((unsigned char*)file_name, FS_MODE_OPEN_READ, &ul_token, &l_file_handle); + + if (ret < 0) { + sl_FsClose(l_file_handle, 0, 0, 0); + return -1; + } + + sl_FsGetInfo((unsigned char*)file_name, ul_token, &file_info); + + if (file_info.FileLen <= 0) { + sl_FsClose(l_file_handle, 0, 0, 0); + return -1; + } + + uint8_t *result_buffer = (uint8_t*) KAA_MALLOC(file_info.FileLen * sizeof(uint8_t)); + if (!result_buffer) { + sl_FsClose(l_file_handle, 0, 0, 0); + return -1; + } + + if ( sl_FsRead(l_file_handle, 0, result_buffer, file_info.FileLen) == 0 ) { + KAA_FREE(result_buffer); + sl_FsClose(l_file_handle, 0, 0, 0); + return -1; + } + + *buffer = (char*) result_buffer; + *buffer_size = file_info.FileLen; + *needs_deallocation = true; + + sl_FsClose(l_file_handle, 0, 0, 0); + return 0; +#else + (void)file_name; + (void)buffer; + (void)buffer_size; + (void)needs_deallocation; +#endif + return -1; +} + +int cc32xx_binary_file_store(const char *file_name, const char *buffer, size_t buffer_size) +{ +// TODO: KAA-845 +// There is no file consistency check when parsing persistence data. This causes +// bugs when data inside a file is misinterpreted. +#if 0 + KAA_RETURN_IF_NIL3(file_name, buffer, buffer_size, -1); + + int32_t ret = -1; + uint32_t ul_token; + int32_t l_file_handle; + + if (MAX_FILE_SIZE < buffer_size) + return ret; + + if (!file_is_exist(file_name) && !create_file(file_name)) + return ret; + + ret = sl_FsOpen((unsigned char*)file_name, FS_MODE_OPEN_WRITE, &ul_token, &l_file_handle); + if (ret == 0) { + if(sl_FsWrite(l_file_handle, 0, (unsigned char *)buffer, buffer_size)) + ret = 0; + sl_FsClose(l_file_handle, 0, 0, 0); + } + return ret; +#else + (void)file_name; + (void)buffer; + (void)buffer_size; + return -1; +#endif +} + +int cc32xx_binary_file_delete(const char *file_name) +{ +// TODO: KAA-845 +// There is no file consistency check when parsing persistence data. This causes +// bugs when data inside a file is misinterpreted. +#if 0 + int32_t ret = 0; + uint32_t ul_token; + int32_t l_file_handle; + + ret = sl_FsOpen((unsigned char*)file_name, FS_MODE_OPEN_READ, &ul_token, &l_file_handle); + sl_FsClose(l_file_handle, 0, 0, 0); + if (ret < 0) + return -1; + + ret = sl_FsDel(file_name, 0); + if (ret < 0) + return -1; + + return 0; +#else + (void)file_name; + return -1; +#endif +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/key_utils.c b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/key_utils.c new file mode 100644 index 0000000000..922e37b4d3 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/key_utils.c @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "utilities/kaa_mem.h" +#include "kaa_common.h" +#include +#include "cc32xx_rsa_key.h" + +#define KAA_KEY_STORAGE "kaa_key.pub" + +static char *kaa_public_key = (char*)KAA_PUBLIC_KEY_DATA; +static size_t kaa_public_key_length = KAA_PUBLIC_KEY_LENGTH; + +void ext_get_endpoint_public_key(char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ + KAA_RETURN_IF_NIL3(buffer, buffer_size, needs_deallocation,); + *buffer = kaa_public_key; + *buffer_size = kaa_public_key_length; + *needs_deallocation = false; +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/defaults.h b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/defaults.h new file mode 100644 index 0000000000..967d300c38 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/defaults.h @@ -0,0 +1,30 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef CC32XX_DEFAULTS_H_ +#define CC32XX_DEFAULTS_H_ + +#define KAA_TCP_CHANNEL_IN_BUFFER_SIZE 2048 +#define KAA_TCP_CHANNEL_OUT_BUFFER_SIZE 8192 + +#define KAA_TCP_CHANNEL_MAX_TIMEOUT 200u +#define KAA_TCP_CHANNEL_PING_TIMEOUT (KAA_TCP_CHANNEL_MAX_TIMEOUT / 2) + +#define KAATCP_PARSER_MAX_MESSAGE_LENGTH 1024 +#define KAA_MAX_LOG_MESSAGE_LENGTH 512 + +#endif /* CC32XX_DEFAULTS_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/file_utils.h b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/file_utils.h new file mode 100644 index 0000000000..3e3647a60d --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/file_utils.h @@ -0,0 +1,36 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CC32XX_FILE_UTILS_H_ +#define CC32XX_FILE_UTILS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int cc32xx_binary_file_read(const char *file_name, char **buffer, size_t *buffer_size, bool *needs_deallocation); + +int cc32xx_binary_file_store(const char *file_name, const char *buffer, size_t buffer_size); + +int cc32xx_binary_file_delete(const char *file_name); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* CC32XX_FILE_UTILS_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/mem.h b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/mem.h new file mode 100644 index 0000000000..7a36b7f1df --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/mem.h @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CC32XX_MEM_H_ +#define CC32XX_MEM_H_ + +#include + +#ifndef __KAA_MALLOC +#define __KAA_MALLOC(S) malloc(S) +#endif + +#ifndef __KAA_CALLOC +#define __KAA_CALLOC(N,S) calloc(N, S) +#endif + +#ifndef __KAA_REALLOC +#define __KAA_REALLOC(P, S) realloc(P, S) +#endif + +#ifndef __KAA_FREE +#define __KAA_FREE(P) free(P) +#endif + +#endif /* CC32XX_MEM_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/sock.h b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/sock.h new file mode 100644 index 0000000000..fe0ec10257 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/sock.h @@ -0,0 +1,88 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CC32XX_SOCK_H_ +#define CC32XX_SOCK_H_ + +/* Avoid redefined warning */ +#undef __CONCAT +#undef FD_SETSIZE +#undef FD_SET +#undef FD_CLR +#undef FD_ISSET +#undef FD_ZERO +#undef fd_set +#undef EBADF +#undef EAGAIN +#undef EWOULDBLOCK +#undef ENOMEM +#undef EACCES +#undef EFAULT +#undef EINVAL +#undef EDESTADDRREQ +#undef EPROTOTYPE +#undef ENOPROTOOPT +#undef EPROTONOSUPPORT +#undef EOPNOTSUPP +#undef EAFNOSUPPORT +#undef EADDRINUSE +#undef EADDRNOTAVAIL +#undef ENETUNREACH +#undef ENOBUFS +#undef EISCONN +#undef ENOTCONN +#undef ETIMEDOUT +#undef ECONNREFUSED + +#include "simplelink.h" +#include "socket.h" +#include "platform-impl/common/kaa_htonll.h" + +struct addrinfo +{ + int ai_flags; /* Input flags. */ + int ai_family; /* Protocol family for socket. */ + int ai_socktype; /* Socket type. */ + int ai_protocol; /* Protocol for socket. */ + socklen_t ai_addrlen; /* Length of socket address. */ + struct sockaddr *ai_addr; /* Socket address for socket. */ + char *ai_canonname; /* Canonical name for service location. */ + struct addrinfo *ai_next; /* Pointer to next in list. */ +}; + +#define _SS_PADSIZE (128 - (2 * sizeof (unsigned long))) +struct sockaddr_storage +{ + unsigned short ss_family; /* Address family, etc. */ + unsigned long __ss_align; /* Force desired alignment. */ + char __ss_padding[_SS_PADSIZE]; +}; + +typedef int kaa_fd_t; + +typedef struct sockaddr kaa_sockaddr_t; +typedef struct sockaddr_storage kaa_sockaddr_storage_t; +typedef socklen_t kaa_socklen_t; + +#define KAA_HTONS(hostshort) htons((hostshort)) +#define KAA_HTONL(hostlong) htonl((hostlong)) +#define KAA_HTONLL(hostlonglong) htonll((hostlonglong)) + +#define KAA_NTOHS(netshort) ntohs((netshort)) +#define KAA_NTOHL(netlong) ntohl((netlong)) +#define KAA_NTOHLL(netlonglong) ntohll((netlonglong)) + +#endif /* CC32XX_SOCK_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/stdio.h b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/stdio.h new file mode 100644 index 0000000000..1243b0195a --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/stdio.h @@ -0,0 +1,24 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CC32XX_STDIO_H_ +#define CC32XX_STDIO_H_ + +#include + +void cc32xx_reboot(void); + +#endif /* CC32XX_STDIO_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/time.h b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/time.h new file mode 100644 index 0000000000..96a01ea3f1 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/platform/time.h @@ -0,0 +1,30 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef CC32XX_TIME_H_ +#define CC32XX_TIME_H_ + +#include + +typedef long kaa_time_t; + +#define KAA_TIME() ((kaa_time_t)cc32xx_time()) + +long cc32xx_time(void); +long long cc32xx_clock_getms(void); + +#endif /* CC32XX_TIME_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/reboot.c b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/reboot.c new file mode 100644 index 0000000000..791d99622e --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/reboot.c @@ -0,0 +1,31 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hw_types.h" +#include "device.h" +#include "prcm.h" +#include "utils_if.h" + +void cc32xx_reboot(void) +{ + sl_Stop(30); + PRCMHibernateIntervalSet(330); + PRCMHibernateWakeupSourceEnable(PRCM_HIB_SLOW_CLK_CTR); + PRCMHibernateEnter(); + + PRCMMCUReset(true); + Utils_TriggerHibCycle(); +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/status.c b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/status.c new file mode 100644 index 0000000000..88d0edc7f9 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/status.c @@ -0,0 +1,39 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#define KAA_STATUS_STORAGE "kaa_status.bin" + +void ext_status_read(char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ + cc32xx_binary_file_read(KAA_STATUS_STORAGE, buffer, buffer_size, needs_deallocation); +} + +void ext_status_store(const char *buffer, size_t buffer_size) +{ + cc32xx_binary_file_store(KAA_STATUS_STORAGE, buffer, buffer_size); +} + +void ext_status_delete(void) +{ + cc32xx_binary_file_delete(KAA_STATUS_STORAGE); +} + + diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/tcp_utils.c b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/tcp_utils.c new file mode 100644 index 0000000000..0ff0a71dce --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/tcp_utils.c @@ -0,0 +1,181 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "platform/ext_tcp_utils.h" +#include +#include "kaa_common.h" +#include + +//Driverlib includes +#include "hw_types.h" +#include "hw_ints.h" +#include "rom.h" +#include "rom_map.h" +#include "interrupt.h" +#include "prcm.h" + +//Common interface includes +#include "common.h" +#include "uart_if.h" + +kaa_error_t ext_tcp_utils_set_sockaddr_port(kaa_sockaddr_t *addr, uint16_t port) +{ + KAA_RETURN_IF_NIL2(addr, port, KAA_ERR_BADPARAM); + switch (addr->sa_family) { + case AF_INET: { + struct sockaddr_in *s_in = (struct sockaddr_in *) addr; + s_in->sin_port = KAA_HTONS(port); + break; + } + default: + return KAA_ERR_SOCKET_INVALID_FAMILY; + } + return KAA_ERR_NONE; +} + + + +ext_tcp_utils_function_return_state_t ext_tcp_utils_getaddrbyhost(kaa_dns_resolve_listener_t *resolve_listener + , const kaa_dns_resolve_info_t *resolve_props + , kaa_sockaddr_t *result + , kaa_socklen_t *result_size) +{ + (void)resolve_listener; + KAA_RETURN_IF_NIL4(resolve_props, resolve_props->hostname, result, result_size, RET_STATE_VALUE_ERROR); + if (*result_size < sizeof(struct sockaddr_in)) + return RET_STATE_BUFFER_NOT_ENOUGH; + + unsigned long out_ip = 0; + int ai_family; + int resolve_error = 0; + struct sockaddr_in tmp_addr; + + ai_family = AF_INET; + *result_size = sizeof(struct sockaddr_in); + + char hostname_str[resolve_props->hostname_length + 1]; + memcpy(hostname_str, resolve_props->hostname, resolve_props->hostname_length); + hostname_str[resolve_props->hostname_length] = '\0'; + + if (strcmp(hostname_str, "localhost")) + resolve_error = sl_NetAppDnsGetHostByName((signed char*)hostname_str, resolve_props->hostname_length, &out_ip, ai_family); + else + out_ip = 0x7F000001; + + memset(&tmp_addr, 0, *result_size); + tmp_addr.sin_family = ai_family; + tmp_addr.sin_addr.s_addr = sl_Htonl((unsigned int)out_ip); + tmp_addr.sin_port = sl_Htons((unsigned short)resolve_props->port); + + if (resolve_error || !out_ip) + return RET_STATE_VALUE_ERROR; + + memcpy(result, (struct sockaddr*)&tmp_addr, *result_size); + return RET_STATE_VALUE_READY; +} + + + +kaa_error_t ext_tcp_utils_open_tcp_socket(kaa_fd_t *fd + , const kaa_sockaddr_t *destination + , kaa_socklen_t destination_size) +{ + KAA_RETURN_IF_NIL3(fd, destination, destination_size, KAA_ERR_BADPARAM); + + long lNonBlocking = 1; + int status; + + kaa_fd_t sock = socket(destination->sa_family, SOCK_STREAM, 0); + if (sock < 0) + return KAA_ERR_SOCKET_ERROR; + + status = setsockopt(sock, SL_SOL_SOCKET, SL_SO_NONBLOCKING, + &lNonBlocking, sizeof(lNonBlocking)); + if ( status < 0 ) { + ext_tcp_utils_tcp_socket_close(sock); + return KAA_ERR_SOCKET_ERROR; + } + + int err = connect(sock, destination, destination_size); + if ( (err < 0 && err != SL_EALREADY) && errno != EINPROGRESS) { + ext_tcp_utils_tcp_socket_close(sock); + return KAA_ERR_SOCKET_CONNECT_ERROR; + } + + *fd = sock; + return KAA_ERR_NONE; +} + + + +ext_tcp_socket_state_t ext_tcp_utils_tcp_socket_check(kaa_fd_t fd + , const kaa_sockaddr_t *destination + , kaa_socklen_t destination_size) +{ + if (connect(fd, destination, destination_size) < 0 ) { + switch (errno) { + case EINPROGRESS: + case EALREADY: + return KAA_TCP_SOCK_CONNECTING; + case EISCONN: + return KAA_TCP_SOCK_CONNECTED; + default: + return KAA_TCP_SOCK_ERROR; + } + } + return KAA_TCP_SOCK_CONNECTED; +} + + + +ext_tcp_socket_io_errors_t ext_tcp_utils_tcp_socket_write(kaa_fd_t fd + , const char *buffer + , size_t buffer_size + , size_t *bytes_written) +{ + KAA_RETURN_IF_NIL2(buffer, buffer_size, KAA_TCP_SOCK_IO_ERROR); + ssize_t write_result = send(fd, buffer, buffer_size, 0); + if (write_result < 0 && errno != EAGAIN) + return KAA_TCP_SOCK_IO_ERROR; + if (bytes_written) + *bytes_written = (write_result > 0) ? write_result : 0; + return KAA_TCP_SOCK_IO_OK; +} + + + +ext_tcp_socket_io_errors_t ext_tcp_utils_tcp_socket_read(kaa_fd_t fd + , char *buffer + , size_t buffer_size + , size_t *bytes_read) +{ + KAA_RETURN_IF_NIL2(buffer, buffer_size, KAA_TCP_SOCK_IO_ERROR); + ssize_t read_result = recv(fd, buffer, buffer_size, 0); + if (!read_result) + return KAA_TCP_SOCK_IO_EOF; + if (read_result < 0 && errno != EAGAIN) + return KAA_TCP_SOCK_IO_ERROR; + if (bytes_read) + *bytes_read = (read_result > 0) ? read_result : 0; + return KAA_TCP_SOCK_IO_OK; +} + + + +kaa_error_t ext_tcp_utils_tcp_socket_close(kaa_fd_t fd) +{ + return (close(fd) < 0) ? KAA_ERR_SOCKET_ERROR : KAA_ERR_NONE; +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/time.c b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/time.c new file mode 100644 index 0000000000..33b4519759 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/cc32xx/time.c @@ -0,0 +1,53 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "systick.h" + +#define MPU_FREQUENCY 80000000 + +static long long milliTimer = 1; + +static void sysTickIntHandler(void) +{ + ++milliTimer; +} + +void cc32xx_init_timer(void) +{ + static int init = 0; + + if (!init) { + SysTickEnable(); + SysTickIntEnable(); + SysTickIntRegister(sysTickIntHandler); + SysTickPeriodSet(MPU_FREQUENCY / 1000);/* 1 ms */ + init = 1; + } +} + +long long cc32xx_clock_getms(void) +{ + cc32xx_init_timer(); + return milliTimer; +} + +long cc32xx_time(void) +{ + cc32xx_init_timer(); + return milliTimer / 1000; +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/configuration_persistence.c b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/configuration_persistence.c new file mode 100644 index 0000000000..4c16a3e5db --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/configuration_persistence.c @@ -0,0 +1,37 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#define KAA_CONFIGURATION_STORAGE "" /* there is no filesystem on esp8266 */ + +void ext_configuration_read(char **buffer, size_t *buffer_size, bool *needs_deallocation) { + (void)buffer; + (void)buffer_size; + (void)needs_deallocation; +} + +void ext_configuration_store(const char *buffer, size_t buffer_size) { + (void)buffer; + (void)buffer_size; +} + +void ext_configuration_delete(void) +{ + +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/kaa_client.c b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/kaa_client.c new file mode 100644 index 0000000000..35ef4e8654 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/kaa_client.c @@ -0,0 +1,464 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include "platform/ext_transport_channel.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include +#include "kaa_channel_manager.h" +#include "platform-impl/common/kaa_tcp_channel.h" +#include "platform-impl/common/ext_log_upload_strategies.h" +#include "platform/ext_kaa_failover_strategy.h" + +#ifndef KAA_DISABLE_FEATURE_LOGGING +#include "kaa_logging.h" +#include "kaa_logging_private.h" + +static kaa_error_t kaa_log_collector_init(kaa_client_t *kaa_client); +#endif + +#include "kaa_private.h" + +typedef enum { + KAA_CLIENT_CHANNEL_STATE_CONNECTED = 0, + KAA_CLIENT_CHANNEL_STATE_NOT_CONNECTED +} kaa_client_channel_state_t; + +typedef enum { + KAA_CLIENT_CHANNEL_TYPE_BOOTSTRAP = 0, + KAA_CLIENT_CHANNEL_TYPE_OPERATIONS +} kaa_client_channel_type_t; + +static kaa_extension_id BOOTSTRAP_SERVICE[] = { KAA_EXTENSION_BOOTSTRAP }; +static const int BOOTSTRAP_SERVICE_COUNT = sizeof(BOOTSTRAP_SERVICE) / sizeof(kaa_extension_id); + +static kaa_extension_id OPERATIONS_SERVICES[] = { KAA_EXTENSION_PROFILE + , KAA_EXTENSION_USER +#ifndef KAA_DISABLE_FEATURE_CONFIGURATION + , KAA_EXTENSION_CONFIGURATION +#endif +#ifndef KAA_DISABLE_FEATURE_EVENTS + , KAA_EXTENSION_EVENT +#endif +#ifndef KAA_DISABLE_FEATURE_LOGGING + , KAA_EXTENSION_LOGGING +#endif +#ifndef KAA_DISABLE_FEATURE_NOTIFICATION + , KAA_EXTENSION_NOTIFICATION +#endif + }; +static const int OPERATIONS_SERVICES_COUNT = sizeof(OPERATIONS_SERVICES) / sizeof(kaa_extension_id); + +/* Logging constraints */ +#define MAX_LOG_COUNT SIZE_MAX +#define MAX_LOG_BUCKET_SIZE (KAA_TCP_CHANNEL_OUT_BUFFER_SIZE >> 3) + +_Static_assert(MAX_LOG_BUCKET_SIZE, "Maximum bucket size cannot be 0!"); + +struct kaa_client_t { + kaa_context_t *context; + bool operate; + + kaa_transport_channel_interface_t channel; + kaa_client_channel_state_t channel_state; + uint32_t channel_id; + bool channel_socket_closed; + + bool bootstrap_complete; + + external_process_fn external_process; + void *external_process_context; + time_t external_process_max_delay; + time_t external_process_last_call; + +#ifndef KAA_DISABLE_FEATURE_LOGGING + void *log_storage_context; + void *log_upload_strategy_context; +#endif +}; + +static kaa_error_t kaa_client_init_channel(kaa_client_t *kaa_client, kaa_client_channel_type_t channel_type); +static kaa_error_t kaa_client_deinit_channel(kaa_client_t *kaa_client); +static kaa_error_t on_kaa_tcp_channel_event(void *context, kaa_tcp_channel_event_t event_type, kaa_fd_t fd); + +#define KAA_RETURN_IF_ERR_MSG(E, msg) \ + { if(E) { printf("Error %i. \"%s\"\n",(E), (msg)); return (E); } } + + +kaa_error_t on_kaa_tcp_channel_event(void *context, kaa_tcp_channel_event_t event_type, kaa_fd_t fd) +{ + (void)fd; + KAA_RETURN_IF_NIL(context, KAA_ERR_BADPARAM); + + if (event_type == SOCKET_DISCONNECTED) { + ((kaa_client_t *)context)->channel_socket_closed = true; + } + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_client_create(kaa_client_t **client, kaa_client_props_t *props) { + (void)props; + KAA_RETURN_IF_NIL(client, KAA_ERR_BADPARAM); + + kaa_client_t *self = (kaa_client_t*)KAA_CALLOC(1, sizeof(kaa_client_t)); + KAA_RETURN_IF_NIL(self, KAA_ERR_NOMEM); + kaa_error_t error_code = kaa_init(&self->context); + + if(error_code) { + printf("Error initialising kaa_context\n"); + kaa_client_destroy(self); + return error_code; + } + + self->operate = true; + +#ifndef KAA_DISABLE_FEATURE_LOGGING + error_code = kaa_log_collector_init(self); + if (error_code) { + KAA_LOG_ERROR(self->context->logger, error_code, "Failed to init Kaa log collector, error %d", error_code); + kaa_client_destroy(self); + return error_code; + } +#endif + + KAA_LOG_INFO(self->context->logger,KAA_ERR_NONE, "Kaa client initiallized"); + *client = self; + return error_code; +} + +kaa_context_t *kaa_client_get_context(kaa_client_t *client) +{ + KAA_RETURN_IF_NIL(client, NULL); + return client->context; +} + +static uint16_t get_poll_timeout(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, 0); + + uint16_t select_timeout; + kaa_tcp_channel_get_max_timeout(&kaa_client->channel, &select_timeout); + + + if ((kaa_client->external_process_max_delay > 0) && (select_timeout > kaa_client->external_process_max_delay)) { + select_timeout = kaa_client->external_process_max_delay; + } + + if ((KAA_BOOTSTRAP_RESPONSE_PERIOD > 0) && (select_timeout > KAA_BOOTSTRAP_RESPONSE_PERIOD)) { + select_timeout = KAA_BOOTSTRAP_RESPONSE_PERIOD; + } + + return select_timeout; +} + + +kaa_error_t kaa_client_process_channel_connected(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + fd_set read_fds, write_fds, except_fds; + struct timeval select_tv = { get_poll_timeout(kaa_client), 0 }; + int channel_fd = 0; + + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + FD_ZERO(&except_fds); + + kaa_error_t error_code = kaa_tcp_channel_get_descriptor(&kaa_client->channel, &channel_fd); + if(error_code) KAA_LOG_ERROR(kaa_client->context->logger, error_code, "No descriptor provided!"); + + if (kaa_tcp_channel_is_ready(&kaa_client->channel, FD_READ)) + FD_SET(channel_fd, &read_fds); + if (kaa_tcp_channel_is_ready(&kaa_client->channel, FD_WRITE)) + FD_SET(channel_fd, &write_fds); + + int poll_result = select(channel_fd + 1, &read_fds, &write_fds, NULL, &select_tv); + if (poll_result == 0) { + error_code = kaa_tcp_channel_check_keepalive(&kaa_client->channel); + } else if (poll_result > 0) { + if (channel_fd >= 0) { + if (FD_ISSET(channel_fd, &read_fds)) { + KAA_LOG_DEBUG(kaa_client->context->logger, KAA_ERR_NONE, + "Processing IN event for the client socket %d", channel_fd); + error_code = kaa_tcp_channel_process_event(&kaa_client->channel, FD_READ); + if (error_code) { + KAA_LOG_ERROR(kaa_client->context->logger, error_code, + "Failed to process IN event for the client socket %d", channel_fd); + } + } + if (FD_ISSET(channel_fd, &write_fds)) { + KAA_LOG_DEBUG(kaa_client->context->logger, KAA_ERR_NONE, + "Processing OUT event for the client socket %d", channel_fd); + + error_code = kaa_tcp_channel_process_event(&kaa_client->channel, FD_WRITE); + if (error_code) { + KAA_LOG_ERROR(kaa_client->context->logger, error_code, + "Failed to process OUT event for the client socket %d", channel_fd); + } + } + } + } else { + KAA_LOG_ERROR(kaa_client->context->logger, KAA_ERR_BAD_STATE, "Failed to poll descriptors"); + error_code = KAA_ERR_BAD_STATE; + } + + if (kaa_client->channel_socket_closed) { + KAA_LOG_INFO(kaa_client->context->logger, KAA_ERR_NONE, + "Channel [0x%08X] connection terminated", kaa_client->channel_id); + + kaa_client->channel_state = KAA_CLIENT_CHANNEL_STATE_NOT_CONNECTED; + if (error_code != KAA_ERR_EVENT_NOT_ATTACHED) { + kaa_client_deinit_channel(kaa_client); + } + } + + return error_code; +} + +kaa_error_t kaa_client_process_channel_disconnected(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_error_t error_code = kaa_tcp_channel_check_keepalive(&kaa_client->channel); + if (error_code) { + KAA_LOG_ERROR(kaa_client->context->logger, error_code, "Failed to connect channel [0x%08X]", kaa_client->channel_id); + } else { + KAA_LOG_TRACE(kaa_client->context->logger, error_code, "Channel [0x%08X] successfully connected", kaa_client->channel_id); + kaa_client->channel_state = KAA_CLIENT_CHANNEL_STATE_CONNECTED; + } + + return error_code; +} + +kaa_error_t kaa_client_start(kaa_client_t *kaa_client, + external_process_fn external_process, + void *external_process_context, + kaa_time_t max_delay) { + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_error_t error_code = kaa_check_readiness(kaa_client->context); + if (error_code != KAA_ERR_NONE) { + KAA_LOG_ERROR(kaa_client->context->logger, error_code, "Cannot start Kaa client: Kaa context is not fully initialized"); + return error_code; + } + + kaa_client->external_process = external_process; + kaa_client->external_process_context = external_process_context; + kaa_client->external_process_max_delay = max_delay; + kaa_client->external_process_last_call = KAA_TIME(); + + KAA_LOG_INFO(kaa_client->context->logger, KAA_ERR_NONE, "Starting Kaa client..."); + + while (kaa_client->operate) { + if (kaa_client->external_process) { + if (KAA_TIME() - kaa_client->external_process_last_call >= (kaa_time_t)kaa_client->external_process_max_delay) { + kaa_client->external_process(kaa_client->external_process_context); + } + kaa_client->external_process_last_call = KAA_TIME(); + } + if (kaa_process_failover(kaa_client->context)) { + kaa_client->bootstrap_complete = false; + } else { + if(kaa_client->channel_id>0) { + if (kaa_client->channel_state == KAA_CLIENT_CHANNEL_STATE_NOT_CONNECTED) { + error_code = kaa_client_process_channel_disconnected(kaa_client); + } else if (kaa_client->channel_state == KAA_CLIENT_CHANNEL_STATE_CONNECTED) { + error_code = kaa_client_process_channel_connected(kaa_client); + if (error_code == KAA_ERR_TIMEOUT) + kaa_client_deinit_channel(kaa_client); + } + } else { + //No initialized channels + if (kaa_client->bootstrap_complete) { + KAA_LOG_INFO(kaa_client->context->logger, KAA_ERR_NONE, + "Channel [0x%08X] Boostrap complete, reinitializing to Operations ...", kaa_client->channel_id); + kaa_client->bootstrap_complete = false; + kaa_client_deinit_channel(kaa_client); + kaa_client_init_channel(kaa_client, KAA_CLIENT_CHANNEL_TYPE_OPERATIONS); + if (error_code == KAA_ERR_BAD_STATE) { + kaa_client_deinit_channel(kaa_client); + kaa_client->bootstrap_complete = false; + } + } else { + KAA_LOG_INFO(kaa_client->context->logger, KAA_ERR_NONE, + "Channel [0x%08X] Operations error, reinitializing to Bootstrap ...", kaa_client->channel_id); + kaa_client->bootstrap_complete = true; + kaa_client_deinit_channel(kaa_client); + + kaa_client_init_channel(kaa_client, KAA_CLIENT_CHANNEL_TYPE_BOOTSTRAP); + } + } + } +#ifndef KAA_DISABLE_FEATURE_LOGGING + ext_log_upload_timeout(kaa_client->context->log_collector); +#endif + } + KAA_LOG_INFO(kaa_client->context->logger, KAA_ERR_NONE, "Kaa client stopped"); + + return error_code; +} + +kaa_error_t kaa_client_init_channel(kaa_client_t *kaa_client, kaa_client_channel_type_t channel_type) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_error_t error_code = KAA_ERR_NONE; + + KAA_LOG_TRACE(kaa_client->context->logger, KAA_ERR_NONE, "Initializing channel...."); + + switch (channel_type) { + case KAA_CLIENT_CHANNEL_TYPE_BOOTSTRAP: + error_code = kaa_tcp_channel_create(&kaa_client->channel + , kaa_client->context->logger + , BOOTSTRAP_SERVICE + , BOOTSTRAP_SERVICE_COUNT); + break; + case KAA_CLIENT_CHANNEL_TYPE_OPERATIONS: + error_code = kaa_tcp_channel_create(&kaa_client->channel + , kaa_client->context->logger + , OPERATIONS_SERVICES + , OPERATIONS_SERVICES_COUNT); + break; + } + + if (error_code) { + KAA_LOG_ERROR(kaa_client->context->logger, error_code, "Failed to create transport channel, type %d", channel_type); + return error_code; + } + + error_code = kaa_tcp_channel_set_socket_events_callback(&kaa_client->channel, &on_kaa_tcp_channel_event, kaa_client); + if (error_code) { + KAA_LOG_ERROR(kaa_client->context->logger, error_code, + "Failed to set socket events callback, channel type %d", channel_type); + return error_code; + } + + error_code = kaa_channel_manager_add_transport_channel(kaa_client->context->channel_manager + , &kaa_client->channel + , &kaa_client->channel_id); + if (error_code) { + KAA_LOG_ERROR(kaa_client->context->logger, error_code, "Failed to add transport channel, type %d", channel_type); + return error_code; + } + + KAA_LOG_INFO(kaa_client->context->logger, KAA_ERR_NONE, "Channel [0x%08X] initialized successfully (type %d)" + , kaa_client->channel_id, channel_type); + + return error_code; +} + +kaa_error_t kaa_client_deinit_channel(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_error_t error_code = KAA_ERR_NONE; + + if (kaa_client->channel_id == 0) + return KAA_ERR_NONE; + + KAA_LOG_TRACE(kaa_client->context->logger, KAA_ERR_NONE, "Deinitializing channel...."); + + error_code = kaa_channel_manager_remove_transport_channel(kaa_client->context->channel_manager,kaa_client->channel_id); + if (error_code) { + KAA_LOG_TRACE(kaa_client->context->logger, error_code, "Bootstrap channel error removing from channel manager"); + return error_code; + } + + kaa_client->channel_id = 0; + kaa_client->channel_socket_closed = false; + kaa_client->channel.context = NULL; + kaa_client->channel.destroy = NULL; + kaa_client->channel.get_protocol_id = NULL; + kaa_client->channel.get_supported_services = NULL; + kaa_client->channel.init = NULL; + kaa_client->channel.set_access_point = NULL; + kaa_client->channel.sync_handler = NULL; + + KAA_LOG_TRACE(kaa_client->context->logger, KAA_ERR_NONE, "Channel deinitialized successfully"); + + return error_code; +} + + +kaa_error_t kaa_client_stop(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + KAA_LOG_INFO(kaa_client->context->logger, KAA_ERR_NONE, "Going to stop Kaa client..."); + kaa_client->operate = false; + + return kaa_stop(kaa_client->context); +} + + +void kaa_client_destroy(kaa_client_t *self) +{ + KAA_RETURN_IF_NIL(self, ); + + if (self->context) { + kaa_deinit(self->context); + } + + KAA_FREE(self); +} + + + +#ifndef KAA_DISABLE_FEATURE_LOGGING +static kaa_error_t kaa_log_collector_init(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + kaa_error_t error_code = ext_unlimited_log_storage_create(&kaa_client->log_storage_context, + kaa_client->context->logger); + + if (error_code) { + KAA_LOG_ERROR(kaa_client->context->logger, error_code, "Failed to create log storage"); + return error_code; + } + + error_code = ext_log_upload_strategy_create(kaa_client->context + ,&kaa_client->log_upload_strategy_context + , KAA_LOG_UPLOAD_VOLUME_STRATEGY); + if (error_code) { + KAA_LOG_ERROR(kaa_client->context->logger, error_code, "Failed to create log upload strategy"); + return error_code; + } + + kaa_log_bucket_constraints_t bucket_sizes = { + .max_bucket_size = MAX_LOG_BUCKET_SIZE, + .max_bucket_log_count = MAX_LOG_COUNT, + }; + + error_code = kaa_logging_init(kaa_client->context->log_collector + , kaa_client->log_storage_context + , kaa_client->log_upload_strategy_context + , &bucket_sizes); + if (error_code) { + KAA_LOG_ERROR(kaa_client->context->logger, error_code,"Failed to init log collector"); + return error_code; + } + + KAA_LOG_INFO(kaa_client->context->logger, KAA_ERR_NONE, "Log collector init completed"); + return error_code; +} +#endif diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/key_utils.c b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/key_utils.c new file mode 100644 index 0000000000..d8155cf2fe --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/key_utils.c @@ -0,0 +1,29 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "rsa.h" + + +void ext_get_endpoint_public_key(char **buffer, size_t *buffer_size, + bool *needs_deallocation) +{ + *buffer = (char*)KAA_PUBLIC_KEY_DATA; + *buffer_size = KAA_PUBLIC_KEY_LENGTH; + *needs_deallocation = false; +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/logger.c b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/logger.c new file mode 100644 index 0000000000..6d9408a253 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/logger.c @@ -0,0 +1,62 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include + +int snprintf(char *str, size_t count, const char *fmt, ...); +int vsnprintf(char *str, size_t count, const char *fmt, va_list arg); + +kaa_time_t ext_get_systime(void) +{ + return system_get_rtc_time()*((system_rtc_clock_cali_proc()*1000)>>12)/1000; +} + +void ext_write_log(FILE *sink, const char *buffer, size_t message_size) +{ + (void)sink; + (void)message_size; + printf("%s", buffer); +} + +int ext_logger_sprintf(char *buffer,size_t buffer_size, const char *format, va_list args) +{ + return vsnprintf(buffer, buffer_size, format, args); +} + +int ext_snpintf(char *buffer, size_t buffer_size, const char *format, ...) +{ + va_list args; + va_start(args, format); + int res_len = vsnprintf(buffer, buffer_size, format, args); + va_end(args); + return res_len; + +} + +int ext_format_sprintf(char * buffer, size_t buffer_size, const char * format, + const char * log_level_name, const char * truncated_name, int lineno, + kaa_error_t error_code) +{ + + return snprintf(buffer, buffer_size, format, 0, + 0, 0, 0, 0, 0, + log_level_name, truncated_name, lineno, error_code); +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/defaults.h b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/defaults.h new file mode 100644 index 0000000000..6195c38e47 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/defaults.h @@ -0,0 +1,30 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ESP8266_DEFAULTS_H_ +#define ESP8266_DEFAULTS_H_ + +#define KAA_TCP_CHANNEL_IN_BUFFER_SIZE 246 +#define KAA_TCP_CHANNEL_OUT_BUFFER_SIZE 1015 + +#define KAA_TCP_CHANNEL_MAX_TIMEOUT 200u +#define KAA_TCP_CHANNEL_PING_TIMEOUT (KAA_TCP_CHANNEL_MAX_TIMEOUT / 2) + +#define KAATCP_PARSER_MAX_MESSAGE_LENGTH 999 + +#define KAA_MAX_LOG_MESSAGE_LENGTH 247 + +#endif /* ESP8266_DEFAULTS_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/mem.h b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/mem.h new file mode 100644 index 0000000000..a26d604508 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/mem.h @@ -0,0 +1,43 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ESP8266_MEM_H +#define ESP8266_MEM_H + +#include + +extern void *pvPortMalloc(size_t xSize); +extern void *pvPortZalloc(size_t xSize); +extern void *pvPortRealloc(void *pvPtr, size_t xSize); +extern void vPortFree(void *pvPtr); + +#ifndef __KAA_MALLOC +#define __KAA_MALLOC(S) pvPortMalloc(S) +#endif /* __KAA_MALLOC */ + +#ifndef __KAA_CALLOC +#define __KAA_CALLOC(S, N) pvPortZalloc((S)*(N)) +#endif /* __KAA_CALLOC */ + +#ifndef __KAA_REALLOC +#define __KAA_REALLOC(P, S) pvPortRealloc(P, S) +#endif /* __KAA_REALLOC */ + +#ifndef __KAA_FREE +#define __KAA_FREE(P) vPortFree(P) +#endif /* __KAA_FREE */ + +#endif /* ESP8266_MEM_H */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/sock.h b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/sock.h new file mode 100644 index 0000000000..af02dc4ea9 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/sock.h @@ -0,0 +1,36 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ESP8266_SOCK_H +#define ESP8266_SOCK_H + +#include + +typedef int kaa_fd_t; + +typedef struct sockaddr kaa_sockaddr_t; +typedef struct sockaddr_in kaa_sockaddr_storage_t; +typedef socklen_t kaa_socklen_t; + + +#define KAA_HTONS(a) htons((a)) +#define KAA_HTONL(a) htonl((a)) +#define KAA_HTONLL(a) htonll((a)) + +#define KAA_NTOHS(a) ntohs((a)) +#define KAA_NTOHL(a) ntohl((a)) +#define KAA_NTOHLL(a) ntohll((a)) +#endif /* ESP8266_SOCK_H */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/stdio.h b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/stdio.h new file mode 100644 index 0000000000..99f391bb86 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/stdio.h @@ -0,0 +1,24 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ESP8266_STDIO_H +#define ESP8266_STDIO_H + +#include +#undef stdout +#define stdout 0 + +#endif /* ESP8266_STDIO_H */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/time.h b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/time.h new file mode 100644 index 0000000000..8106d21b1a --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/platform/time.h @@ -0,0 +1,26 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ESP8266_TIME_H +#define ESP8266_TIME_H + +typedef unsigned int kaa_time_t; + +kaa_time_t kaa_esp8266_get_time(void); + +#define KAA_TIME() kaa_esp8266_get_time() + +#endif /* ESP8266_TIME_H */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/status.c b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/status.c new file mode 100644 index 0000000000..b6870dbb80 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/status.c @@ -0,0 +1,32 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "platform/ext_status.h" + +void ext_status_read(char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ + (void)buffer; + (void)buffer_size; + (void)needs_deallocation; +} + +void ext_status_store(const char *buffer, size_t buffer_size) +{ + (void)buffer; + (void)buffer_size; +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/tcp_utils.c b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/tcp_utils.c new file mode 100644 index 0000000000..2d3e0321f2 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/tcp_utils.c @@ -0,0 +1,159 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include +#include +#include "kaa_common.h" + +#include + +kaa_error_t ext_tcp_utils_open_tcp_socket(kaa_fd_t *fd, + const kaa_sockaddr_t *destination, + kaa_socklen_t destination_size) +{ + KAA_RETURN_IF_NIL3(fd, destination, destination_size, KAA_ERR_BADPARAM); + kaa_fd_t sock = socket(destination->sa_family, SOCK_STREAM, 0); + + int flags = lwip_fcntl(sock, F_GETFL, 0); + if (flags < 0) { + ext_tcp_utils_tcp_socket_close(sock); + printf("Error getting sicket access flags\n"); + return KAA_ERR_SOCKET_ERROR; + } + + if (lwip_fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { + ext_tcp_utils_tcp_socket_close(sock); + printf("Error setting socket flags. fcntl(F_GETFL): %d\n", lwip_fcntl(sock, F_GETFL, 0)); + return KAA_ERR_SOCKET_ERROR; + } + + if (connect(sock, destination, destination_size) && errno != EINPROGRESS) { + ext_tcp_utils_tcp_socket_close(sock); + printf("Error connecting to destination\n"); + return KAA_ERR_SOCKET_CONNECT_ERROR; + } + + *fd = sock; + + return KAA_ERR_NONE; +} + +ext_tcp_utils_function_return_state_t ext_tcp_utils_getaddrbyhost(kaa_dns_resolve_listener_t *resolve_listener + , const kaa_dns_resolve_info_t *resolve_props + , kaa_sockaddr_t *result + , kaa_socklen_t *result_size) +{ + (void)resolve_listener; + KAA_RETURN_IF_NIL4(resolve_props, resolve_props->hostname, result, result_size, RET_STATE_VALUE_ERROR); + if (*result_size < sizeof(struct sockaddr_in)) + return RET_STATE_BUFFER_NOT_ENOUGH; + + struct addrinfo hints; + memset(&hints, 0 , sizeof(struct addrinfo)); + hints.ai_socktype = SOCK_STREAM; + /* LWIP does not support ipv6, so sockaddr_in6 is not even defined in lwip/sockets.h */ + /* if (*result_size < sizeof(struct sockaddr_in6)) */ + hints.ai_family = AF_INET; + + char hostname_str[resolve_props->hostname_length + 1]; + memcpy(hostname_str, resolve_props->hostname, resolve_props->hostname_length); + hostname_str[resolve_props->hostname_length] = '\0'; + + struct addrinfo *resolve_result = NULL; + int resolve_error = 0; + + if (resolve_props->port) { + char port_str[6]; + snprintf(port_str, 6, "%u", resolve_props->port); + resolve_error = getaddrinfo(hostname_str, port_str, &hints, &resolve_result); + } else { + resolve_error = getaddrinfo(hostname_str, NULL, &hints, &resolve_result); + } + + if (resolve_error || !resolve_result) + return RET_STATE_VALUE_ERROR; + + if (resolve_result->ai_addrlen > *result_size) { + freeaddrinfo(resolve_result); + return RET_STATE_BUFFER_NOT_ENOUGH; + } + + memcpy(result, resolve_result->ai_addr, resolve_result->ai_addrlen); + *result_size = resolve_result->ai_addrlen; + freeaddrinfo(resolve_result); + return RET_STATE_VALUE_READY; +} + +ext_tcp_socket_state_t ext_tcp_utils_tcp_socket_check(kaa_fd_t fd + , const kaa_sockaddr_t *destination + , kaa_socklen_t destination_size) +{ + if (connect(fd, destination, destination_size) < 0 ) { + switch (errno) { + case EINPROGRESS: + case EALREADY: + return KAA_TCP_SOCK_CONNECTING; + case EISCONN: + return KAA_TCP_SOCK_CONNECTED; + default: + return KAA_TCP_SOCK_ERROR; + } + } + return KAA_TCP_SOCK_CONNECTED; +} + +ext_tcp_socket_io_errors_t ext_tcp_utils_tcp_socket_write(kaa_fd_t fd + , const char *buffer + , size_t buffer_size + , size_t *bytes_written) +{ + KAA_RETURN_IF_NIL2(buffer, buffer_size, KAA_TCP_SOCK_IO_ERROR); + ssize_t write_result = write(fd, buffer, buffer_size); + if (write_result < 0 && errno != EAGAIN) + return KAA_TCP_SOCK_IO_ERROR; + if (bytes_written) + *bytes_written = (write_result > 0) ? write_result : 0; + return KAA_TCP_SOCK_IO_OK; +} + + + +ext_tcp_socket_io_errors_t ext_tcp_utils_tcp_socket_read(kaa_fd_t fd + , char *buffer + , size_t buffer_size + , size_t *bytes_read) +{ + KAA_RETURN_IF_NIL2(buffer, buffer_size, KAA_TCP_SOCK_IO_ERROR); + ssize_t read_result = read(fd, buffer, buffer_size); + if (!read_result) + return KAA_TCP_SOCK_IO_EOF; + if (read_result < 0 && errno != EAGAIN) + return KAA_TCP_SOCK_IO_ERROR; + if (bytes_read) + *bytes_read = (read_result > 0) ? read_result : 0; + return KAA_TCP_SOCK_IO_OK; +} + +kaa_error_t ext_tcp_utils_tcp_socket_close(kaa_fd_t fd) +{ + return (close(fd) < 0) ? KAA_ERR_SOCKET_ERROR : KAA_ERR_NONE; +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/esp8266/time.c b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/time.c new file mode 100644 index 0000000000..126656d31a --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/esp8266/time.c @@ -0,0 +1,24 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +kaa_time_t kaa_esp8266_get_time(void) +{ + return (system_get_time() / 1000000); +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/configuration_persistence.c b/client/client-multi/client-c/src/kaa/platform-impl/posix/configuration_persistence.c new file mode 100644 index 0000000000..91643b3734 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/configuration_persistence.c @@ -0,0 +1,41 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include + +#define KAA_CONFIGURATION_STORAGE "kaa_configuration.bin" + +__attribute__((weak)) +void ext_configuration_read(char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ + posix_binary_file_read(KAA_CONFIGURATION_STORAGE, buffer, buffer_size, needs_deallocation); +} + +__attribute__((weak)) +void ext_configuration_store(const char *buffer, size_t buffer_size) +{ + posix_binary_file_store(KAA_CONFIGURATION_STORAGE, buffer, buffer_size); +} + +__attribute__((weak)) +void ext_configuration_delete(void) +{ + posix_binary_file_delete(KAA_CONFIGURATION_STORAGE); +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/file_utils.c b/client/client-multi/client-c/src/kaa/platform-impl/posix/file_utils.c new file mode 100644 index 0000000000..535d0bf025 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/file_utils.c @@ -0,0 +1,81 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "utilities/kaa_mem.h" +#include "kaa_common.h" + + +int posix_binary_file_read(const char *file_name, char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ + KAA_RETURN_IF_NIL4(file_name, buffer, buffer_size, needs_deallocation, -1); + *buffer = NULL; + *buffer_size = 0; + *needs_deallocation = true; + + FILE* file = fopen(file_name, "rb"); + KAA_RETURN_IF_NIL(file, -1); + + fseek(file, 0, SEEK_END); + long result_size = ftell(file); + if (result_size <= 0) { + fclose(file); + return -1; + } + char *result_buffer = KAA_MALLOC(result_size * sizeof(char)); + if (!result_buffer) { + fclose(file); + return -1; + } + + fseek(file, 0, SEEK_SET); + if (fread(result_buffer, result_size, 1, file) == 0) { + KAA_FREE(result_buffer); + fclose(file); + return -1; + } + + *buffer = result_buffer; + *buffer_size = result_size; + + fclose(file); + return 0; +} + +int posix_binary_file_store(const char *file_name, const char *buffer, size_t buffer_size) +{ + KAA_RETURN_IF_NIL3(file_name, buffer, buffer_size, -1); + + FILE* status_file = fopen(file_name, "wb"); + if (status_file) { + fwrite(buffer, buffer_size, 1, status_file); + fclose(status_file); + return 0; + } + return -1; +} + +int posix_binary_file_delete(const char *file_name) +{ + FILE *file = fopen(file_name, "r"); + if (file) { + fclose(file); + return remove(file_name); + } + return -1; +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/kaa_client.c b/client/client-multi/client-c/src/kaa/platform-impl/posix/kaa_client.c new file mode 100644 index 0000000000..240b3ee48b --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/kaa_client.c @@ -0,0 +1,474 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kaa.h" +#include "kaa_error.h" +#include "kaa_common.h" +#include "utilities/kaa_mem.h" +#include "utilities/kaa_log.h" +#include +#include "kaa_context.h" +#include +#include "platform/ext_transport_channel.h" +#include "platform-impl/common/kaa_tcp_channel.h" +#include "platform-impl/common/ext_log_upload_strategies.h" +#include +#include "kaa_channel_manager.h" +#include "kaa_platform_utils.h" + +#ifndef KAA_DISABLE_FEATURE_LOGGING +#include "kaa_logging.h" +#include "kaa_logging_private.h" +#endif + +#include "kaa_private.h" + +static kaa_extension_id BOOTSTRAP_SERVICE[] = { KAA_EXTENSION_BOOTSTRAP }; +static const int BOOTSTRAP_SERVICE_COUNT = sizeof(BOOTSTRAP_SERVICE) / sizeof(kaa_extension_id); + +static kaa_extension_id OPERATIONS_SERVICES[] = { KAA_EXTENSION_PROFILE + , KAA_EXTENSION_USER +#ifndef KAA_DISABLE_FEATURE_CONFIGURATION + , KAA_EXTENSION_CONFIGURATION +#endif +#ifndef KAA_DISABLE_FEATURE_EVENTS + , KAA_EXTENSION_EVENT +#endif +#ifndef KAA_DISABLE_FEATURE_LOGGING + , KAA_EXTENSION_LOGGING +#endif +#ifndef KAA_DISABLE_FEATURE_NOTIFICATION + , KAA_EXTENSION_NOTIFICATION +#endif + }; +static const int OPERATIONS_SERVICES_COUNT = sizeof(OPERATIONS_SERVICES) / sizeof(kaa_extension_id); + + +/* Logging constraints */ +#define MAX_LOG_COUNT SIZE_MAX +#define MAX_LOG_BUCKET_SIZE (KAA_TCP_CHANNEL_OUT_BUFFER_SIZE >> 3) + +KAA_STATIC_ASSERT(log_bucket_size_is_not_zero, MAX_LOG_BUCKET_SIZE != 0); + +typedef enum { + KAA_CLIENT_CHANNEL_STATE_NOT_CONNECTED = 0, + KAA_CLIENT_CHANNEL_STATE_CONNECTED +} kaa_client_channel_state_t; + +typedef enum { + KAA_CLIENT_CHANNEL_TYPE_BOOTSTRAP = 0, + KAA_CLIENT_CHANNEL_TYPE_OPERATIONS +} kaa_client_channel_type_t; + +struct kaa_client_t { + kaa_context_t *kaa_context; + + bool operate; + + kaa_transport_channel_interface_t channel; + kaa_client_channel_state_t channel_state; + uint32_t channel_id; + bool channel_socket_closed; + + bool boostrap_complete; + + external_process_fn external_process_fn; + void *external_process_context; + time_t external_process_max_delay; + time_t external_process_last_call; + +#ifndef KAA_DISABLE_FEATURE_LOGGING + void *log_storage_context; + void *log_upload_strategy_context; +#endif +}; + + + +static kaa_error_t kaa_client_init_channel(kaa_client_t *kaa_client, kaa_client_channel_type_t channel_type); +static kaa_error_t kaa_client_deinit_channel(kaa_client_t *kaa_client); +static kaa_error_t on_kaa_tcp_channel_event(void *context, kaa_tcp_channel_event_t event_type, kaa_fd_t fd); + +#ifndef KAA_DISABLE_FEATURE_LOGGING +kaa_error_t kaa_log_collector_init(kaa_client_t *kaa_client); +#endif + +kaa_error_t on_kaa_tcp_channel_event(void *context, kaa_tcp_channel_event_t event_type, kaa_fd_t fd) +{ + (void)fd; + KAA_RETURN_IF_NIL(context, KAA_ERR_BADPARAM); + + if (event_type == SOCKET_DISCONNECTED) { + ((kaa_client_t *)context)->channel_socket_closed = true; + } + + return KAA_ERR_NONE; +} + +kaa_error_t kaa_client_create(kaa_client_t **kaa_client, kaa_client_props_t *props) +{ + (void)props; + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_client_t *self = KAA_CALLOC(1, sizeof(kaa_client_t)); + KAA_RETURN_IF_NIL(self, KAA_ERR_NOMEM); + + kaa_error_t error_code = kaa_init(&self->kaa_context); + if (error_code) { + kaa_client_destroy(self); + return error_code; + } + + self->operate = true; + +#ifndef KAA_DISABLE_FEATURE_LOGGING + error_code = kaa_log_collector_init(self); + if (error_code) { + KAA_LOG_ERROR(self->kaa_context->logger, error_code, "Failed to init Kaa log collector, error %d", error_code); + kaa_client_destroy(self); + return error_code; + } +#endif + + KAA_LOG_INFO(self->kaa_context->logger, KAA_ERR_NONE, "Kaa client created"); + + *kaa_client = self; + return error_code; +} + +void kaa_client_destroy(kaa_client_t *self) +{ + KAA_RETURN_IF_NIL(self, ); + + if (self->kaa_context) { + kaa_deinit(self->kaa_context); + } + + KAA_FREE(self); +} + +kaa_context_t* kaa_client_get_context(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, NULL); + return kaa_client->kaa_context; +} + +static uint16_t get_poll_timeout(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, 0); + + uint16_t select_timeout; + kaa_tcp_channel_get_max_timeout(&kaa_client->channel, &select_timeout); + + + if ((kaa_client->external_process_max_delay > 0) && (select_timeout > kaa_client->external_process_max_delay)) { + select_timeout = kaa_client->external_process_max_delay; + } + + if ((KAA_BOOTSTRAP_RESPONSE_PERIOD > 0) && (select_timeout > KAA_BOOTSTRAP_RESPONSE_PERIOD)) { + select_timeout = KAA_BOOTSTRAP_RESPONSE_PERIOD; + } + + return select_timeout; +} + +kaa_error_t kaa_client_process_channel_connected(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_error_t error_code = KAA_ERR_NONE; + + fd_set read_fds, write_fds, except_fds; + struct timeval select_tv = { get_poll_timeout(kaa_client), 0 }; + int channel_fd = 0; + + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + FD_ZERO(&except_fds); + + kaa_tcp_channel_get_descriptor(&kaa_client->channel, &channel_fd); + + if (kaa_tcp_channel_is_ready(&kaa_client->channel, FD_READ)) + FD_SET(channel_fd, &read_fds); + + if (kaa_tcp_channel_is_ready(&kaa_client->channel, FD_WRITE)) + FD_SET(channel_fd, &write_fds); + + int poll_result = select(channel_fd + 1, &read_fds, &write_fds, NULL, &select_tv); + if (poll_result == 0) { + error_code = kaa_tcp_channel_check_keepalive(&kaa_client->channel); + } else if (poll_result > 0) { + if (channel_fd >= 0) { + if (FD_ISSET(channel_fd, &read_fds)) { + KAA_LOG_TRACE(kaa_client->kaa_context->logger, KAA_ERR_NONE, + "Processing IN event for the client socket %d", channel_fd); + error_code = kaa_tcp_channel_process_event(&kaa_client->channel, FD_READ); + if (error_code) { + KAA_LOG_ERROR(kaa_client->kaa_context->logger, error_code, + "Failed to process IN event for the client socket %d", channel_fd); + } + } + if (FD_ISSET(channel_fd, &write_fds)) { + KAA_LOG_TRACE(kaa_client->kaa_context->logger, KAA_ERR_NONE, + "Processing OUT event for the client socket %d", channel_fd); + + error_code = kaa_tcp_channel_process_event(&kaa_client->channel, FD_WRITE); + if (error_code) { + KAA_LOG_ERROR(kaa_client->kaa_context->logger, error_code, + "Failed to process OUT event for the client socket %d", channel_fd); + } + } + } + } else { + KAA_LOG_ERROR(kaa_client->kaa_context->logger, KAA_ERR_BAD_STATE, "Failed to poll descriptors: %s", strerror(errno)); + error_code = KAA_ERR_BAD_STATE; + } + + if (kaa_client->channel_socket_closed) { + KAA_LOG_INFO(kaa_client->kaa_context->logger, KAA_ERR_NONE, + "Channel [0x%08X] connection terminated", kaa_client->channel_id); + + kaa_client->channel_state = KAA_CLIENT_CHANNEL_STATE_NOT_CONNECTED; + if (error_code != KAA_ERR_EVENT_NOT_ATTACHED) { + kaa_client_deinit_channel(kaa_client); + } + } + + return error_code; +} + +kaa_error_t kaa_client_process_channel_disconnected(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_error_t error_code = kaa_tcp_channel_check_keepalive(&kaa_client->channel); + if (error_code) { + KAA_LOG_ERROR(kaa_client->kaa_context->logger, error_code, "Failed to connect channel [0x%08X]", kaa_client->channel_id); + } else { + KAA_LOG_TRACE(kaa_client->kaa_context->logger, error_code, "Channel [0x%08X] successfully connected", kaa_client->channel_id); + kaa_client->channel_state = KAA_CLIENT_CHANNEL_STATE_CONNECTED; + } + + return error_code; +} + +kaa_error_t kaa_client_start(kaa_client_t *kaa_client + , external_process_fn external_process + , void *external_process_context + , kaa_time_t max_delay) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_error_t error_code = kaa_check_readiness(kaa_client->kaa_context); + if (error_code != KAA_ERR_NONE) { + KAA_LOG_ERROR(kaa_client->kaa_context->logger, error_code, "Cannot start Kaa client: Kaa context is not fully initialized"); + return error_code; + } + + kaa_client->external_process_fn = external_process; + kaa_client->external_process_context = external_process_context; + kaa_client->external_process_max_delay = max_delay; + kaa_client->external_process_last_call = KAA_TIME(); + + KAA_LOG_INFO(kaa_client->kaa_context->logger, KAA_ERR_NONE, "Starting Kaa client..."); + + while (kaa_client->operate) { + if (kaa_client->external_process_fn) { + if ((KAA_TIME() - kaa_client->external_process_last_call) >= kaa_client->external_process_max_delay) { + kaa_client->external_process_fn(kaa_client->external_process_context); + } + kaa_client->external_process_last_call = KAA_TIME(); + } + + //Check Kaa channel is ready to transmit something + if (kaa_process_failover(kaa_client->kaa_context)) { + kaa_client->boostrap_complete = false; + } else { + if (kaa_client->channel_id > 0) { + if (kaa_client->channel_state == KAA_CLIENT_CHANNEL_STATE_NOT_CONNECTED) { + error_code = kaa_client_process_channel_disconnected(kaa_client); + } else if (kaa_client->channel_state == KAA_CLIENT_CHANNEL_STATE_CONNECTED) { + error_code = kaa_client_process_channel_connected(kaa_client); + if (error_code == KAA_ERR_TIMEOUT) + kaa_client_deinit_channel(kaa_client); + } + } else { + //No initialized channels + if (kaa_client->boostrap_complete) { + KAA_LOG_INFO(kaa_client->kaa_context->logger, KAA_ERR_NONE, + "Channel [0x%08X] Boostrap complete, reinitializing to Operations ...", kaa_client->channel_id); + kaa_client->boostrap_complete = false; + kaa_client_deinit_channel(kaa_client); + error_code = kaa_client_init_channel(kaa_client, KAA_CLIENT_CHANNEL_TYPE_OPERATIONS); + if (error_code == KAA_ERR_BAD_STATE) { + kaa_client_deinit_channel(kaa_client); + kaa_client->boostrap_complete = false; + } + } else { + KAA_LOG_INFO(kaa_client->kaa_context->logger, KAA_ERR_NONE, + "Channel [0x%08X] Operations error, reinitializing to Bootstrap ...", kaa_client->channel_id); + kaa_client->boostrap_complete = true; + kaa_client_deinit_channel(kaa_client); + kaa_client_init_channel(kaa_client, KAA_CLIENT_CHANNEL_TYPE_BOOTSTRAP); + } + } + } +#ifndef KAA_DISABLE_FEATURE_LOGGING + ext_log_upload_timeout(kaa_client->kaa_context->log_collector); +#endif + } + KAA_LOG_INFO(kaa_client->kaa_context->logger, KAA_ERR_NONE, "Kaa client stopped"); + + return error_code; +} + +kaa_error_t kaa_client_stop(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + KAA_LOG_TRACE(kaa_client->kaa_context->logger, KAA_ERR_NONE, "Going to stop Kaa client..."); + kaa_client->operate = false; + return kaa_stop(kaa_client->kaa_context); +} + +kaa_error_t kaa_client_init_channel(kaa_client_t *kaa_client, kaa_client_channel_type_t channel_type) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_error_t error_code = KAA_ERR_NONE; + + KAA_LOG_TRACE(kaa_client->kaa_context->logger, KAA_ERR_NONE, "Initializing channel...."); + + switch (channel_type) { + case KAA_CLIENT_CHANNEL_TYPE_BOOTSTRAP: + error_code = kaa_tcp_channel_create(&kaa_client->channel + , kaa_client->kaa_context->logger + , BOOTSTRAP_SERVICE + , BOOTSTRAP_SERVICE_COUNT); + break; + case KAA_CLIENT_CHANNEL_TYPE_OPERATIONS: + error_code = kaa_tcp_channel_create(&kaa_client->channel + , kaa_client->kaa_context->logger + , OPERATIONS_SERVICES + , OPERATIONS_SERVICES_COUNT); + break; + } + + if (error_code) { + KAA_LOG_ERROR(kaa_client->kaa_context->logger, error_code, "Failed to create transport channel, type %d", channel_type); + return error_code; + } + + error_code = kaa_tcp_channel_set_socket_events_callback(&kaa_client->channel, &on_kaa_tcp_channel_event, kaa_client); + if (error_code) { + KAA_LOG_ERROR(kaa_client->kaa_context->logger, error_code, + "Failed to set socket events callback, channel type %d", channel_type); + return error_code; + } + + error_code = kaa_channel_manager_add_transport_channel(kaa_client->kaa_context->channel_manager + , &kaa_client->channel + , &kaa_client->channel_id); + if (error_code) { + KAA_LOG_WARN(kaa_client->kaa_context->logger, error_code, "Failed to %s channel, type %d", + error_code == KAA_ERR_BAD_STATE ? "initialize" : "add", channel_type); + + return error_code; + } + + KAA_LOG_TRACE(kaa_client->kaa_context->logger, KAA_ERR_NONE, "Channel [0x%08X] initialized successfully (type %d)" + , kaa_client->channel_id, channel_type); + + return error_code; +} + +kaa_error_t kaa_client_deinit_channel(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_error_t error_code = KAA_ERR_NONE; + + if (kaa_client->channel_id == 0) + return KAA_ERR_NONE; + + KAA_LOG_TRACE(kaa_client->kaa_context->logger, KAA_ERR_NONE, "Deinitializing channel...."); + + error_code = kaa_channel_manager_remove_transport_channel(kaa_client->kaa_context->channel_manager,kaa_client->channel_id); + if (error_code) { + KAA_LOG_TRACE(kaa_client->kaa_context->logger, error_code, "Bootstrap channel error removing from channel manager"); + return error_code; + } + + kaa_client->channel_id = 0; + kaa_client->channel_socket_closed = false; + kaa_client->channel.context = NULL; + kaa_client->channel.destroy = NULL; + kaa_client->channel.get_protocol_id = NULL; + kaa_client->channel.get_supported_services = NULL; + kaa_client->channel.init = NULL; + kaa_client->channel.set_access_point = NULL; + kaa_client->channel.sync_handler = NULL; + + KAA_LOG_TRACE(kaa_client->kaa_context->logger, KAA_ERR_NONE, "Channel deinitialized successfully"); + + return error_code; +} + +#ifndef KAA_DISABLE_FEATURE_LOGGING +kaa_error_t kaa_log_collector_init(kaa_client_t *kaa_client) +{ + KAA_RETURN_IF_NIL(kaa_client, KAA_ERR_BADPARAM); + + kaa_error_t error_code = ext_unlimited_log_storage_create(&kaa_client->log_storage_context + , kaa_client->kaa_context->logger); + if (error_code) { + KAA_LOG_ERROR(kaa_client->kaa_context->logger, error_code, "Failed to create log storage"); + return error_code; + } + + error_code = ext_log_upload_strategy_create(kaa_client->kaa_context, &kaa_client->log_upload_strategy_context, KAA_LOG_UPLOAD_VOLUME_STRATEGY); + if (error_code) { + KAA_LOG_ERROR(kaa_client->kaa_context->logger, error_code, "Failed to create log upload strategy"); + return error_code; + } + + kaa_log_bucket_constraints_t bucket_sizes = { + .max_bucket_size = MAX_LOG_BUCKET_SIZE, + .max_bucket_log_count = MAX_LOG_COUNT, + }; + + error_code = kaa_logging_init(kaa_client->kaa_context->log_collector + , kaa_client->log_storage_context + , kaa_client->log_upload_strategy_context + , &bucket_sizes); + if (error_code) { + KAA_LOG_ERROR(kaa_client->kaa_context->logger, error_code,"Failed to init log collector"); + return error_code; + } + + KAA_LOG_INFO(kaa_client->kaa_context->logger, KAA_ERR_NONE, "Log collector init completed"); + return error_code; +} +#endif diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/key_utils.c b/client/client-multi/client-c/src/kaa/platform-impl/posix/key_utils.c new file mode 100644 index 0000000000..1652aa7db5 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/key_utils.c @@ -0,0 +1,83 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include "platform/ext_key_utils.h" +#include "utilities/kaa_mem.h" +#include "kaa_common.h" +#include + + +#define KAA_KEY_STORAGE "kaa_key.pub" + +static char *kaa_public_key = NULL; +static size_t kaa_public_key_length = 0; + +static void kaa_generate_pub_key(void) +{ + const int kBits = 2048; + const int kExp = 65537; + + RSA *rsa = RSA_generate_key(kBits, kExp, 0, 0); + + BIO *bio_pem = BIO_new(BIO_s_mem()); + i2d_RSA_PUBKEY_bio(bio_pem, rsa); + + kaa_public_key_length = BIO_pending(bio_pem); + kaa_public_key = (char *) KAA_MALLOC(kaa_public_key_length); + if (!kaa_public_key) { + kaa_public_key_length = 0; + BIO_free_all(bio_pem); + RSA_free(rsa); + return; + } + BIO_read(bio_pem, kaa_public_key, kaa_public_key_length); + + BIO_free_all(bio_pem); + RSA_free(rsa); +} + +static int kaa_init_key(void) +{ + struct stat stat_result; + int key_result = stat(KAA_KEY_STORAGE, &stat_result); + + if (!key_result) { + bool need_dealloc = false; + posix_binary_file_read(KAA_KEY_STORAGE, &kaa_public_key, &kaa_public_key_length, &need_dealloc); + } else { + kaa_generate_pub_key(); + posix_binary_file_store(KAA_KEY_STORAGE, kaa_public_key, kaa_public_key_length); + } + + return 0; +} + +void ext_get_endpoint_public_key(char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ + KAA_RETURN_IF_NIL3(buffer, buffer_size, needs_deallocation,); + if (!kaa_public_key) + kaa_init_key(); + *buffer = kaa_public_key; + *buffer_size = kaa_public_key_length; + *needs_deallocation = false; +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/defaults.h b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/defaults.h new file mode 100644 index 0000000000..b012230ecb --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/defaults.h @@ -0,0 +1,36 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +@file posix_defaults.h + Created on: Mar 26, 2015 + Author: Andriy Panasenko +*/ + +#ifndef POSIX_DEFAULTS_H_ +#define POSIX_DEFAULTS_H_ + +#define KAA_TCP_CHANNEL_IN_BUFFER_SIZE 2048 +#define KAA_TCP_CHANNEL_OUT_BUFFER_SIZE 8192 + +#define KAA_TCP_CHANNEL_MAX_TIMEOUT 200u +#define KAA_TCP_CHANNEL_PING_TIMEOUT (KAA_TCP_CHANNEL_MAX_TIMEOUT / 2) + +#define KAATCP_PARSER_MAX_MESSAGE_LENGTH 1024 * 1024 + +#define KAA_MAX_LOG_MESSAGE_LENGTH 512 + +#endif /* POSIX_DEFAULTS_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/file_utils.h b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/file_utils.h new file mode 100644 index 0000000000..cf0997cc52 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/file_utils.h @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef POSIX_FILE_UTILS_H_ +#define POSIX_FILE_UTILS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int posix_binary_file_read(const char *file_name, char **buffer, size_t *buffer_size, bool *needs_deallocation); + + +int posix_binary_file_store(const char *file_name, const char *buffer, size_t buffer_size); + + +int posix_binary_file_delete(const char *file_name); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* POSIX_FILE_UTILS_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/mem.h b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/mem.h new file mode 100644 index 0000000000..8daeb4f762 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/mem.h @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_KAA_PLATFORM_IMPL_POSIX_MEM_H_ +#define SRC_KAA_PLATFORM_IMPL_POSIX_MEM_H_ + +#include + +#ifndef __KAA_MALLOC +#define __KAA_MALLOC(S) malloc(S) +#endif + +#ifndef __KAA_CALLOC +#define __KAA_CALLOC(N,S) calloc(N, S) +#endif + +#ifndef __KAA_REALLOC +#define __KAA_REALLOC(P, S) realloc(P, S) +#endif + +#ifndef __KAA_FREE +#define __KAA_FREE(P) free(P) +#endif + +#endif /* SRC_KAA_PLATFORM_IMPL_POSIX_MEM_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/sock.h b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/sock.h new file mode 100644 index 0000000000..10530e1aab --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/sock.h @@ -0,0 +1,46 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +posix_sock.h + Created on: Jan 15, 2015 + Author: Andriy Panasenko +*/ + +#ifndef POSIX_SOCK_H_ +#define POSIX_SOCK_H_ + +#include +#include +#include + +#include "platform-impl/common/kaa_htonll.h" + +typedef int kaa_fd_t; + +typedef struct sockaddr kaa_sockaddr_t; +typedef struct sockaddr_storage kaa_sockaddr_storage_t; +typedef socklen_t kaa_socklen_t; + +#define KAA_HTONS(hostshort) htons((hostshort)) +#define KAA_HTONL(hostlong) htonl((hostlong)) +#define KAA_HTONLL(hostlonglong) htonll((hostlonglong)) + +#define KAA_NTOHS(netshort) ntohs((netshort)) +#define KAA_NTOHL(netlong) ntohl((netlong)) +#define KAA_NTOHLL(netlonglong) ntohll((netlonglong)) + +#endif /* POSIX_SOCK_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/stdio.h b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/stdio.h new file mode 100644 index 0000000000..cb41f3325e --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/stdio.h @@ -0,0 +1,28 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +posix_stdio.h + Created on: Jan 15, 2015 + Author: Andriy Panasenko +*/ + +#ifndef POSIX_STDIO_H_ +#define POSIX_STDIO_H_ + +#include + +#endif /* POSIX_STDIO_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/time.h b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/time.h new file mode 100644 index 0000000000..3d10e1de43 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/platform/time.h @@ -0,0 +1,27 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef POSIX_TIME_H_ +#define POSIX_TIME_H_ + +#include + +typedef time_t kaa_time_t; + +#define KAA_TIME() (kaa_time_t)time(NULL) + +#endif /* POSIX_TIME_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/status.c b/client/client-multi/client-c/src/kaa/platform-impl/posix/status.c new file mode 100644 index 0000000000..373cf9341c --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/status.c @@ -0,0 +1,32 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "platform/ext_status.h" +#include + +#define KAA_STATUS_STORAGE "kaa_status.bin" + +void ext_status_read(char **buffer, size_t *buffer_size, bool *needs_deallocation) +{ + posix_binary_file_read(KAA_STATUS_STORAGE, buffer, buffer_size, needs_deallocation); +} + +void ext_status_store(const char *buffer, size_t buffer_size) +{ + posix_binary_file_store(KAA_STATUS_STORAGE, buffer, buffer_size); +} diff --git a/client/client-multi/client-c/src/kaa/platform-impl/posix/tcp_utils.c b/client/client-multi/client-c/src/kaa/platform-impl/posix/tcp_utils.c new file mode 100644 index 0000000000..2bb02fe1f2 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform-impl/posix/tcp_utils.c @@ -0,0 +1,192 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// See feature_test_macros(7) man page +#define _POSIX_C_SOURCE 200112L + +#include "platform/ext_tcp_utils.h" +#include +#include "kaa_common.h" +#include +#include +#include +#include +#include +#include +#include +#include + + + +kaa_error_t ext_tcp_utils_set_sockaddr_port(kaa_sockaddr_t *addr, uint16_t port) +{ + KAA_RETURN_IF_NIL2(addr, port, KAA_ERR_BADPARAM); + switch (addr->sa_family) { + case AF_INET: { + struct sockaddr_in *s_in = (struct sockaddr_in *) addr; + s_in->sin_port = KAA_HTONS(port); + break; + } + case AF_INET6: { + struct sockaddr_in6 *s_in6 = (struct sockaddr_in6 *) addr; + s_in6->sin6_port = KAA_HTONS(port); + break; + } + default: + return KAA_ERR_SOCKET_INVALID_FAMILY; + } + return KAA_ERR_NONE; +} + + + +ext_tcp_utils_function_return_state_t ext_tcp_utils_getaddrbyhost(kaa_dns_resolve_listener_t *resolve_listener + , const kaa_dns_resolve_info_t *resolve_props + , kaa_sockaddr_t *result + , kaa_socklen_t *result_size) +{ + (void)resolve_listener; + KAA_RETURN_IF_NIL4(resolve_props, resolve_props->hostname, result, result_size, RET_STATE_VALUE_ERROR); + if (*result_size < sizeof(struct sockaddr_in)) + return RET_STATE_BUFFER_NOT_ENOUGH; + + struct addrinfo hints; + memset(&hints, 0 , sizeof(struct addrinfo)); + hints.ai_socktype = SOCK_STREAM; + if (*result_size < sizeof(struct sockaddr_in6)) + hints.ai_family = AF_INET; + + char hostname_str[resolve_props->hostname_length + 1]; + memcpy(hostname_str, resolve_props->hostname, resolve_props->hostname_length); + hostname_str[resolve_props->hostname_length] = '\0'; + + struct addrinfo *resolve_result = NULL; + int resolve_error = 0; + + if (resolve_props->port) { + char port_str[6]; + snprintf(port_str, 6, "%u", resolve_props->port); + resolve_error = getaddrinfo(hostname_str, port_str, &hints, &resolve_result); + } else { + resolve_error = getaddrinfo(hostname_str, NULL, &hints, &resolve_result); + } + + if (resolve_error || !resolve_result) + return RET_STATE_VALUE_ERROR; + + if (resolve_result->ai_addrlen > *result_size) { + freeaddrinfo(resolve_result); + return RET_STATE_BUFFER_NOT_ENOUGH; + } + + memcpy(result, resolve_result->ai_addr, resolve_result->ai_addrlen); + *result_size = resolve_result->ai_addrlen; + freeaddrinfo(resolve_result); + return RET_STATE_VALUE_READY; +} + + + +kaa_error_t ext_tcp_utils_open_tcp_socket(kaa_fd_t *fd + , const kaa_sockaddr_t *destination + , kaa_socklen_t destination_size) +{ + KAA_RETURN_IF_NIL3(fd, destination, destination_size, KAA_ERR_BADPARAM); + + kaa_fd_t sock = socket(destination->sa_family, SOCK_STREAM, 0); + if (sock < 0) + return KAA_ERR_SOCKET_ERROR; + + int flags = fcntl(sock, F_GETFL); + if (flags < 0) { + ext_tcp_utils_tcp_socket_close(sock); + return KAA_ERR_SOCKET_ERROR; + } + + if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { + ext_tcp_utils_tcp_socket_close(sock); + return KAA_ERR_SOCKET_ERROR; + } + + if (connect(sock, destination, destination_size) && errno != EINPROGRESS) { + ext_tcp_utils_tcp_socket_close(sock); + return KAA_ERR_SOCKET_CONNECT_ERROR; + } + + *fd = sock; + return KAA_ERR_NONE; +} + + + +ext_tcp_socket_state_t ext_tcp_utils_tcp_socket_check(kaa_fd_t fd + , const kaa_sockaddr_t *destination + , kaa_socklen_t destination_size) +{ + if (connect(fd, destination, destination_size) < 0 ) { + switch (errno) { + case EINPROGRESS: + case EALREADY: + return KAA_TCP_SOCK_CONNECTING; + case EISCONN: + return KAA_TCP_SOCK_CONNECTED; + default: + return KAA_TCP_SOCK_ERROR; + } + } + return KAA_TCP_SOCK_CONNECTED; +} + + + +ext_tcp_socket_io_errors_t ext_tcp_utils_tcp_socket_write(kaa_fd_t fd + , const char *buffer + , size_t buffer_size + , size_t *bytes_written) +{ + KAA_RETURN_IF_NIL2(buffer, buffer_size, KAA_TCP_SOCK_IO_ERROR); + ssize_t write_result = write(fd, buffer, buffer_size); + if (write_result < 0 && errno != EAGAIN) + return KAA_TCP_SOCK_IO_ERROR; + if (bytes_written) + *bytes_written = (write_result > 0) ? write_result : 0; + return KAA_TCP_SOCK_IO_OK; +} + + + +ext_tcp_socket_io_errors_t ext_tcp_utils_tcp_socket_read(kaa_fd_t fd + , char *buffer + , size_t buffer_size + , size_t *bytes_read) +{ + KAA_RETURN_IF_NIL2(buffer, buffer_size, KAA_TCP_SOCK_IO_ERROR); + ssize_t read_result = read(fd, buffer, buffer_size); + if (!read_result) + return KAA_TCP_SOCK_IO_EOF; + if (read_result < 0 && errno != EAGAIN) + return KAA_TCP_SOCK_IO_ERROR; + if (bytes_read) + *bytes_read = (read_result > 0) ? read_result : 0; + return KAA_TCP_SOCK_IO_OK; +} + + + +kaa_error_t ext_tcp_utils_tcp_socket_close(kaa_fd_t fd) +{ + return (close(fd) < 0) ? KAA_ERR_SOCKET_ERROR : KAA_ERR_NONE; +} diff --git a/client/client-multi/client-c/src/kaa/platform/kaa_client_properties.h b/client/client-multi/client-c/src/kaa/platform/kaa_client_properties.h new file mode 100644 index 0000000000..404fce1537 --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform/kaa_client_properties.h @@ -0,0 +1,31 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @file posix_kaa_client_properies.h + * + * Created on: Apr 16, 2015 + * Author: Andriy Panasenko + */ + +#ifndef POSIX_KAA_CLIENT_PROPERIES_H_ +#define POSIX_KAA_CLIENT_PROPERIES_H_ + +typedef struct { + unsigned long max_update_time; +} kaa_client_props_t; + +#endif /* POSIX_KAA_CLIENT_PROPERIES_H_ */ diff --git a/client/client-multi/client-c/src/kaa/platform/kaa_failover_strategy.h b/client/client-multi/client-c/src/kaa/platform/kaa_failover_strategy.h new file mode 100644 index 0000000000..22f51ad00d --- /dev/null +++ b/client/client-multi/client-c/src/kaa/platform/kaa_failover_strategy.h @@ -0,0 +1,34 @@ +/* + * Copyright 2014-2016 CyberVision, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef POSIX_KAA_STRATEGY_H_ +#define POSIX_KAA_STRATEGY_H_ + +#include "platform/ext_kaa_failover_strategy.h" + +/** +* @brief Sets decision that will be made in case of failover. +* +* @param[in] strategy The pointer to the failover strategy instance. +* @param[in] decision The pointer to the the decision structure, filled by user. +* +* @return The error code. +*/ +kaa_error_t kaa_failover_strategy_set(kaa_failover_strategy_t *strategy, + kaa_failover_decision_t *decision); + + +#endif /* POSIX_KAA_STRATEGY_H_ */ diff --git a/client/client-multi/client-cpp/tools/avro-cpp-disable-tests.patch b/client/client-multi/client-cpp/tools/avro-cpp-disable-tests.patch new file mode 100644 index 0000000000..2fe4aa5d77 --- /dev/null +++ b/client/client-multi/client-cpp/tools/avro-cpp-disable-tests.patch @@ -0,0 +1,92 @@ +diff -crB avro-cpp-1.7.5/CMakeLists.txt avro-cpp-1.7.5_patched/CMakeLists.txt +*** avro-cpp-1.7.5/CMakeLists.txt 2013-08-19 21:12:49.000000000 +0300 +*************** +*** 93,168 **** + + target_link_libraries (avrocpp ${Boost_LIBRARIES}) + +- add_executable (precompile test/precompile.cc) +- +- target_link_libraries (precompile avrocpp_s ${Boost_LIBRARIES}) +- +- macro (gencpp file ns) +- add_custom_command (OUTPUT ${ns}.hh +- COMMAND precompile ${CMAKE_CURRENT_SOURCE_DIR}/jsonschemas/${file} +- ${file} +- COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen-cppcode.py +- -n ${ns} -i ${file} -o ${ns}.hh +- DEPENDS precompile ${CMAKE_CURRENT_SOURCE_DIR}/jsonschemas/${file}) +- add_custom_target(${ns} DEPENDS ${ns}.hh) +- endmacro (gencpp) +- +- if (CYGWIN OR NOT WIN32) +- gencpp (bigrecord testgen) +- gencpp (bigrecord2 testgen2) +- endif () +- +- macro (gen file ns) +- add_custom_command (OUTPUT ${file}.hh +- COMMAND avrogencpp +- -p - +- -i ${CMAKE_CURRENT_SOURCE_DIR}/jsonschemas/${file} +- -o ${file}.hh -n ${ns} -U +- DEPENDS avrogencpp ${CMAKE_CURRENT_SOURCE_DIR}/jsonschemas/${file}) +- add_custom_target (${file}_hh DEPENDS ${file}.hh) +- endmacro (gen) +- +- gen (bigrecord testgen) +- gen (bigrecord2 testgen2) +- gen (tweet testgen3) +- gen (union_array_union uau) +- gen (union_map_union umu) +- gen (union_conflict uc) +- gen (recursive rec) +- gen (reuse ru) +- gen (circulardep cd) +- +- add_executable (avrogencpp impl/avrogencpp.cc) +- target_link_libraries (avrogencpp avrocpp_s ${Boost_LIBRARIES}) +- +- enable_testing() +- +- macro (unittest name) +- add_executable (${name} test/${name}.cc) +- target_link_libraries (${name} avrocpp ${Boost_LIBRARIES}) +- add_test (NAME ${name} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +- COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${name}) +- endmacro (unittest) +- +- unittest (buffertest) +- unittest (unittest) +- unittest (SchemaTests) +- unittest (CodecTests) +- unittest (StreamTests) +- unittest (SpecificTests) +- unittest (DataFileTests) +- unittest (JsonTests) +- unittest (AvrogencppTests) +- +- if (CYGWIN OR NOT WIN32) +- unittest (testgentest) +- add_dependencies (testgentest testgen testgen2) +- endif() +- +- add_dependencies (AvrogencppTests bigrecord_hh bigrecord2_hh tweet_hh +- union_array_union_hh union_map_union_hh union_conflict_hh +- recursive_hh reuse_hh circulardep_hh) +- + include (InstallRequiredSystemLibraries) + + set (CPACK_PACKAGE_FILE_NAME "avrocpp-${AVRO_VERSION_MAJOR}") +--- 93,98 ---- +*************** +*** 174,181 **** + ARCHIVE DESTINATION lib + RUNTIME DESTINATION lib) + +- install (TARGETS avrogencpp RUNTIME DESTINATION bin) +- + install (DIRECTORY api/ DESTINATION include/avro + FILES_MATCHING PATTERN *.hh) + +--- 104,109 ---- diff --git a/keys/bootstrap/private.key b/keys/bootstrap/private.key new file mode 100644 index 0000000000000000000000000000000000000000..86b969b8efa0d07129e86d6b0ed73bc2b1fedadf GIT binary patch literal 1219 zcmV;!1U&mNf&{+;0RS)!1_>&LNQUrsW5^Br2+u}0)hbn0D~q)frFcq z(IZM(AzdL~ItWWEA(YY?qtVUukC8jUXyECZuBw8nntAXY=Gv*34FDvMnShe#8KaFT zX(OV=k+nqBd=?B2zRv|#YfU!*mX4qXk|=0&ulqBYH2U0$erGD-LLEND=c?LroAm{CSvHNa1Mj$*x(qGrYJAJybHWQ2x%j;DQJ`@^@y6EOWgpALP7AxoKA z@U*EnUJGnFyIKGte_788OcqsQw2^2K49tsTf1mOR;WO_nCruh2>kGT@{Gik54B0%B{~P% zb0z^6TvnBPH3URUxlT}Sa(SV>!04Xbt>ke@OaFiB@a{ig+f)e`BOw2BIK$^o!_oM^ zLqgSRDr3P*7`arPVbuV7eGVLiM#|+b{v*{Qyu>Qr8J$X-qc`Sz@*;DzwBM2&MSsk% zI!@D5Xeoqzm3V@-$qE=^V>?uSYLht|$o+)nw2xHzA~eK85lpoV4o3#$8F2!EfdIK= z_m-v;8cdop(V5i)J)`Qgycr}(n5-a6Wo2vc;1#P2)>EqPcD{xkZ5N-6S?wnfseGdc zfx?0ZyVIP*BZ4xK=BxsFD5bl{Ns;2&;DB z@-O`VLHqpu{sg_eJrla~#EPl%k5G%#EtaW`_~r_i%`3g)kZ)HlJr!&|c%>zVK1ZN& zR)%DR(z}X2A|k#}wO(h`2qRb%ovMZ5u?yhzaVFSy-TyOAMKEA~b=wUk{3|KaLnQ)% zfdGr8Zw-yu^TlDE2j0Bl?F&p@$Uu|f&)ZLw8#7ZQV}ef2;>s}v;BnBY*Q2DZUW-u@ zuIJDZc7I{qxpnS}=+WToP%`k%C{(UuAy)XJO~)UYCV3|VM@9tF8Jzzt>O197lc9YT z>1d@XiQwmv(gJ4pJ2vmeV`1Bje;7^dt=3>QJe|!1}qKCiF(#OIw zhMm6X>DQH!$m#`_88J|l45|g%J>a1!uV_m&5lpQp^N3x#Hu5>PiQ>VYU;q`;)DXC`n&C55Z(V>5(sP5e%9VVVB=DTZ zQsx$NA)j?fWmGEN2BsVd9>9D~nrCx%Q3==3(HtuEWctHPs4G)D?Jq?jXL$X4LA@#A hj9fbhcTs@%`l#IIRqco#l+w_X=hf_gD$~zuiS(SgPe1?w literal 0 HcmV?d00001 diff --git a/keys/bootstrap/public.key b/keys/bootstrap/public.key new file mode 100644 index 0000000000000000000000000000000000000000..b577ec4bd35853ddc046b40bf3734a58ffd51bbe GIT binary patch literal 294 zcmV+>0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>lgC<3RgPW7lBT88z zT_ImO2umv=l+qca(arRakvqX?;OUyKs)DMTdGH77Px)&jnU%O*a6Rj-UpTC}?!A`!kp{`rL_rXDZ=B9X`b8s@ig!_pi;W8MahL z0Nzs@0`E;o^H-ORM)wpO)-qHKY;%qZ8;yQx4&$CcRF%^G1lXi~x@WVO*Y9HdQ(IQ< z_%eo=QAS=hz)yRQV!f22X2tFw)#Nv1gob>Mr+r`h!?(l}F#SHC4t<6pOPN{lw5c~> s2kd4q?z=;}G&7i)2$?Q3&@(0EQfK$^W=kTzb5x+YQcyq<0s{d60Ze&;i2wiq literal 0 HcmV?d00001 diff --git a/keys/operations/private.key b/keys/operations/private.key new file mode 100644 index 0000000000000000000000000000000000000000..df4fc443a9cc1f8a36ff9c53eacaf72c1cdba19b GIT binary patch literal 1218 zcmV;z1U>sOf&{(-0RS)!1_>&LNQUrs4#*Aqyhl|0)hbn0Hr}#iLL@T zYVx{zIkL<2Tu

d8Pu&5w!NqjB4w_Xu)RYXRp!I;s$;8VNTNdYU=JddBBYVL#mi0 zukPKfV(-;9)Q2pbCJdr94CY{xNBG&|x?X z?^}ah*t^ag_7vV(F%v>QH+Xs~;DZtIt-tI$C1TIw0NP#$Y#5Q1thq6H3hgY;(0i`! z@JIAl)+U({@{kxi?n}baRi@L5Nkw^G8HGI+!{m0ih#9tEhDV&D-`l~rK_&8mSFC&m zHL1W;J1`dEzs7Kz#X?=zdosVWoPQNGIDk>#nFKh!#qiW~+WJj>C>+W5YJN?xHfq?+HXEja&w)}!EW2?ecBvv}&m!ik^ybjA9 z1J_s&t+si)FpSBuw$o8T&BRw7>2!aWsn(zUUZosJz6`Kb&v z1)-jjnLq37{^wP^_oT$mB$;!yjww#zB@k|LF#)QM)0ER0^cz7B{2#ig)v#9A&CNkfMSkr)A8wn4aQoQ3pdkm?&|txdjDxf za?@ZTF(kMUW&vRmZXP&$y4y3cvyCEZ&(Gqp3Q&1W|E~w7&zDsxHk0lxL+XTc*B~?{ zV5?CxFF<7d;0JrTf_{t98H%6J5F^3{TkS(t1L7reA&F9!Avvbm1}+&;jwpia{~ouK zJpzG&0FL>zyTMzKMB6G>!Kk%q8zJB~33u_ZeZzi3^G$!Wm1fLblE4jzL?3wwW}QTl z!j6tS%3&pE!D81cANp{LG==EJj?-JLua$|0|AiAnkEXo=4x>Utof44StZpbH?N%2_ gw^v3NgL^r{LOr%jEin}uN@@Q3p8F6fMyrJc{#1uj+yDRo literal 0 HcmV?d00001 diff --git a/keys/operations/public.key b/keys/operations/public.key new file mode 100644 index 0000000000000000000000000000000000000000..f631ef3845e2d2dec332cd928161ac347909024e GIT binary patch literal 294 zcmV+>0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>lr9oJUt^zn}^16CC zvdi>bPvE|JrUJ?lwD!!5YU{yh!Di-XuhG-u27UHnPSW{m>h3ssz>NY!s+c6N?%k|n z@6|TeK|UA>rjeAM86rf((Q*p;1K(m&HzW$j(k4Nm3V^1iJVX5cF==+tVK@x$TZ3HK zyUrZ;6y8}e6GA>WczP<}gAwtqzwA3DV$b3L+Fl227?G8%xiNVP?JUmFd#>&9NAy?L zCYccOkQh7eOTy7rrqhZ^MR{Erg*_F+k literal 0 HcmV?d00001 diff --git a/nix/kaa-docs/Gemfile b/nix/kaa-docs/Gemfile new file mode 100644 index 0000000000..1ead85f530 --- /dev/null +++ b/nix/kaa-docs/Gemfile @@ -0,0 +1,22 @@ +# +# Copyright 2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +source "https://rubygems.org" + +gem "jekyll" +gem "jekyll-sitemap" +gem "jekyll-gist" +gem "jekyll-paginate" diff --git a/nix/kaa-docs/Gemfile.lock b/nix/kaa-docs/Gemfile.lock new file mode 100644 index 0000000000..65fcc02f5a --- /dev/null +++ b/nix/kaa-docs/Gemfile.lock @@ -0,0 +1,55 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.4.0) + colorator (0.1) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) + ffi (1.9.10) + jekyll (3.1.6) + colorator (~> 0.1) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 1.1) + kramdown (~> 1.3) + liquid (~> 3.0) + mercenary (~> 0.3.3) + rouge (~> 1.7) + safe_yaml (~> 1.0) + jekyll-gist (1.4.0) + octokit (~> 4.2) + jekyll-paginate (1.1.0) + jekyll-sass-converter (1.4.0) + sass (~> 3.4) + jekyll-sitemap (0.10.0) + jekyll-watch (1.4.0) + listen (~> 3.0, < 3.1) + kramdown (1.11.1) + liquid (3.0.6) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + mercenary (0.3.6) + multipart-post (2.0.0) + octokit (4.3.0) + sawyer (~> 0.7.0, >= 0.5.3) + rb-fsevent (0.9.7) + rb-inotify (0.9.7) + ffi (>= 0.5.0) + rouge (1.11.1) + safe_yaml (1.0.4) + sass (3.4.22) + sawyer (0.7.0) + addressable (>= 2.3.5, < 2.5) + faraday (~> 0.8, < 0.10) + +PLATFORMS + ruby + +DEPENDENCIES + jekyll + jekyll-gist + jekyll-paginate + jekyll-sitemap + +BUNDLED WITH + 1.11.2 diff --git a/nix/kaa-docs/default.nix b/nix/kaa-docs/default.nix new file mode 100644 index 0000000000..47db343106 --- /dev/null +++ b/nix/kaa-docs/default.nix @@ -0,0 +1,40 @@ +# +# Copyright 2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +{ stdenv +, bundlerEnv +, git +, ruby +}: + +let + ruby-deps = bundlerEnv { + name = "kaa-docs-ruby-deps"; + + # The only file that should be edited by hand is ./Gemfile. + # To get a Gemfile.lock, run nix-shell -p bundler --run 'bundler lock' + # To get a gemset.nix, run nix-shell -p bundix --run 'bundix' + # You can update both in one go: + # nix-shell -p bundler -p bundix --run 'bundler lock && bundix' + gemfile = ./Gemfile; + lockfile = ./Gemfile.lock; + gemset = ./gemset.nix; + }; + +in stdenv.mkDerivation { + name = "kaa-docs"; + buildInputs = [ git ruby ruby-deps ]; +} diff --git a/nix/kaa-docs/gemset.nix b/nix/kaa-docs/gemset.nix new file mode 100644 index 0000000000..e4ac17e160 --- /dev/null +++ b/nix/kaa-docs/gemset.nix @@ -0,0 +1,178 @@ +{ + addressable = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0mpn7sbjl477h56gmxsjqb89r5s3w7vx5af994ssgc3iamvgzgvs"; + type = "gem"; + }; + version = "2.4.0"; + }; + colorator = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "09zp15hyd9wlbgf1kmrf4rnry8cpvh1h9fj7afarlqcy4hrfdpvs"; + type = "gem"; + }; + version = "0.1"; + }; + faraday = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1kplqkpn2s2yl3lxdf6h7sfldqvkbkpxwwxhyk7mdhjplb5faqh6"; + type = "gem"; + }; + version = "0.9.2"; + }; + ffi = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1m5mprppw0xcrv2mkim5zsk70v089ajzqiq5hpyb0xg96fcyzyxj"; + type = "gem"; + }; + version = "1.9.10"; + }; + jekyll = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1l1kq96bps29sx1cawbn4p9al4cljkywlr02zwgbcdwrr0211rhp"; + type = "gem"; + }; + version = "3.1.6"; + }; + jekyll-gist = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1mjw9y7mqmglckn81ix9x1gqnvbxb28fbz72yhvmm5sdk2l957lr"; + type = "gem"; + }; + version = "1.4.0"; + }; + jekyll-paginate = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0r7bcs8fq98zldih4787zk5i9w24nz5wa26m84ssja95n3sas2l8"; + type = "gem"; + }; + version = "1.1.0"; + }; + jekyll-sass-converter = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "095757w0pg6qh3wlfg1j1mw4fsz7s89ia4zai5f2rhx9yxsvk1d8"; + type = "gem"; + }; + version = "1.4.0"; + }; + jekyll-sitemap = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1sg0yzhzja2lw48w5l23l3612pig5c2x4hf883c8bgz0rvr81di1"; + type = "gem"; + }; + version = "0.10.0"; + }; + jekyll-watch = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "15imgkfdzvbsz159bc2aa7a21x3379licrij5g0sdid8bs9rxd4a"; + type = "gem"; + }; + version = "1.4.0"; + }; + kramdown = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "05ljwi07hjjwgnjg19sg8mkyxf1an5xn8kn1717d5qrrqkzn3zq1"; + type = "gem"; + }; + version = "1.11.1"; + }; + liquid = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "033png37ym4jrjz5bi7zb4ic4yxacwvnllm1xxmrnr4swgyyygc2"; + type = "gem"; + }; + version = "3.0.6"; + }; + listen = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1l0y7hbyfiwpvk172r28hsdqsifq1ls39hsfmzi1vy4ll0smd14i"; + type = "gem"; + }; + version = "3.0.8"; + }; + mercenary = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "10la0xw82dh5mqab8bl0dk21zld63cqxb1g16fk8cb39ylc4n21a"; + type = "gem"; + }; + version = "0.3.6"; + }; + multipart-post = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "09k0b3cybqilk1gwrwwain95rdypixb2q9w65gd44gfzsd84xi1x"; + type = "gem"; + }; + version = "2.0.0"; + }; + octokit = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1hq47ck0z03vr3rzblyszihn7x2m81gv35chwwx0vrhf17nd27np"; + type = "gem"; + }; + version = "4.3.0"; + }; + rb-fsevent = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1xlkflgxngwkd4nyybccgd1japrba4v3kwnp00alikj404clqx4v"; + type = "gem"; + }; + version = "0.9.7"; + }; + rb-inotify = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1yfcp3065n08balljmxn0qzwhdbwwxn2h9z89wmydyfj2gq1p71d"; + type = "gem"; + }; + version = "0.9.7"; + }; + rouge = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "13amckbdknnc5491ag28y8pqbyfpbzx5n4rlmadxhd3wkrhp92c8"; + type = "gem"; + }; + version = "1.11.1"; + }; + safe_yaml = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1hly915584hyi9q9vgd968x2nsi5yag9jyf5kq60lwzi5scr7094"; + type = "gem"; + }; + version = "1.0.4"; + }; + sass = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "0dkj6v26fkg1g0majqswwmhxva7cd6p3psrhdlx93qal72dssywy"; + type = "gem"; + }; + version = "3.4.22"; + }; + sawyer = { + source = { + remotes = ["https://rubygems.org"]; + sha256 = "1cn48ql00mf1ag9icmfpj7g7swh7mdn7992ggynjqbw1gh15bs3j"; + type = "gem"; + }; + version = "0.7.0"; + }; +} \ No newline at end of file diff --git a/server/common/admin-rest-client/src/main/java/org/kaaproject/kaa/server/common/admin/AdminClient.java b/server/common/admin-rest-client/src/main/java/org/kaaproject/kaa/server/common/admin/AdminClient.java index d77c6f634b..662bb92819 100644 --- a/server/common/admin-rest-client/src/main/java/org/kaaproject/kaa/server/common/admin/AdminClient.java +++ b/server/common/admin-rest-client/src/main/java/org/kaaproject/kaa/server/common/admin/AdminClient.java @@ -205,12 +205,6 @@ public TenantUserDto getTenant(String userId) throws Exception { return restTemplate.getForObject(restTemplate.getUrl() + "tenant/" + userId, TenantUserDto.class); } - public void deleteTenant(String userId) throws Exception { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("userId", userId); - restTemplate.postForLocation(restTemplate.getUrl() + "delTenant", params); - } - public ApplicationDto editApplication(ApplicationDto application) throws Exception { return restTemplate.postForObject(restTemplate.getUrl() + "application", application, ApplicationDto.class); } @@ -222,12 +216,8 @@ public List getApplications() throws Exception { return entity.getBody(); } - public ApplicationDto getApplication(String applicationId) throws Exception { - return restTemplate.getForObject(restTemplate.getUrl() + "application/" + applicationId, ApplicationDto.class); - } - public ApplicationDto getApplicationByApplicationToken(String token) throws Exception { - return restTemplate.getForObject(restTemplate.getUrl() + "application/token/" + token, ApplicationDto.class); + return restTemplate.getForObject(restTemplate.getUrl() + "application/" + token, ApplicationDto.class); } public void deleteApplication(String applicationId) throws Exception { @@ -261,15 +251,15 @@ public ServerProfileSchemaDto saveServerProfileSchema(ServerProfileSchemaDto ser return restTemplate.postForObject(restTemplate.getUrl() + "saveServerProfileSchema", serverProfileSchema, ServerProfileSchemaDto.class); } - public NotificationSchemaDto createNotificationSchema(NotificationSchemaDto notificationSchema, String schemaResource) throws Exception { - return createNotificationSchema(notificationSchema, getFileResource(schemaResource)); - } +// public NotificationSchemaDto createNotificationSchema(NotificationSchemaDto notificationSchema, String schemaResource) throws Exception { +// return createNotificationSchema(notificationSchema); +// } - public NotificationSchemaDto createNotificationSchema(NotificationSchemaDto notificationSchema, ByteArrayResource schemaResource) + public NotificationSchemaDto createNotificationSchema(NotificationSchemaDto notificationSchema/*, ByteArrayResource schemaResource*/) throws Exception { MultiValueMap params = new LinkedMultiValueMap<>(); params.add("notificationSchema", notificationSchema); - params.add("file", schemaResource); + // params.add("file", schemaResource); return restTemplate.postForObject(restTemplate.getUrl() + "createNotificationSchema", params, NotificationSchemaDto.class); } @@ -300,21 +290,10 @@ public TopicDto getTopic(String topicId) throws Exception { return restTemplate.getForObject(restTemplate.getUrl() + "topic/" + topicId, TopicDto.class); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getTopicsByApplicationToken(String)} - */ - @Deprecated - public List getTopicsByApplicationId(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "topics/" + applicationId, HttpMethod.GET, null, typeRef); - return entity.getBody(); - } - public List getTopicsByApplicationToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "topicsByAppToken/" + applicationToken, HttpMethod.GET, null, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "topics/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } @@ -423,54 +402,22 @@ public LogSchemaDto getLogSchema(String logSchemaId) throws Exception { return restTemplate.getForObject(restTemplate.getUrl() + "logSchema/" + logSchemaId, LogSchemaDto.class); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getSchemaVersionsByApplicationToken(String)} - */ - @Deprecated - public SchemaVersions getSchemaVersionsByApplicationId(String applicationId) throws Exception { - return restTemplate.getForObject(restTemplate.getUrl() + "schemaVersions/" + applicationId, SchemaVersions.class); - } - public SchemaVersions getSchemaVersionsByApplicationToken(String applicationToken) throws Exception { - return restTemplate.getForObject(restTemplate.getUrl() + "schemaVersionsByAppToken/" + applicationToken, SchemaVersions.class); - } - - /** - * @deprecated As of release 0.9.0, replaced by {@link #getConfigurationSchemasByAppToken(String)} - */ - @Deprecated - public List getConfigurationSchemas(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "configurationSchemas/" + applicationId, - HttpMethod.GET, null, typeRef); - return entity.getBody(); + return restTemplate.getForObject(restTemplate.getUrl() + "schemaVersions/" + applicationToken, SchemaVersions.class); } public List getConfigurationSchemasByAppToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "configurationSchemasByAppToken/" + + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "configurationSchemas/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } - public List getProfileSchemas(String applicationId) throws Exception { + public List getProfileSchemas(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "profileSchemas/" + applicationId, - HttpMethod.GET, null, typeRef); - return entity.getBody(); - } - - /** - * @deprecated As of release 0.9.0, replaced by {@link #getServerProfileSchemasByAppToken(String)} - */ - @Deprecated - public List getServerProfileSchemas(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "serverProfileSchemas/" + applicationId, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "profileSchemas/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } @@ -478,67 +425,31 @@ public List getServerProfileSchemas(String applicationId public List getServerProfileSchemasByAppToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "serverProfileSchemasByAppToken/" + + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "serverProfileSchemas/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getNotificationSchemasByAppToken(String)} - */ - @Deprecated - public List getNotificationSchemas(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "notificationSchemas/" + applicationId, - HttpMethod.GET, null, typeRef); - return entity.getBody(); - } - public List getNotificationSchemasByAppToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "notificationSchemasByAppToken/" + applicationToken, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "notificationSchemas/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getUserNotificationSchemasByAppToken(String)} - */ - @Deprecated - public List getUserNotificationSchemas(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "userNotificationSchemas/" + applicationId, HttpMethod.GET, - null, typeRef); - return entity.getBody(); - } - public List getUserNotificationSchemasByAppToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "userNotificationSchemasByAppToken/" + applicationToken, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "userNotificationSchemas/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getLogSchemasByAppToken(String)} - */ - @Deprecated - public List getLogSchemas(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "logSchemas/" + applicationId, HttpMethod.GET, null, - typeRef); - return entity.getBody(); - } - public List getLogSchemasByAppToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "logSchemasByAppToken/" + applicationToken, HttpMethod.GET, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "logSchemas/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } @@ -577,22 +488,10 @@ public void deleteEndpointGroup(String endpointGroupId) throws Exception { restTemplate.postForLocation(restTemplate.getUrl() + "delEndpointGroup", params); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getEndpointGroupsByAppToken(String)} - */ - @Deprecated - public List getEndpointGroups(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "endpointGroups/" + applicationId, HttpMethod.GET, - null, typeRef); - return entity.getBody(); - } - public List getEndpointGroupsByAppToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "endpointGroupsByAppToken/" + applicationToken, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "endpointGroups/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } @@ -784,62 +683,26 @@ public ApplicationEventFamilyMapDto getApplicationEventFamilyMap(String aefMapId return restTemplate.getForObject(restTemplate.getUrl() + "applicationEventMap/" + aefMapId, ApplicationEventFamilyMapDto.class); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getApplicationEventFamilyMapsByApplicationToken(String)} - */ - @Deprecated - public List getApplicationEventFamilyMapsByApplicationId(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "applicationEventMaps/" + applicationId, - HttpMethod.GET, null, typeRef); - return entity.getBody(); - } - public List getApplicationEventFamilyMapsByApplicationToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "applicationEventMapsByAppToken/" + + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "applicationEventMaps/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getVacantEventClassFamiliesByApplicationToken(String)} - */ - @Deprecated - public List getVacantEventClassFamiliesByApplicationId(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "vacantEventClassFamilies/" + applicationId, HttpMethod.GET, - null, typeRef); - return entity.getBody(); - } - public List getVacantEventClassFamiliesByApplicationToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "vacantEventClassFamiliesByAppToken/" + applicationToken, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "vacantEventClassFamilies/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getEventClassFamiliesByApplicationToken(String)} - */ - @Deprecated - public List getEventClassFamiliesByApplicationId(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "eventClassFamilies/" + applicationId, HttpMethod.GET, - null, typeRef); - return entity.getBody(); - } - public List getEventClassFamiliesByApplicationToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "eventClassFamiliesByAppToken/" + applicationToken, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "eventClassFamilies/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } @@ -852,22 +715,10 @@ public LogAppenderDto getLogAppender(String logAppenderId) throws Exception { return restTemplate.getForObject(restTemplate.getUrl() + "logAppender/" + logAppenderId, LogAppenderDto.class); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getLogAppendersByAppToken(String)} - */ - @Deprecated - public List getLogAppenders(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "logAppenders/" + applicationId, HttpMethod.GET, null, - typeRef); - return entity.getBody(); - } - public List getLogAppendersByAppToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "logAppendersByAppToken/" + applicationToken, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "logAppenders/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } @@ -882,21 +733,10 @@ public UserVerifierDto getUserVerifier(String userVerifierId) throws Exception { return restTemplate.getForObject(restTemplate.getUrl() + "userVerifier/" + userVerifierId, UserVerifierDto.class); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getUserVerifiersByApplicationToken(String)} - */ - @Deprecated - public List getUserVerifiersByApplicationId(String applicationId) { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "userVerifiers/" + applicationId, HttpMethod.GET, null, typeRef); - return entity.getBody(); - } - public List getUserVerifiersByApplicationToken(String applicationToken) { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "userVerifiersByAppToken/" + applicationToken, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "userVerifiers/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } @@ -928,22 +768,10 @@ public SdkProfileDto getSdkProfile(String sdkProfileId) throws Exception { return entity.getBody(); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getSdkProfilesByApplicationToken(String)} - */ - @Deprecated - public List getSdkProfiles(String applicationId) throws Exception { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "sdkProfiles/" + applicationId, HttpMethod.GET, null, - typeRef); - return entity.getBody(); - } - public List getSdkProfilesByApplicationToken(String applicationToken) throws Exception { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "sdkProfilesByAppToken/" + applicationToken, + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "sdkProfiles/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } @@ -994,23 +822,6 @@ public FileData downloadSdk(SdkProfileDto key) throws Exception { return data; } - /** - * @deprecated As of release 0.9.0, replaced by {@link #downloadCtlSchemaByAppToken(CTLSchemaDto, CTLSchemaExportMethod, String)} - */ - @Deprecated - public FileData downloadCtlSchema(CTLSchemaDto ctlSchemaDto, CTLSchemaExportMethod method) { - FileDataResponseExtractor extractor = new FileDataResponseExtractor(); - MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.add("fqn", ctlSchemaDto.getMetaInfo().getFqn()); - parameters.add("version", Integer.toString(ctlSchemaDto.getVersion())); - if (ctlSchemaDto.getMetaInfo().getApplicationId() != null) { - parameters.add("applicationId", ctlSchemaDto.getMetaInfo().getApplicationId()); - } - parameters.add("method", method.name()); - RequestCallback request = new DataRequestCallback<>(parameters); - return restTemplate.execute(restTemplate.getUrl() + "CTL/exportSchema", HttpMethod.POST, request, extractor); - } - public FileData downloadCtlSchemaByAppToken(CTLSchemaDto ctlSchemaDto, CTLSchemaExportMethod method, String appToken) { FileDataResponseExtractor extractor = new FileDataResponseExtractor(); MultiValueMap parameters = new LinkedMultiValueMap<>(); @@ -1021,7 +832,7 @@ public FileData downloadCtlSchemaByAppToken(CTLSchemaDto ctlSchemaDto, CTLSchema } parameters.add("method", method.name()); RequestCallback request = new DataRequestCallback<>(parameters); - return restTemplate.execute(restTemplate.getUrl() + "CTL/appToken/exportSchema", HttpMethod.POST, request, extractor); + return restTemplate.execute(restTemplate.getUrl() + "CTL/exportSchema", HttpMethod.POST, request, extractor); } public void flushSdkCache() throws Exception { @@ -1161,22 +972,6 @@ public String getFilename() { return bar; } - /** - * @deprecated As of release 0.9.0, replaced by {@link #saveCTLSchemaWithAppToken(String, String, String)} - */ - @Deprecated - public CTLSchemaDto saveCTLSchema(String body, String tenantId, String applicationId) { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("body", body); - if (tenantId != null) { - params.add("tenantId", tenantId); - } - if (applicationId != null) { - params.add("applicationId", applicationId); - } - return restTemplate.postForObject(restTemplate.getUrl() + "CTL/saveSchema", params, CTLSchemaDto.class); - } - public CTLSchemaDto saveCTLSchemaWithAppToken(String body, String tenantId, String applicationToken) { MultiValueMap params = new LinkedMultiValueMap<>(); params.add("body", body); @@ -1186,27 +981,7 @@ public CTLSchemaDto saveCTLSchemaWithAppToken(String body, String tenantId, Stri if (applicationToken != null) { params.add("applicationToken", applicationToken); } - return restTemplate.postForObject(restTemplate.getUrl() + "CTL/appToken/saveSchema", params, CTLSchemaDto.class); - } - - /** - * @deprecated As of release 0.9.0, replaced by {@link #deleteCTLSchemaByFqnVersionTenantIdAndApplicationToken(String, Integer, String, String)} - */ - @Deprecated - public void deleteCTLSchemaByFqnVersionTenantIdAndApplicationId(String fqn, - Integer version, - String tenantId, - String applicationId) { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("fqn", fqn); - params.add("version", version); - if (tenantId != null) { - params.add("tenantId", tenantId); - } - if (applicationId != null) { - params.add("applicationId", applicationId); - } - restTemplate.postForLocation(restTemplate.getUrl() + "CTL/deleteSchema", params); + return restTemplate.postForObject(restTemplate.getUrl() + "CTL/saveSchema", params, CTLSchemaDto.class); } public void deleteCTLSchemaByFqnVersionTenantIdAndApplicationToken(String fqn, @@ -1222,32 +997,18 @@ public void deleteCTLSchemaByFqnVersionTenantIdAndApplicationToken(String fqn, if (applicationToken!= null) { params.add("applicationToken", applicationToken); } - restTemplate.postForLocation(restTemplate.getUrl() + "CTL/appToken/deleteSchema", params); - } - - /** - * @deprecated As of release 0.9.0, replaced by {@link #getCTLSchemaByFqnVersionTenantIdAndApplicationToken(String, Integer, String, String)} - */ - @Deprecated - public CTLSchemaDto getCTLSchemaByFqnVersionTenantIdAndApplicationId(String fqn, Integer version, String tenantId, String applicationId) { - if (tenantId != null && applicationId != null) { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/getSchema?fqn={fqn}&version={version}&tenantId={tenantId}&applicationId={applicationId}", CTLSchemaDto.class, fqn, version, tenantId, applicationId); - }else if (tenantId != null) { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/getSchema?fqn={fqn}&version={version}&tenantId={tenantId}", CTLSchemaDto.class, fqn, version, tenantId); - } else { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/getSchema?fqn={fqn}&version={version}", CTLSchemaDto.class, fqn, version); - } + restTemplate.postForLocation(restTemplate.getUrl() + "CTL/deleteSchema", params); } public CTLSchemaDto getCTLSchemaByFqnVersionTenantIdAndApplicationToken(String fqn, Integer version, String tenantId, String applicationToken) { if (tenantId != null && applicationToken != null) { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/appToken/getSchema?fqn={fqn}&version={version}&tenantId={tenantId}" + + return restTemplate.getForObject(restTemplate.getUrl() + "CTL/getSchema?fqn={fqn}&version={version}&tenantId={tenantId}" + "&applicationToken={applicationToken}", CTLSchemaDto.class, fqn, version, tenantId, applicationToken); }else if (tenantId != null) { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/appToken/getSchema?fqn={fqn}&version={version}&tenantId={tenantId}", + return restTemplate.getForObject(restTemplate.getUrl() + "CTL/getSchema?fqn={fqn}&version={version}&tenantId={tenantId}", CTLSchemaDto.class, fqn, version, tenantId); } else { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/appToken/getSchema?fqn={fqn}&version={version}", CTLSchemaDto.class, fqn, version); + return restTemplate.getForObject(restTemplate.getUrl() + "CTL/getSchema?fqn={fqn}&version={version}", CTLSchemaDto.class, fqn, version); } } @@ -1255,34 +1016,22 @@ public CTLSchemaDto getCTLSchemaById(String id) { return restTemplate.getForObject(restTemplate.getUrl() + "CTL/getSchemaById?id={id}", CTLSchemaDto.class, id); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #checkFqnExistsWithAppToken(String, String, String)} - */ - @Deprecated - public boolean checkFqnExists(String fqn, String tenantId, String applicationId) { - if (tenantId != null && applicationId != null) { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/checkFqn?fqn={fqn}&tenantId={tenantId}&applicationId={applicationId}", - Boolean.class, fqn, tenantId, applicationId); - } else if (tenantId != null) { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/checkFqn?fqn={fqn}&tenantId={tenantId}", Boolean.class, fqn, tenantId); - } else { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/checkFqn?fqn={fqn}", Boolean.class, fqn); - } - } - public boolean checkFqnExistsWithAppToken(String fqn, String tenantId, String applicationToken) { if (tenantId != null && applicationToken != null) { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/appToken/checkFqn?fqn={fqn}&tenantId={tenantId}&applicationToken={applicationToken}", + return restTemplate.getForObject(restTemplate.getUrl() + "CTL/checkFqn?fqn={fqn}&tenantId={tenantId}&applicationToken={applicationToken}", Boolean.class, fqn, tenantId, applicationToken); } else if (tenantId != null) { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/appToken/checkFqn?fqn={fqn}&tenantId={tenantId}", Boolean.class, fqn, tenantId); + return restTemplate.getForObject(restTemplate.getUrl() + "CTL/checkFqn?fqn={fqn}&tenantId={tenantId}", Boolean.class, fqn, tenantId); } else { - return restTemplate.getForObject(restTemplate.getUrl() + "CTL/appToken/checkFqn?fqn={fqn}", Boolean.class, fqn); + return restTemplate.getForObject(restTemplate.getUrl() + "CTL/checkFqn?fqn={fqn}", Boolean.class, fqn); } } - public CTLSchemaMetaInfoDto updateCTLSchemaMetaInfoScope(CTLSchemaMetaInfoDto ctlSchemaMetaInfo) { - return restTemplate.postForObject(restTemplate.getUrl() + "CTL/updateScope", ctlSchemaMetaInfo, CTLSchemaMetaInfoDto.class); + public CTLSchemaMetaInfoDto promoteScopeToTenant(String applicationId, String fqn) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("applicationId", applicationId); + params.add("fqn", fqn); + return restTemplate.postForObject(restTemplate.getUrl() + "CTL/promoteScopeToTenant", params, CTLSchemaMetaInfoDto.class); } public List getSystemLevelCTLSchemas() { @@ -1299,22 +1048,10 @@ public List getTenantLevelCTLSchemas() { return entity.getBody(); } - /** - * @deprecated As of release 0.9.0, replaced by {@link #getApplicationLevelCTLSchemasByAppToken(String)} - */ - @Deprecated - public List getApplicationLevelCTLSchemas(String applicationId) { - ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { - }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "CTL/getApplicationSchemas/" + - applicationId, HttpMethod.GET, null, typeRef); - return entity.getBody(); - } - public List getApplicationLevelCTLSchemasByAppToken(String applicationToken) { ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { }; - ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "CTL/appToken/getApplicationSchemas/" + + ResponseEntity> entity = restTemplate.exchange(restTemplate.getUrl() + "CTL/getApplicationSchemas/" + applicationToken, HttpMethod.GET, null, typeRef); return entity.getBody(); } diff --git a/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/model/sql/NotificationSchema.java b/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/model/sql/NotificationSchema.java index 6ce52e0fc4..67fd41a539 100644 --- a/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/model/sql/NotificationSchema.java +++ b/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/model/sql/NotificationSchema.java @@ -30,7 +30,7 @@ @Entity @Table(name = NOTIFICATION_SCHEMA_TABLE_NAME) @OnDelete(action = OnDeleteAction.CASCADE) -public class NotificationSchema extends Schema implements Serializable { +public class NotificationSchema extends BaseSchema implements Serializable { private static final long serialVersionUID = 6585856417466958172L; diff --git a/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/service/ApplicationServiceImpl.java b/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/service/ApplicationServiceImpl.java index a934543935..2fb6d26523 100644 --- a/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/service/ApplicationServiceImpl.java +++ b/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/service/ApplicationServiceImpl.java @@ -270,19 +270,18 @@ private ServerProfileSchemaDto createDefaultServerProfileSchema(String appId, St } private NotificationSchemaDto createDefaultNotificationSchema(String appId, String createdUsername) { - NotificationSchemaDto schema = new NotificationSchemaDto(); - schema.setApplicationId(appId); - DataSchema defSchema = new KaaSchemaFactoryImpl().createDataSchema(getStringFromFile(DEFAULT_NOTIFICATION_SCHEMA_FILE, ApplicationServiceImpl.class)); - if (!defSchema.isEmpty()) { - schema.setSchema(defSchema.getRawSchema()); - } else { - throw new RuntimeException("Can't read default notification schema."); //NOSONAR + NotificationSchemaDto notificationSchemaDto = new NotificationSchemaDto(); + notificationSchemaDto.setApplicationId(appId); + CTLSchemaDto ctlSchema = ctlService.getOrCreateEmptySystemSchema(createdUsername); + notificationSchemaDto.setCtlSchemaId(ctlSchema.getId()); + notificationSchemaDto.setName(DEFAULT_SCHEMA_NAME); + notificationSchemaDto.setCreatedUsername(createdUsername); + notificationSchemaDto.setType(NotificationTypeDto.USER); + notificationSchemaDto = notificationService.saveNotificationSchema(notificationSchemaDto); + if (notificationSchemaDto == null) { + throw new RuntimeException("Can't save default profile schema "); //NOSONAR } - schema.setType(NotificationTypeDto.USER); - schema.setName(DEFAULT_SCHEMA_NAME); - schema.setCreatedUsername(createdUsername); - - return notificationService.saveNotificationSchema(schema); + return notificationSchemaDto; } private LogSchemaDto createDefaultLogSchema(String appId, String createdUsername) { diff --git a/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/service/NotificationServiceImpl.java b/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/service/NotificationServiceImpl.java index b9876e694c..415f093d49 100644 --- a/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/service/NotificationServiceImpl.java +++ b/server/common/dao/src/main/java/org/kaaproject/kaa/server/common/dao/service/NotificationServiceImpl.java @@ -170,7 +170,7 @@ public NotificationDto saveNotificationAndIncTopicSecNum(NotificationDto dto) { } private byte[] serializeNotificationBody(NotificationDto nf, NotificationSchema nfSchema) throws IOException { - GenericAvroConverter converter = new GenericAvroConverter<>(nfSchema.getSchema()); + GenericAvroConverter converter = new GenericAvroConverter<>(nfSchema.getCtlSchema().getBody()); String notificationJson = new String(nf.getBody(), Charset.forName("UTF8")); GenericRecord notificationAvro = converter.decodeJson(notificationJson); return converter.encode(notificationAvro); diff --git a/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/AbstractTest.java b/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/AbstractTest.java index 71aa4e7967..06b658771f 100644 --- a/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/AbstractTest.java +++ b/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/AbstractTest.java @@ -83,6 +83,7 @@ import org.kaaproject.kaa.server.common.core.schema.KaaSchemaFactoryImpl; import org.kaaproject.kaa.server.common.core.schema.OverrideSchema; import org.kaaproject.kaa.server.common.dao.exception.CredentialsServiceException; +import org.kaaproject.kaa.server.common.dao.exception.DatabaseProcessingException; import org.kaaproject.kaa.server.common.dao.impl.LogAppenderDao; import org.kaaproject.kaa.server.common.dao.impl.TenantDao; import org.kaaproject.kaa.server.common.dao.impl.UserDao; @@ -151,9 +152,7 @@ public class AbstractTest { protected static final String ENDPOINT_USER_EXTERNAL_ID = "Generated Test Endpoint User External Id"; protected static final String ENDPOINT_USER_NAME = "Generated Test Endpoint User Name"; public static final String DEFAULT_FQN = "org.kaaproject.kaa.ctl.TestSchema"; - public static final String TEST_PROFILE_BODY = "{ \"Profile\" : { \"org.kaaproject.kaa.schema.sample.profile\" : { \"country.$\" : \"1.0.$.\" , " + - "\"country$IsoCode\" : \"2.0.$.\" , \"city\" : \"3.0.$.\" , \"Obj\" : { \"org.kaaproject.kaa.schema.sample.profile.Obj\" : { \"name\" : { " + - "\"string\" : \"Obj.name$\"}}}}} , \"OS\" : { \"org.kaaproject.kaa.schema.sample.profile.OS\" : \"Android\"}}"; + public static final String TEST_PROFILE_BODY_PATH = "dao/schema/testProfileBody.json"; @Autowired protected DataSource dataSource; @@ -569,20 +568,24 @@ protected TopicDto generateTopicDto(String appId, TopicTypeDto type) { protected NotificationSchemaDto generateNotificationSchemaDto(String appId, NotificationTypeDto type) { NotificationSchemaDto schema = new NotificationSchemaDto(); + ApplicationDto app = null; if (isBlank(appId)) { - appId = generateApplicationDto().getId(); + app = generateApplicationDto(); + appId = app.getId(); + } else { + app = applicationService.findAppById(appId); } schema.setApplicationId(appId); schema.setName(NOTIFICATION_SCHEMA_NAME); - String schemaBody = null; + schema.setType(type != null ? type : NotificationTypeDto.USER); + CTLSchemaDto ctlSchema = null; try { - schemaBody = readSchemaFileAsString("dao/schema/testBaseSchema.json"); - } catch (IOException e) { - e.printStackTrace(); - Assert.fail(e.getMessage()); + ctlSchema = ctlService.saveCTLSchema(generateCTLSchemaDto(app.getTenantId())); + } catch (DatabaseProcessingException e){ + ctlSchema = ctlService.getOrCreateEmptySystemSchema(USER_NAME); + } - schema.setSchema(new KaaSchemaFactoryImpl().createDataSchema(schemaBody).getRawSchema()); - schema.setType(type != null ? type : NotificationTypeDto.USER); + schema.setCtlSchemaId(ctlSchema.getId()); return notificationService.saveNotificationSchema(schema); } @@ -729,7 +732,11 @@ protected EndpointProfileDto generateEndpointProfileDto(String appId, List groupState = new ArrayList<>(); groupState.add(new EndpointGroupStateDto(endpointGroupId, null, null)); profileDto.setGroupState(groupState); - profileDto.setClientProfileBody(TEST_PROFILE_BODY); + try { + profileDto.setClientProfileBody(readSchemaFileAsString(TEST_PROFILE_BODY_PATH)); + } catch (IOException e) { + LOG.error("Can't set client-side EP body {}", e); + } profileDto.setServerProfileBody("{\"serverTitle\": \"SERVER_TEST\"}"); profileDto.setSdkToken(UUID.randomUUID().toString()); return endpointService.saveEndpointProfile(profileDto); diff --git a/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/impl/sql/HibernateAbstractTest.java b/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/impl/sql/HibernateAbstractTest.java index 8eddd68589..2ae49dd739 100644 --- a/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/impl/sql/HibernateAbstractTest.java +++ b/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/impl/sql/HibernateAbstractTest.java @@ -258,29 +258,30 @@ protected CTLSchema generateCTLSchema(String fqn, int version, Tenant tenant, CT return ctlSchema; } - protected List generateNotificationSchema(Application app, int count, NotificationTypeDto type) { + protected List generateNotificationSchema(Application app, int ctlVersion, int count, NotificationTypeDto type) { List schemas = Collections.emptyList(); try { if (app == null) { app = generateApplication(null); } - NotificationSchema notificationSchema; + CTLSchema ctlSchema = generateCTLSchema(DEFAULT_FQN, ctlVersion, app.getTenant(), null); + NotificationSchema schemaDto; schemas = new ArrayList<>(count); for (int i = 0; i < count; i++) { - notificationSchema = new NotificationSchema(); - notificationSchema.setApplication(app); - notificationSchema.setSchema(readSchemaFileAsString("dao/schema/testDataSchema.json")); - notificationSchema.setCreatedUsername("Test User"); - notificationSchema.setVersion(i + 1); - notificationSchema.setName("Test Name"); - notificationSchema.setType(type == null ? NotificationTypeDto.SYSTEM : type); - notificationSchema = notificationSchemaDao.save(notificationSchema); - Assert.assertNotNull(notificationSchema); - schemas.add(notificationSchema); + schemaDto = new NotificationSchema(); + schemaDto.setApplication(app); + schemaDto.setCreatedUsername("Test User"); + schemaDto.setCtlSchema(ctlSchema); + schemaDto.setVersion(i + 1); + schemaDto.setName("Test Name"); + schemaDto.setType(type); + schemaDto = notificationSchemaDao.save(schemaDto); + Assert.assertNotNull(schemaDto); + schemas.add(schemaDto); } - } catch (IOException e) { - LOG.error("Can't generate notification schema {}", e); - Assert.fail("Can't generate notification schema." + e.getMessage()); + } catch (Exception e) { + LOG.error("Can't generate profile schema {}", e); + Assert.fail("Can't generate profile schema." + e.getMessage()); } return schemas; } diff --git a/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/impl/sql/HibernateNotificationSchemaDaoTest.java b/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/impl/sql/HibernateNotificationSchemaDaoTest.java index f186924e83..ee12a8a6bd 100644 --- a/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/impl/sql/HibernateNotificationSchemaDaoTest.java +++ b/server/common/dao/src/test/java/org/kaaproject/kaa/server/common/dao/impl/sql/HibernateNotificationSchemaDaoTest.java @@ -16,6 +16,7 @@ package org.kaaproject.kaa.server.common.dao.impl.sql; +import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,6 +32,7 @@ import javax.transaction.Transactional; import java.util.List; +import java.util.stream.Collectors; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "/common-dao-test-context.xml") @@ -44,7 +46,7 @@ public class HibernateNotificationSchemaDaoTest extends HibernateAbstractTest { @Test public void testFindNotificationSchemasByAppId() throws Exception { Application application = generateApplication(null); - List schemas = generateNotificationSchema(application, 1, null); + List schemas = generateNotificationSchema(application, 1, 1, null); List found = notificationSchemaDao.findNotificationSchemasByAppId(application.getStringId()); Assert.assertEquals(schemas, found); } @@ -52,7 +54,7 @@ public void testFindNotificationSchemasByAppId() throws Exception { @Test public void testRemoveNotificationSchemasByAppId() throws Exception { Application application = generateApplication(null); - generateNotificationSchema(application, 1, null); + generateNotificationSchema(application, 1, 1, null); notificationSchemaDao.removeNotificationSchemasByAppId(application.getStringId()); List found = notificationSchemaDao.findNotificationSchemasByAppId(application.getStringId()); Assert.assertTrue(found.isEmpty()); @@ -61,8 +63,8 @@ public void testRemoveNotificationSchemasByAppId() throws Exception { @Test public void testFindNotificationSchemasByAppIdAndType() throws Exception { Application application = generateApplication(null); - List userSchemas = generateNotificationSchema(application, 2, NotificationTypeDto.USER); - generateNotificationSchema(application, 3, NotificationTypeDto.SYSTEM); + List userSchemas = generateNotificationSchema(application, 1, 2, NotificationTypeDto.USER); + generateNotificationSchema(application, 2, 3, NotificationTypeDto.SYSTEM); List found = notificationSchemaDao.findNotificationSchemasByAppIdAndType(application.getStringId(), NotificationTypeDto.USER); Assert.assertEquals(userSchemas, found); } @@ -70,8 +72,8 @@ public void testFindNotificationSchemasByAppIdAndType() throws Exception { @Test public void testFindNotificationSchemasByAppIdAndTypeAndVersion() throws Exception { Application application = generateApplication(null); - generateNotificationSchema(application, 1, NotificationTypeDto.SYSTEM); - List userSchemas = generateNotificationSchema(application, 3, NotificationTypeDto.USER); + generateNotificationSchema(application, 1, 1, NotificationTypeDto.SYSTEM); + List userSchemas = generateNotificationSchema(application, 2, 3, NotificationTypeDto.USER); NotificationSchema expected = userSchemas.get(2); NotificationSchema found = notificationSchemaDao.findNotificationSchemasByAppIdAndTypeAndVersion(application.getStringId(), NotificationTypeDto.USER, expected.getVersion()); Assert.assertEquals(expected, found); @@ -80,8 +82,8 @@ public void testFindNotificationSchemasByAppIdAndTypeAndVersion() throws Excepti @Test public void testFindLatestNotificationSchemaByAppId() throws Exception { Application application = generateApplication(null); - List userSchemas = generateNotificationSchema(application, 3, NotificationTypeDto.USER); - List systemSchemas = generateNotificationSchema(application, 3, NotificationTypeDto.SYSTEM); + List userSchemas = generateNotificationSchema(application, 1, 3, NotificationTypeDto.USER); + List systemSchemas = generateNotificationSchema(application, 2, 3, NotificationTypeDto.SYSTEM); NotificationSchema found = notificationSchemaDao.findLatestNotificationSchemaByAppId(application.getStringId(), NotificationTypeDto.USER); Assert.assertEquals(userSchemas.get(2), found); } diff --git a/server/common/dao/src/test/resources/dao/schema/testProfileBody.json b/server/common/dao/src/test/resources/dao/schema/testProfileBody.json new file mode 100644 index 0000000000..7c941d9f57 --- /dev/null +++ b/server/common/dao/src/test/resources/dao/schema/testProfileBody.json @@ -0,0 +1,24 @@ +{ "Profile" : { + "org.kaaproject.kaa.schema.sample.profile" : { + "country.$" : "1.$.", + "country$IsoCode" : "2.$.", + "city" : "3.$.", + "Obj" : { + "org.kaaproject.kaa.schema.sample.profile.Obj" : { + "name" : { + "string" : "Obj.name$" + } + } + } + } +}, + "OS" : { + "org.kaaproject.kaa.schema.sample.profile.OS" : "Linux" + }, + "array" : [ "one", "two", "three" ], + "arrayWithRecord" : [ { + "nameInArray." : "one.one" + }, { + "nameInArray." : "two$two" + } ] +} diff --git a/server/common/dto/src/main/java/org/kaaproject/kaa/common/dto/NotificationSchemaDto.java b/server/common/dto/src/main/java/org/kaaproject/kaa/common/dto/NotificationSchemaDto.java index 73e8c6f47e..8fbf5397eb 100644 --- a/server/common/dto/src/main/java/org/kaaproject/kaa/common/dto/NotificationSchemaDto.java +++ b/server/common/dto/src/main/java/org/kaaproject/kaa/common/dto/NotificationSchemaDto.java @@ -17,13 +17,12 @@ package org.kaaproject.kaa.common.dto; -public class NotificationSchemaDto extends AbstractSchemaDto { +public class NotificationSchemaDto extends BaseSchemaDto { private static final long serialVersionUID = -2514664251184915862L; private NotificationTypeDto type; - public NotificationTypeDto getType() { return type; } @@ -49,7 +48,7 @@ public boolean equals(Object o) { if (applicationId != null ? !applicationId.equals(that.applicationId) : that.applicationId != null) { return false; } - if (schema != null ? !schema.equals(that.schema) : that.schema != null) { + if (ctlSchemaId != null ? !ctlSchemaId.equals(that.ctlSchemaId) : that.ctlSchemaId != null) { return false; } if (type != that.type) { @@ -64,7 +63,7 @@ public int hashCode() { int result = applicationId != null ? applicationId.hashCode() : 0; result = 31 * result + version; result = 31 * result + (type != null ? type.hashCode() : 0); - result = 31 * result + (schema != null ? schema.hashCode() : 0); + result = 31 * result + (ctlSchemaId != null ? ctlSchemaId.hashCode() : 0); return result; } @@ -75,7 +74,7 @@ public String toString() { ", applicationId='" + applicationId + '\'' + ", majorVersion=" + version + ", type=" + type + - ", schema='" + schema + '\'' + + ", ctlSchemaId='" + ctlSchemaId + '\'' + '}'; } diff --git a/server/common/nosql/mongo-dao/src/main/resources/common-dao-mongodb.properties b/server/common/nosql/mongo-dao/src/main/resources/common-dao-mongodb.properties index 2f60a5d229..a46bb31b2f 100644 --- a/server/common/nosql/mongo-dao/src/main/resources/common-dao-mongodb.properties +++ b/server/common/nosql/mongo-dao/src/main/resources/common-dao-mongodb.properties @@ -1,6 +1,6 @@ # Mongodb configurations # list of mongodb nodes, possible to use multiply servers -servers=localhost:27017 +servers=10.2.3.80:27017 # mongodb database name db_name=kaa diff --git a/server/common/nosql/mongo-dao/src/test/java/org/kaaproject/kaa/server/common/nosql/mongo/dao/NotificationServiceImplTest.java b/server/common/nosql/mongo-dao/src/test/java/org/kaaproject/kaa/server/common/nosql/mongo/dao/NotificationServiceImplTest.java index 6301dcc68a..fa1cad02ce 100644 --- a/server/common/nosql/mongo-dao/src/test/java/org/kaaproject/kaa/server/common/nosql/mongo/dao/NotificationServiceImplTest.java +++ b/server/common/nosql/mongo-dao/src/test/java/org/kaaproject/kaa/server/common/nosql/mongo/dao/NotificationServiceImplTest.java @@ -31,15 +31,8 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import org.kaaproject.kaa.common.dto.EndpointNotificationDto; -import org.kaaproject.kaa.common.dto.EndpointProfileDto; -import org.kaaproject.kaa.common.dto.NotificationDto; -import org.kaaproject.kaa.common.dto.NotificationSchemaDto; -import org.kaaproject.kaa.common.dto.NotificationTypeDto; -import org.kaaproject.kaa.common.dto.TopicDto; -import org.kaaproject.kaa.common.dto.TopicTypeDto; -import org.kaaproject.kaa.common.dto.UpdateNotificationDto; -import org.kaaproject.kaa.common.dto.VersionDto; +import org.kaaproject.kaa.common.dto.*; +import org.kaaproject.kaa.common.dto.ctl.CTLSchemaDto; import org.kaaproject.kaa.server.common.core.schema.KaaSchemaFactoryImpl; import org.kaaproject.kaa.server.common.dao.exception.IncorrectParameterException; import org.slf4j.Logger; @@ -74,17 +67,22 @@ public void afterTest() { @Ignore @Test public void findNotificationsByIdWithExpiredTimeTest() { - String appId = generateApplicationDto().getId(); + ApplicationDto app = generateApplicationDto(); + + NotificationSchemaDto notificationSchemaDto = new NotificationSchemaDto(); + notificationSchemaDto.setApplicationId(app.getId()); + CTLSchemaDto ctlSchema = ctlService.saveCTLSchema(generateCTLSchemaDto(app.getTenantId())); + notificationSchemaDto.setCtlSchemaId(ctlSchema.getId()); + if (notificationSchemaDto == null) { + throw new RuntimeException("Can't save default profile schema "); //NOSONAR + } - NotificationSchemaDto schema = new NotificationSchemaDto(); - schema.setApplicationId(appId); - String schemaBody = "{\"type\":\"record\",\"name\":\"BasicSystemNotification\",\"namespace\":\"org.kaaproject.kaa.common.endpoint.gen\",\"fields\":[{\"name\":\"notificationBody\",\"type\":{\"type\":\"string\",\"avro.java.string\":\"String\"}},{\"name\":\"systemNotificationParam1\",\"type\":\"int\"},{\"name\":\"systemNotificationParam2\",\"type\":\"int\"}]}"; - schema.setSchema(new KaaSchemaFactoryImpl().createDataSchema(schemaBody).getRawSchema()); - schema.setType(NotificationTypeDto.USER); - NotificationSchemaDto savedSchema = notificationService.saveNotificationSchema(schema); + notificationSchemaDto.setCtlSchemaId(ctlSchema.getId()); + notificationSchemaDto.setType(NotificationTypeDto.USER); + NotificationSchemaDto savedSchema = notificationService.saveNotificationSchema(notificationSchemaDto); TopicDto topicDto = new TopicDto(); - topicDto.setApplicationId(appId); + topicDto.setApplicationId(app.getId()); topicDto.setName("New Topic"); topicDto.setType(TopicTypeDto.MANDATORY); diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/ClientFactory.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/ClientFactory.java index aa9e2744e4..d472be7d33 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/ClientFactory.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/ClientFactory.java @@ -105,8 +105,8 @@ public interface ClientFactory { BaseSchemaView getCreateConfigurationSchemaView(); BaseListView getNotificationSchemasView(); - BaseSchemaView getNotificationSchemaView(); - BaseSchemaView getCreateNotificationSchemaView(); + BaseCtlSchemaView getNotificationSchemaView(); + BaseCtlSchemaView getCreateNotificationSchemaView(); BaseListView getLogSchemasView(); BaseSchemaView getLogSchemaView(); diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/ClientFactoryImpl.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/ClientFactoryImpl.java index d175449f5c..96396db01e 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/ClientFactoryImpl.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/ClientFactoryImpl.java @@ -157,8 +157,8 @@ public class ClientFactoryImpl implements ClientFactory { private final BaseSchemaView createConfigurationSchemaView = new ConfigurationSchemaViewImpl(true); private final BaseListView notificationSchemasView = new NotificationSchemasViewImpl(); - private final BaseSchemaView notificationSchemaView = new NotificationSchemaViewImpl(false); - private final BaseSchemaView createNotificationSchemaView = new NotificationSchemaViewImpl(true); + private final BaseCtlSchemaView notificationSchemaView = new NotificationSchemaViewImpl(false); + private final BaseCtlSchemaView createNotificationSchemaView = new NotificationSchemaViewImpl(true); private final BaseListView logSchemasView = new LogSchemasViewImpl(); private final BaseSchemaView logSchemaView = new LogSchemaViewImpl(false); @@ -361,12 +361,12 @@ public BaseListView getNotificationSchemasView() { } @Override - public BaseSchemaView getNotificationSchemaView() { + public BaseCtlSchemaView getNotificationSchemaView() { return notificationSchemaView; } @Override - public BaseSchemaView getCreateNotificationSchemaView() { + public BaseCtlSchemaView getCreateNotificationSchemaView() { return createNotificationSchemaView; } diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/CtlSchemaActivity.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/CtlSchemaActivity.java index 371980c839..54967d35c2 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/CtlSchemaActivity.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/CtlSchemaActivity.java @@ -30,6 +30,7 @@ import org.kaaproject.kaa.server.admin.client.mvp.ClientFactory; import org.kaaproject.kaa.server.admin.client.mvp.place.CtlSchemaPlace; import org.kaaproject.kaa.server.admin.client.mvp.place.CtlSchemaPlace.SchemaType; +import org.kaaproject.kaa.server.admin.client.mvp.place.NotificationSchemasPlace; import org.kaaproject.kaa.server.admin.client.mvp.place.ProfileSchemasPlace; import org.kaaproject.kaa.server.admin.client.mvp.place.ServerProfileSchemasPlace; import org.kaaproject.kaa.server.admin.client.mvp.view.CtlSchemaView; @@ -39,6 +40,7 @@ import org.kaaproject.kaa.server.admin.client.util.SchemaErrorMessageCustomizer; import org.kaaproject.kaa.server.admin.client.util.Utils; import org.kaaproject.kaa.server.admin.shared.schema.CtlSchemaFormDto; +import org.kaaproject.kaa.server.admin.shared.schema.NotificationSchemaViewDto; import org.kaaproject.kaa.server.admin.shared.schema.ProfileSchemaViewDto; import org.kaaproject.kaa.server.admin.shared.schema.ServerProfileSchemaViewDto; @@ -98,8 +100,8 @@ public void onClick(ClickEvent event) { @Override public void onClick(ClickEvent event) { CTLSchemaMetaInfoDto metaInfo = entity.getMetaInfo(); - metaInfo.setApplicationId(null); - KaaAdmin.getDataSource().updateCtlSchemaScope(metaInfo, new BusyAsyncCallback() { + + KaaAdmin.getDataSource().promoteScopeToTenant(metaInfo.getApplicationId(), metaInfo.getFqn(), new BusyAsyncCallback() { @Override public void onFailureImpl(Throwable caught) { Utils.handleException(caught, detailsView); @@ -256,8 +258,10 @@ public void onSuccessImpl(CtlSchemaFormDto result) { } else if (place.getSchemaType() != null) { if (place.getSchemaType() == SchemaType.ENDPOINT_PROFILE) { goTo(new ProfileSchemasPlace(place.getApplicationId())); - } else { + } else if (place.getSchemaType() == SchemaType.SERVER_PROFILE){ goTo(new ServerProfileSchemasPlace(place.getApplicationId())); + } else { + goTo(new NotificationSchemasPlace(place.getApplicationId())); } } else if (place.getPreviousPlace() != null) { goTo(place.getPreviousPlace()); @@ -379,7 +383,7 @@ public void onSuccessImpl(ProfileSchemaViewDto result) { callback.onSuccess(null); } }); - } else { + } else if (place.getSchemaType() == SchemaType.SERVER_PROFILE){ KaaAdmin.getDataSource().createServerProfileSchemaFormCtlSchema(entity, new BusyAsyncCallback() { @Override @@ -391,6 +395,18 @@ public void onSuccessImpl(ServerProfileSchemaViewDto result) { callback.onSuccess(null); } }); + } else { + KaaAdmin.getDataSource().createNotificationSchemaFormCtlSchema(entity, new BusyAsyncCallback() { + @Override + public void onFailureImpl(Throwable caught) { + callback.onFailure(caught); + } + + @Override + public void onSuccessImpl(NotificationSchemaViewDto notificationSchemaViewDto) { + callback.onSuccess(null); + } + }); } } else { KaaAdmin.getDataSource().editCTLSchemaForm(entity, callback); diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/NotificationSchemaActivity.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/NotificationSchemaActivity.java index dd40b6510a..0b8290898f 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/NotificationSchemaActivity.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/NotificationSchemaActivity.java @@ -21,13 +21,14 @@ import org.kaaproject.kaa.server.admin.client.KaaAdmin; import org.kaaproject.kaa.server.admin.client.mvp.ClientFactory; import org.kaaproject.kaa.server.admin.client.mvp.place.NotificationSchemaPlace; -import org.kaaproject.kaa.server.admin.client.mvp.view.BaseSchemaView; +import org.kaaproject.kaa.server.admin.client.mvp.view.BaseCtlSchemaView; import com.google.gwt.user.client.rpc.AsyncCallback; +import org.kaaproject.kaa.server.admin.shared.schema.CtlSchemaFormDto; +import org.kaaproject.kaa.server.admin.shared.schema.NotificationSchemaViewDto; +import org.kaaproject.kaa.server.admin.client.mvp.place.CtlSchemaPlace.SchemaType; -public class NotificationSchemaActivity - extends - AbstractSchemaActivity { +public class NotificationSchemaActivity extends AbstractBaseCtlSchemaActivity { public NotificationSchemaActivity(NotificationSchemaPlace place, ClientFactory clientFactory) { @@ -35,12 +36,12 @@ public NotificationSchemaActivity(NotificationSchemaPlace place, } @Override - protected NotificationSchemaDto newSchema() { - return new NotificationSchemaDto(); + protected NotificationSchemaViewDto newSchema() { + return new NotificationSchemaViewDto(); } @Override - protected BaseSchemaView getView(boolean create) { + protected BaseCtlSchemaView getView(boolean create) { if (create) { return clientFactory.getCreateNotificationSchemaView(); } else { @@ -50,25 +51,38 @@ protected BaseSchemaView getView(boolean create) { @Override protected void getEntity(String id, - AsyncCallback callback) { - KaaAdmin.getDataSource().getNotificationSchemaForm(id, callback); + AsyncCallback callback) { + KaaAdmin.getDataSource().getNotificationSchemaView(id, callback); } @Override - protected void editEntity(NotificationSchemaDto entity, - AsyncCallback callback) { - KaaAdmin.getDataSource().editNotificationSchemaForm(entity, callback); + protected void editEntity(NotificationSchemaViewDto entity, + AsyncCallback callback) { + KaaAdmin.getDataSource().saveNotificationSchemaView(entity, callback); } @Override - protected void createEmptySchemaForm(AsyncCallback callback) { - KaaAdmin.getDataSource().createCommonEmptySchemaForm(callback); + protected void createEmptyCtlSchemaForm(AsyncCallback callback) { + KaaAdmin.getDataSource().createNewCTLSchemaFormInstance(null, + null, + applicationId, + callback); } @Override public void loadFormData(String fileItemName, - AsyncCallback callback) { + AsyncCallback callback) { KaaAdmin.getDataSource().generateCommonSchemaForm(fileItemName, callback); } + @Override + protected NotificationSchemaPlace existingSchemaPlace(String applicationId, String schemaId) { + return new NotificationSchemaPlace(applicationId, schemaId); + } + + @Override + protected SchemaType getPlaceSchemaType() { + return SchemaType.NOTIFICATION ; + } + } diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/NotificationSchemasActivity.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/NotificationSchemasActivity.java index 031e108772..7b14c0ff95 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/NotificationSchemasActivity.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/activity/NotificationSchemasActivity.java @@ -35,7 +35,7 @@ import com.google.gwt.user.client.rpc.AsyncCallback; -public class NotificationSchemasActivity extends AbstractListActivity { +public class NotificationSchemasActivity extends AbstractBaseCtlSchemasActivity { private String applicationId; @@ -70,20 +70,4 @@ protected void deleteEntity(String id, AsyncCallback callback) { callback.onSuccess((Void)null); } - @Override - protected void onCustomRowAction(RowActionEvent event) { - Integer schemaVersion = Integer.valueOf(event.getClickedId()); - if (event.getAction() == KaaRowAction.DOWNLOAD_SCHEMA) { - KaaAdmin.getDataSource().getRecordData(applicationId, schemaVersion, RecordFiles.NOTIFICATION_SCHEMA, new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - Utils.handleException(caught, listView); - } - @Override - public void onSuccess(String key) { - ServletHelper.downloadRecordLibrary(key); - } - }); - } - } } diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/data/DataSource.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/data/DataSource.java index 6a15e51e48..319fa20655 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/data/DataSource.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/data/DataSource.java @@ -63,15 +63,12 @@ import org.kaaproject.kaa.server.admin.shared.endpoint.EndpointProfileViewDto; import org.kaaproject.kaa.server.admin.shared.plugin.PluginInfoDto; import org.kaaproject.kaa.server.admin.shared.properties.PropertiesDto; -import org.kaaproject.kaa.server.admin.shared.schema.CtlSchemaFormDto; -import org.kaaproject.kaa.server.admin.shared.schema.CtlSchemaReferenceDto; -import org.kaaproject.kaa.server.admin.shared.schema.ProfileSchemaViewDto; -import org.kaaproject.kaa.server.admin.shared.schema.SchemaInfoDto; -import org.kaaproject.kaa.server.admin.shared.schema.ServerProfileSchemaViewDto; +import org.kaaproject.kaa.server.admin.shared.schema.*; import org.kaaproject.kaa.server.admin.shared.services.KaaAdminServiceAsync; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.web.bindery.event.shared.EventBus; +import org.kaaproject.kaa.server.common.dao.model.sql.NotificationSchema; public class DataSource { @@ -699,22 +696,53 @@ protected void onResult(List result) { } - public void editNotificationSchemaForm( - NotificationSchemaDto notificationSchema, - final AsyncCallback callback) { - rpcService.editNotificationSchemaForm(notificationSchema, - new DataCallback(callback) { + public void saveNotificationSchemaView( + NotificationSchemaViewDto notificationSchema, + final AsyncCallback callback) { + rpcService.saveNotificationSchemaView(notificationSchema, + new DataCallback(callback) { @Override - protected void onResult(NotificationSchemaDto result) { + protected void onResult(NotificationSchemaViewDto result) { eventBus.fireEvent(new DataEvent( - NotificationSchemaDto.class)); + NotificationSchemaViewDto.class)); + } + }); + } +// +// public void getNotificationSchemaView(String notificationSchemaId, +// final AsyncCallback callback) { +// rpcService.getNotificationSchemaView(notificationSchemaId, +// new DataCallback(callback) { +// @Override +// protected void onResult(NotificationSchemaViewDto result) { +// } +// }); +// } + + public void createNotificationSchemaFormCtlSchema(CtlSchemaFormDto ctlSchemaForm, + final AsyncCallback callback) { + rpcService.createNotificationSchemaFormCtlSchema(ctlSchemaForm, + new DataCallback(callback) { + @Override + protected void onResult(NotificationSchemaViewDto result) { + eventBus.fireEvent(new DataEvent(NotificationSchemaViewDto.class)); } }); } - public void getNotificationSchemaForm(String notificationSchemaId, - final AsyncCallback callback) { - rpcService.getNotificationSchemaForm(notificationSchemaId, + public void getNotificationSchemaView(String profileSchemaId, + final AsyncCallback callback) { + rpcService.getNotificationSchemaView(profileSchemaId, + new DataCallback(callback) { + @Override + protected void onResult(NotificationSchemaViewDto result) { + } + }); + } + + public void getNotificationSchema(String profileSchemaId, + final AsyncCallback callback) { + rpcService.getNotificationSchema(profileSchemaId, new DataCallback(callback) { @Override protected void onResult(NotificationSchemaDto result) { @@ -856,9 +884,9 @@ protected void onResult(Boolean result) { }); } - public void updateCtlSchemaScope(CTLSchemaMetaInfoDto metaInfo, - final AsyncCallback callback) { - rpcService.updateCTLSchemaMetaInfoScope(metaInfo, + public void promoteScopeToTenant(String applicationId, String fqn, + final AsyncCallback callback) { + rpcService.promoteScopeToTenant(applicationId, fqn, new DataCallback(callback) { @Override protected void onResult(CTLSchemaMetaInfoDto result) { @@ -1288,6 +1316,16 @@ protected void onResult(RecordField result) { }); } + public void getConfigurationRecordDataFromFile(String schema, String fileItemName, + final AsyncCallback callback) { + rpcService.getConfigurationRecordDataFromFile(schema, fileItemName, + new DataCallback(callback) { + @Override + protected void onResult(RecordField result) { + } + }); + } + public void sendNotification( NotificationDto notification, RecordField notificationData, final AsyncCallback callback) { diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/place/CtlSchemaPlace.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/place/CtlSchemaPlace.java index f9b57a7786..44f32f0770 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/place/CtlSchemaPlace.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/place/CtlSchemaPlace.java @@ -182,8 +182,10 @@ public TreePlace createDefaultPreviousPlace() { if (schemaType != null) { if (schemaType == SchemaType.ENDPOINT_PROFILE) { return new ProfileSchemasPlace(applicationId); - } else { + } else if (schemaType == SchemaType.SERVER_PROFILE){ return new ServerProfileSchemasPlace(applicationId); + } else { + return new NotificationSchemasPlace(applicationId); } } else { return new ApplicationCtlSchemasPlace(applicationId); @@ -200,7 +202,8 @@ public TreePlace createDefaultPreviousPlace() { public static enum SchemaType { ENDPOINT_PROFILE, - SERVER_PROFILE + SERVER_PROFILE, + NOTIFICATION } diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/view/notification/NotificationSchemaViewImpl.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/view/notification/NotificationSchemaViewImpl.java index 91fac5c18d..7d27a2e275 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/view/notification/NotificationSchemaViewImpl.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/view/notification/NotificationSchemaViewImpl.java @@ -16,10 +16,11 @@ package org.kaaproject.kaa.server.admin.client.mvp.view.notification; +import org.kaaproject.kaa.server.admin.client.mvp.view.schema.BaseCtlSchemaViewImpl; import org.kaaproject.kaa.server.admin.client.mvp.view.schema.BaseSchemaViewImpl; import org.kaaproject.kaa.server.admin.client.util.Utils; -public class NotificationSchemaViewImpl extends BaseSchemaViewImpl { +public class NotificationSchemaViewImpl extends BaseCtlSchemaViewImpl { public NotificationSchemaViewImpl(boolean create) { super(create); diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/view/notification/NotificationSchemasViewImpl.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/view/notification/NotificationSchemasViewImpl.java index 084a30bdb6..f1950e94a8 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/view/notification/NotificationSchemasViewImpl.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/client/mvp/view/notification/NotificationSchemasViewImpl.java @@ -19,6 +19,7 @@ import org.kaaproject.avro.ui.gwt.client.widget.grid.AbstractGrid; import org.kaaproject.kaa.common.dto.NotificationSchemaDto; import org.kaaproject.kaa.server.admin.client.mvp.view.base.BaseListViewImpl; +import org.kaaproject.kaa.server.admin.client.mvp.view.schema.BaseCtlSchemasGrid; import org.kaaproject.kaa.server.admin.client.mvp.view.schema.BaseSchemasGrid; import org.kaaproject.kaa.server.admin.client.util.Utils; @@ -30,7 +31,7 @@ public NotificationSchemasViewImpl() { @Override protected AbstractGrid createGrid() { - return new BaseSchemasGrid(); + return new BaseCtlSchemasGrid(); } @Override diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/controller/KaaAdminController.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/controller/KaaAdminController.java index 1ae67c6ed6..cac5f6da61 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/controller/KaaAdminController.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/controller/KaaAdminController.java @@ -17,6 +17,7 @@ package org.kaaproject.kaa.server.admin.controller; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.util.StringUtils.isEmpty; import java.io.IOException; import java.nio.charset.Charset; @@ -75,6 +76,7 @@ import org.kaaproject.kaa.server.admin.services.entity.CreateUserResult; import org.kaaproject.kaa.server.admin.services.util.Utils; import org.kaaproject.kaa.server.admin.servlet.ServletUtils; +import org.kaaproject.kaa.server.admin.shared.schema.NotificationSchemaViewDto; import org.kaaproject.kaa.server.admin.shared.services.KaaAdminService; import org.kaaproject.kaa.server.admin.shared.services.KaaAdminServiceException; import org.kaaproject.kaa.server.admin.shared.services.KaaAuthService; @@ -151,32 +153,34 @@ public void handleKaaAdminServiceException(KaaAdminServiceException ex, HttpServ try { ServiceErrorCode errorCode = ex.getErrorCode(); switch (errorCode) { - case NOT_AUTHORIZED: - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage()); - break; - case PERMISSION_DENIED: - response.sendError(HttpServletResponse.SC_FORBIDDEN, ex.getMessage()); - break; - case INVALID_ARGUMENTS: - response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage()); - break; - case INVALID_SCHEMA: - response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage()); - break; - case FILE_NOT_FOUND: - response.sendError(HttpServletResponse.SC_NOT_FOUND, ex.getMessage()); - break; - case ITEM_NOT_FOUND: - response.sendError(HttpServletResponse.SC_NOT_FOUND, ex.getMessage()); - break; - case BAD_REQUEST_PARAMS: - response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage()); - break; - case GENERAL_ERROR: - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); - break; - default: - break; + case NOT_AUTHORIZED: + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage()); + break; + case PERMISSION_DENIED: + response.sendError(HttpServletResponse.SC_FORBIDDEN, ex.getMessage()); + break; + case INVALID_ARGUMENTS: + response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage()); + break; + case INVALID_SCHEMA: + response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage()); + break; + case FILE_NOT_FOUND: + response.sendError(HttpServletResponse.SC_NOT_FOUND, ex.getMessage()); + break; + case ITEM_NOT_FOUND: + response.sendError(HttpServletResponse.SC_NOT_FOUND, ex.getMessage()); + break; + case BAD_REQUEST_PARAMS: + response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage()); + break; + case GENERAL_ERROR: + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); + break; + case CONFLICT: + response.sendError(HttpServletResponse.SC_CONFLICT, ex.getMessage()); + default: + break; } } catch (IOException e) { LOG.error("Can't handle exception", e); @@ -435,6 +439,11 @@ public TenantUserDto getTenant(@PathVariable("userId") String userId) throws Kaa @ResponseBody public TenantUserDto editTenant(@RequestBody TenantUserDto tenantUser) throws KaaAdminServiceException { try { + if (isEmpty(tenantUser.getAuthority())) { + tenantUser.setAuthority(KaaAuthorityDto.TENANT_ADMIN); + } else if (!KaaAuthorityDto.TENANT_ADMIN.equals(tenantUser.getAuthority())) { + throw new KaaAdminServiceException("Incorrect authority for tenant. Authority must be TENANT_ADMIN.", ServiceErrorCode.INVALID_ARGUMENTS); + } CreateUserResult result = userFacade.saveUserDto(tenantUser, passwordEncoder); tenantUser.setExternalUid(result.getUserId().toString()); TenantUserDto tenant = kaaAdminService.editTenant(tenantUser); @@ -447,20 +456,6 @@ public TenantUserDto editTenant(@RequestBody TenantUserDto tenantUser) throws Ka } } - /** - * Delete tenant by user id. - * - * @param userId - * the user id - * @throws KaaAdminServiceException - * the kaa admin service exception - */ - @RequestMapping(value = "delTenant", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) - public void deleteTenant(@RequestParam(value = "userId") String userId) throws KaaAdminServiceException { - kaaAdminService.deleteTenant(userId); - } - /** * Gets all applications. * @@ -474,23 +469,6 @@ public List getApplications() throws KaaAdminServiceException { return kaaAdminService.getApplications(); } - /** - * Gets the application by its id. - * - * @param applicationId - * the application id - * @return the application dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getApplicationByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "application/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public ApplicationDto getApplication(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getApplication(applicationId); - } - /** * Gets the application by its application token. * @@ -500,7 +478,7 @@ public ApplicationDto getApplication(@PathVariable String applicationId) throws * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "application/token/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "application/{applicationToken}", method = RequestMethod.GET) @ResponseBody public ApplicationDto getApplicationByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getApplicationByApplicationToken(applicationToken); @@ -630,23 +608,6 @@ public void deleteUser(@RequestParam(value = "userId") String userId) throws Kaa kaaAdminService.deleteUser(userId); } - /** - * Gets the schema versions by application id. - * - * @param applicationId - * the application id - * @return the schema versions - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getSchemaVersionsByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "schemaVersions/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public SchemaVersions getSchemaVersionsByApplicationId(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getSchemaVersionsByApplicationId(applicationId); - } - /** * Gets the schema versions by application token. * @@ -656,7 +617,7 @@ public SchemaVersions getSchemaVersionsByApplicationId(@PathVariable String appl * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "schemaVersionsByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "schemaVersions/{applicationToken}", method = RequestMethod.GET) @ResponseBody public SchemaVersions getSchemaVersionsByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getSchemaVersionsByApplicationToken(applicationToken); @@ -739,23 +700,6 @@ public SdkProfileDto getSdkProfile(@PathVariable String sdkProfileId) throws Kaa return kaaAdminService.getSdkProfile(sdkProfileId); } - /** - * Returns a list of SDK profiles for the given application. - * - * @param applicationId - * the application id - * @return the list sdk profile dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getSdkProfilesByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "sdkProfiles/{applicationId}") - @ResponseBody - public List getSdkProfilesByApplicationId(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getSdkProfilesByApplicationId(applicationId); - } - /** * Returns a list of SDK profiles for the given application. * @@ -765,7 +709,7 @@ public List getSdkProfilesByApplicationId(@PathVariable String ap * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "sdkProfilesByAppToken/{applicationToken}") + @RequestMapping(value = "sdkProfiles/{applicationToken}") @ResponseBody public List getSdkProfilesByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getSdkProfilesByApplicationToken(applicationToken); @@ -787,30 +731,6 @@ public void flushSdkCache() throws KaaAdminServiceException { } } - /** - * Saves a CTL schema. - * - * @param body - * the ctl body - * @param applicationId - * id of the application - * @param tenantId - * id of the tenant - * - * @throws KaaAdminServiceException - * the kaa admin service exception - * - * @return CTL schema info - * @deprecated As of release 0.9.0, replaced by {@link #saveCTLSchemaWithAppToken(String, String, String)} - */ - @Deprecated - @RequestMapping(value = "CTL/saveSchema", params = { "body" }, method = RequestMethod.POST) - @ResponseBody - public CTLSchemaDto saveCTLSchema(@RequestParam String body, @RequestParam(required = false) String tenantId, - @RequestParam(required = false) String applicationId) throws KaaAdminServiceException { - return kaaAdminService.saveCTLSchema(body, tenantId, applicationId); - } - /** * Saves a CTL schema. * @@ -826,38 +746,13 @@ public CTLSchemaDto saveCTLSchema(@RequestParam String body, @RequestParam(requi * * @return CTL schema info */ - @RequestMapping(value = "CTL/appToken/saveSchema", params = { "body" }, method = RequestMethod.POST) + @RequestMapping(value = "CTL/saveSchema", params = { "body" }, method = RequestMethod.POST) @ResponseBody public CTLSchemaDto saveCTLSchemaWithAppToken(@RequestParam String body, @RequestParam(required = false) String tenantId, @RequestParam(required = false) String applicationToken) throws KaaAdminServiceException { return kaaAdminService.saveCTLSchemaWithAppToken(body, tenantId, applicationToken); } - /** - * Removes a CTL schema by its fully qualified name and version number. - * - * @param fqn - * the fqn - * @param version - * the version - * @param tenantId - * id of the tenant - * @param applicationId - * id of the application - * - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #deleteCTLSchemaByFqnVersionTenantIdAndApplicationToken(String, int, String, String)} - */ - @Deprecated - @RequestMapping(value = "CTL/deleteSchema", params = { "fqn", "version" }, method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) - public void deleteCTLSchemaByFqnVersionTenantIdAndApplicationId(@RequestParam String fqn, @RequestParam int version, - @RequestParam(required = false) String tenantId, - @RequestParam(required = false) String applicationId) throws KaaAdminServiceException { - kaaAdminService.deleteCTLSchemaByFqnVersionTenantIdAndApplicationId(fqn, version, tenantId, applicationId); - } - /** * Removes a CTL schema by its fully qualified name and version number. * @@ -873,7 +768,7 @@ public void deleteCTLSchemaByFqnVersionTenantIdAndApplicationId(@RequestParam St * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "CTL/appToken/deleteSchema", params = { "fqn", "version" }, method = RequestMethod.POST) + @RequestMapping(value = "CTL/deleteSchema", params = { "fqn", "version" }, method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) public void deleteCTLSchemaByFqnVersionTenantIdAndApplicationToken(@RequestParam String fqn, @RequestParam int version, @RequestParam(required = false) String tenantId, @@ -882,35 +777,6 @@ public void deleteCTLSchemaByFqnVersionTenantIdAndApplicationToken(@RequestParam kaaAdminService.deleteCTLSchemaByFqnVersionTenantIdAndApplicationToken(fqn, version, tenantId, applicationToken); } - /** - * Retrieves a CTL schema by its fully qualified name and version number. - * - * @param fqn - * the fqn - * @param version - * the version - * @param tenantId - * id of the tenant - * @param applicationId - * id of the application - * - * @throws KaaAdminServiceException - * the kaa admin service exception - * - * @return CTL schema info - * @deprecated As of release 0.9.0, replaced by {@link #getCTLSchemaByFqnVersionTenantIdAndApplicationToken(String, int, String, String)} - */ - @Deprecated - @RequestMapping(value = "CTL/getSchema", params = { "fqn", "version" }, method = RequestMethod.GET) - @ResponseBody - public CTLSchemaDto getCTLSchemaByFqnVersionTenantIdAndApplicationId(@RequestParam String fqn, - @RequestParam int version, - @RequestParam(required = false) String tenantId, - @RequestParam(required = false) String applicationId) - throws KaaAdminServiceException { - return kaaAdminService.getCTLSchemaByFqnVersionTenantIdAndApplicationId(fqn, version, tenantId, applicationId); - } - /** * Retrieves a CTL schema by its fully qualified name and version number. * @@ -928,7 +794,7 @@ public CTLSchemaDto getCTLSchemaByFqnVersionTenantIdAndApplicationId(@RequestPar * * @return CTL schema info */ - @RequestMapping(value = "CTL/appToken/getSchema", params = { "fqn", "version" }, method = RequestMethod.GET) + @RequestMapping(value = "CTL/getSchema", params = { "fqn", "version" }, method = RequestMethod.GET) @ResponseBody public CTLSchemaDto getCTLSchemaByFqnVersionTenantIdAndApplicationToken(@RequestParam String fqn, @RequestParam int version, @@ -955,32 +821,6 @@ public CTLSchemaDto getCTLSchemaById(@RequestParam String id) throws KaaAdminSer return kaaAdminService.getCTLSchemaById(id); } - /** - * Checks if CTL schema with same fqn is already exists in the sibling application. - * - * @param fqn - * the fqn - * @param tenantId - * id of the tenant - * @param applicationId - * id of the application - * - * @throws KaaAdminServiceException - * the kaa admin service exception - * - * @return true if CTL schema with same fqn is already exists in other scope - * @deprecated As of release 0.9.0, replaced by {@link #checkFqnExistsWithAppToken(String, String, String)} - */ - @Deprecated - @RequestMapping(value = "CTL/checkFqn", params = { "fqn" }, method = RequestMethod.GET) - @ResponseBody - public boolean checkFqnExists(@RequestParam String fqn, - @RequestParam(required = false) String tenantId, - @RequestParam(required = false) String applicationId) - throws KaaAdminServiceException { - return kaaAdminService.checkFqnExists(fqn, tenantId, applicationId); - } - /** * Checks if CTL schema with same fqn is already exists in the sibling application. * @@ -996,7 +836,7 @@ public boolean checkFqnExists(@RequestParam String fqn, * * @return true if CTL schema with same fqn is already exists in other scope */ - @RequestMapping(value = "CTL/appToken/checkFqn", params = { "fqn" }, method = RequestMethod.GET) + @RequestMapping(value = "CTL/checkFqn", params = { "fqn" }, method = RequestMethod.GET) @ResponseBody public boolean checkFqnExistsWithAppToken(@RequestParam String fqn, @RequestParam(required = false) String tenantId, @@ -1006,26 +846,24 @@ public boolean checkFqnExistsWithAppToken(@RequestParam String fqn, } /** - * Update existing CTL schema meta info scope by the given CTL schema meta info object. + * Promote existing CTL schema meta info from application to tenant scope * - * @param ctlSchemaMetaInfo - * the CTL schema meta info object. - * - * @throws KaaAdminServiceException - * the kaa admin service exception - * - * @return CTLSchemaMetaInfoDto the updated CTL schema meta info object. - */ - @RequestMapping(value = "CTL/updateScope", method = RequestMethod.POST) + * @param applicationId the id of application where schema was created + * @param fqn the fqn of promoting CTL schema + * @throws KaaAdminServiceException the kaa admin service exception + * + * @return CTLSchemaMetaInfoDto the promoted CTL schema meta info object. + */ + @RequestMapping(value = "CTL/promoteScopeToTenant", method = RequestMethod.POST) @ResponseBody - public CTLSchemaMetaInfoDto updateCTLSchemaMetaInfoScope(@RequestBody CTLSchemaMetaInfoDto ctlSchemaMetaInfo) + public CTLSchemaMetaInfoDto promoteScopeToTenant(@RequestParam String applicationId, @RequestParam String fqn) throws KaaAdminServiceException { - return kaaAdminService.updateCTLSchemaMetaInfoScope(ctlSchemaMetaInfo); + return kaaAdminService.promoteScopeToTenant(applicationId, fqn); } /** * Retrieves a list of available system CTL schemas. - * + * * @throws KaaAdminServiceException * the kaa admin service exception * @return CTL schema metadata list @@ -1035,10 +873,10 @@ public CTLSchemaMetaInfoDto updateCTLSchemaMetaInfoScope(@RequestBody CTLSchemaM public List getSystemLevelCTLSchemas() throws KaaAdminServiceException { return kaaAdminService.getSystemLevelCTLSchemas(); } - + /** * Retrieves a list of available CTL schemas for tenant. - * + * * @throws KaaAdminServiceException * the kaa admin service exception * @return CTL schema metadata list @@ -1048,24 +886,6 @@ public List getSystemLevelCTLSchemas() throws KaaAdminServ public List getTenantLevelCTLSchemas() throws KaaAdminServiceException { return kaaAdminService.getTenantLevelCTLSchemas(); } - - /** - * Retrieves a list of available CTL schemas for application. - * - * @param applicationId - * id of the application - * - * @throws KaaAdminServiceException - * the kaa admin service exception - * @return CTL schema metadata list - * @deprecated As of release 0.9.0, replaced by {@link #getApplicationLevelCTLSchemasByAppToken(String)} - */ - @Deprecated - @RequestMapping(value = "CTL/getApplicationSchemas/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getApplicationLevelCTLSchemas(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getApplicationLevelCTLSchemas(applicationId); - } /** * Retrieves a list of available CTL schemas for application. @@ -1077,54 +897,12 @@ public List getApplicationLevelCTLSchemas(@PathVariable St * the kaa admin service exception * @return CTL schema metadata list */ - @RequestMapping(value = "CTL/appToken/getApplicationSchemas/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "CTL/getApplicationSchemas/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getApplicationLevelCTLSchemasByAppToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getApplicationLevelCTLSchemasByAppToken(applicationToken); } - /** - * Exports a CTL schema and, depending on the export method specified, all - * of its dependencies. - * - * @param fqn - * - the schema fqn - * @param version - * - the schema version - * @param method - * - the schema export method - * @param applicationId - * id of the application - * @param request - * - the http request - * @param response - * - the http response - * - * @see CTLSchemaExportMethod - * - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #exportCTLSchemaByAppToken(String, int, String, String, HttpServletRequest, HttpServletResponse)} - */ - @Deprecated - @RequestMapping(value = "CTL/exportSchema", params = { "fqn", "version", "method" }, method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) - public void exportCTLSchema(@RequestParam String fqn, @RequestParam int version, @RequestParam String method, - @RequestParam(required = false) String applicationId, - HttpServletRequest request, HttpServletResponse response) throws KaaAdminServiceException { - try { - FileData output = kaaAdminService.exportCTLSchema(fqn, version, applicationId, CTLSchemaExportMethod.valueOf(method.toUpperCase())); - ServletUtils.prepareDisposition(request, response, output.getFileName()); - response.setContentType(output.getContentType()); - response.setContentLength(output.getFileData().length); - response.setBufferSize(BUFFER); - response.getOutputStream().write(output.getFileData()); - response.flushBuffer(); - } catch (Exception cause) { - throw Utils.handleException(cause); - } - } - /** * Exports a CTL schema and, depending on the export method specified, all * of its dependencies. @@ -1147,7 +925,7 @@ public void exportCTLSchema(@RequestParam String fqn, @RequestParam int version, * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "CTL/appToken/exportSchema", params = { "fqn", "version", "method" }, method = RequestMethod.POST) + @RequestMapping(value = "CTL/exportSchema", params = { "fqn", "version", "method" }, method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) public void exportCTLSchemaByAppToken(@RequestParam String fqn, @RequestParam int version, @RequestParam String method, @RequestParam(required = false) String applicationToken, @@ -1165,24 +943,6 @@ public void exportCTLSchemaByAppToken(@RequestParam String fqn, @RequestParam in } } - /** - * Gets the server profile schemas by application id. - * - * @param applicationId - * the application id - * @return the list profile schema dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getServerProfileSchemasByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "serverProfileSchemas/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getServerProfileSchemasByApplicationId(@PathVariable String applicationId) - throws KaaAdminServiceException { - return kaaAdminService.getServerProfileSchemasByApplicationId(applicationId); - } - /** * Gets the server profile schemas by application token. * @@ -1192,7 +952,7 @@ public List getServerProfileSchemasByApplicationId(@Path * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "serverProfileSchemasByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "serverProfileSchemas/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getServerProfileSchemasByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { @@ -1230,24 +990,6 @@ public ServerProfileSchemaDto saveServerProfileSchema(@RequestBody ServerProfile return kaaAdminService.saveServerProfileSchema(serverProfileSchema); } - /** - * Gets the profile schemas by application id. - * - * @param applicationId - * the application id - * @return the list of endpoint profile schema dto objects - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getProfileSchemasByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "profileSchemas/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getProfileSchemasByApplicationId(@PathVariable String applicationId) - throws KaaAdminServiceException { - return kaaAdminService.getProfileSchemasByApplicationId(applicationId); - } - /** * Gets the profile schemas by application token. * @@ -1257,7 +999,7 @@ public List getProfileSchemasByApplicationId(@PathVari * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "profileSchemasByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "profileSchemas/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getProfileSchemasByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { @@ -1266,7 +1008,7 @@ public List getProfileSchemasByApplicationToken(@PathV /** * Gets the profile schema by its id. - * + * * @param profileSchemaId * the profile schema id * @return the endpoint profile schema dto @@ -1294,24 +1036,6 @@ public EndpointProfileSchemaDto saveProfileSchema(@RequestBody EndpointProfileSc return kaaAdminService.saveProfileSchema(profileSchema); } - /** - * Gets the configuration schemas by application id. - * - * @param applicationId - * the application id - * @return the сonfiguration schema dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getConfigurationSchemasByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "configurationSchemas/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getConfigurationSchemasByApplicationId(@PathVariable String applicationId) - throws KaaAdminServiceException { - return kaaAdminService.getConfigurationSchemasByApplicationId(applicationId); - } - /** * Gets the configuration schemas by application token. * @@ -1321,7 +1045,7 @@ public List getConfigurationSchemasByApplicationId(@Path * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "configurationSchemasByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "configurationSchemas/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getConfigurationSchemasByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { @@ -1378,24 +1102,6 @@ public ConfigurationSchemaDto editConfigurationSchema(@RequestBody Configuration return kaaAdminService.editConfigurationSchema(configurationSchema, null); } - /** - * Gets the notification schemas by application id. - * - * @param applicationId - * the application id - * @return the list notification schema dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getNotificationSchemasByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "notificationSchemas/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getNotificationSchemasByApplicationId(@PathVariable String applicationId) - throws KaaAdminServiceException { - return kaaAdminService.getNotificationSchemasByApplicationId(applicationId); - } - /** * Gets the notification schemas by application token. * @@ -1405,30 +1111,13 @@ public List getNotificationSchemasByApplicationId(@PathVa * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "notificationSchemasByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "notificationSchemas/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getNotificationSchemasByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getNotificationSchemasByApplicationToken(applicationToken); } - /** - * Gets the user notification schemas by application id. - * - * @param applicationId - * the application id - * @return the list schema dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getUserNotificationSchemasByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "userNotificationSchemas/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getUserNotificationSchemasByApplicationId(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getUserNotificationSchemasByApplicationId(applicationId); - } - /** * Gets the user notification schemas by application token. * @@ -1438,7 +1127,7 @@ public List getUserNotificationSchemasByApplicationId(@PathVariable * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "userNotificationSchemasByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "userNotificationSchemas/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getUserNotificationSchemasByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getUserNotificationSchemasByApplicationToken(applicationToken); @@ -1464,18 +1153,15 @@ public NotificationSchemaDto getNotificationSchema(@PathVariable String notifica * * @param notificationSchema * the notification schema - * @param file - * the file * @return the notification schema dto * @throws KaaAdminServiceException * the kaa admin service exception */ @RequestMapping(value = "createNotificationSchema", method = RequestMethod.POST, consumes = { "multipart/mixed", "multipart/form-data" }) @ResponseBody - public NotificationSchemaDto createNotificationSchema(@RequestPart("notificationSchema") NotificationSchemaDto notificationSchema, - @RequestPart("file") MultipartFile file) throws KaaAdminServiceException { - byte[] data = getFileContent(file); - return kaaAdminService.editNotificationSchema(notificationSchema, data); + public NotificationSchemaDto createNotificationSchema(@RequestPart("notificationSchema") NotificationSchemaDto notificationSchema/*, @RequestPart("file") MultipartFile file*/) throws KaaAdminServiceException { + //byte[] data = getFileContent(file); + return kaaAdminService.editNotificationSchema(notificationSchema); } /** @@ -1491,24 +1177,7 @@ public NotificationSchemaDto createNotificationSchema(@RequestPart("notification @ResponseBody public NotificationSchemaDto editNotificationSchema(@RequestBody NotificationSchemaDto notificationSchema) throws KaaAdminServiceException { - return kaaAdminService.editNotificationSchema(notificationSchema, null); - } - - /** - * Gets all log schemas by application id. - * - * @param applicationId - * the application id - * @return the list log schema dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getLogSchemasByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "logSchemas/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getLogSchemasByApplicationId(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getLogSchemasByApplicationId(applicationId); + return kaaAdminService.editNotificationSchema(notificationSchema); } /** @@ -1520,7 +1189,7 @@ public List getLogSchemasByApplicationId(@PathVariable String appl * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "logSchemasByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "logSchemas/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getLogSchemasByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getLogSchemasByApplicationToken(applicationToken); @@ -1593,23 +1262,6 @@ public LogSchemaDto editLogSchema(@RequestBody LogSchemaDto logSchema) throws Ka return kaaAdminService.editLogSchema(logSchema, null); } - /** - * Gets all log appenders by application id. - * - * @param applicationId - * the application id - * @return the list log appender dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getLogAppendersByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "logAppenders/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getLogAppendersByApplicationId(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getRestLogAppendersByApplicationId(applicationId); - } - /** * Gets all log appenders by application token. * @@ -1619,7 +1271,7 @@ public List getLogAppendersByApplicationId(@PathVariable String * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "logAppendersByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "logAppenders/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getLogAppendersByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getRestLogAppendersByApplicationToken(applicationToken); @@ -1669,23 +1321,6 @@ public void deleteLogAppender(@RequestParam(value = "logAppenderId") String logA kaaAdminService.deleteLogAppender(logAppenderId); } - /** - * Gets all user verifiers by application id. - * - * @param applicationId - * the application id - * @return the list user verifier dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getUserVerifiersByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "userVerifiers/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getUserVerifiersByApplicationId(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getRestUserVerifiersByApplicationId(applicationId); - } - /** * Gets all user verifiers by application token. * @@ -1695,7 +1330,7 @@ public List getUserVerifiersByApplicationId(@PathVariable Strin * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "userVerifiersByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "userVerifiers/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getUserVerifiersByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getRestUserVerifiersByApplicationToken(applicationToken); @@ -1803,23 +1438,6 @@ public void getRecordSchema(@RequestBody RecordKey key, HttpServletRequest reque } } - /** - * Gets all endpoint groups by application id. - * - * @param applicationId - * the application id - * @return the list endpoint group dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getEndpointGroupsByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "endpointGroups/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getEndpointGroupsByApplicationId(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getEndpointGroupsByApplicationId(applicationId); - } - /** * Gets all endpoint groups by application token. * @@ -1829,7 +1447,7 @@ public List getEndpointGroupsByApplicationId(@PathVariable Str * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "endpointGroupsByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "endpointGroups/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getEndpointGroupsByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getEndpointGroupsByApplicationToken(applicationToken); @@ -2117,23 +1735,6 @@ public void deleteConfigurationRecord(@RequestParam(value = "schemaId") String s kaaAdminService.deleteConfigurationRecord(schemaId, endpointGroupId); } - /** - * Gets all topics by application id. - * - * @param applicationId - * the application id - * @return the topic dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getTopicsByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "topics/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getTopicsByApplicationId(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getTopicsByApplicationId(applicationId); - } - /** * Gets all topics by application token. * @@ -2143,7 +1744,7 @@ public List getTopicsByApplicationId(@PathVariable String applicationI * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "topicsByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "topics/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getTopicsByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getTopicsByApplicationToken(applicationToken); @@ -2382,24 +1983,6 @@ public List getEventClassesByFamilyIdVersionAndType( return kaaAdminService.getEventClassesByFamilyIdVersionAndType(eventClassFamilyId, version, type); } - /** - * Gets all application event family maps by application id. - * - * @param applicationId - * the application id - * @return list the application event family map dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getApplicationEventFamilyMapsByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "applicationEventMaps/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getApplicationEventFamilyMapsByApplicationId(@PathVariable String applicationId) - throws KaaAdminServiceException { - return kaaAdminService.getApplicationEventFamilyMapsByApplicationId(applicationId); - } - /** * Gets all application event family maps by application token. * @@ -2409,7 +1992,7 @@ public List getApplicationEventFamilyMapsByApplica * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "applicationEventMapsByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "applicationEventMaps/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getApplicationEventFamilyMapsByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { @@ -2448,23 +2031,6 @@ public ApplicationEventFamilyMapDto editApplicationEventFamilyMap(@RequestBody A return kaaAdminService.editApplicationEventFamilyMap(applicationEventFamilyMap); } - /** - * Gets all vacant event class families by application id. - * - * @param applicationId - * the application id - * @return the list ecf info dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getVacantEventClassFamiliesByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "vacantEventClassFamilies/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getVacantEventClassFamiliesByApplicationId(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getVacantEventClassFamiliesByApplicationId(applicationId); - } - /** * Gets all vacant event class families by application token. * @@ -2474,29 +2040,12 @@ public List getVacantEventClassFamiliesByApplicationId(@PathVariable * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "vacantEventClassFamiliesByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "vacantEventClassFamilies/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getVacantEventClassFamiliesByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getVacantEventClassFamiliesByApplicationToken(applicationToken); } - /** - * Gets all event class families by application id. - * - * @param applicationId - * the application id - * @return the list aef map info dto - * @throws KaaAdminServiceException - * the kaa admin service exception - * @deprecated As of release 0.9.0, replaced by {@link #getEventClassFamiliesByApplicationToken(String)} - */ - @Deprecated - @RequestMapping(value = "eventClassFamilies/{applicationId}", method = RequestMethod.GET) - @ResponseBody - public List getEventClassFamiliesByApplicationId(@PathVariable String applicationId) throws KaaAdminServiceException { - return kaaAdminService.getEventClassFamiliesByApplicationId(applicationId); - } - /** * Gets all event class families by application token. * @@ -2506,7 +2055,7 @@ public List getEventClassFamiliesByApplicationId(@PathVariable St * @throws KaaAdminServiceException * the kaa admin service exception */ - @RequestMapping(value = "eventClassFamiliesByAppToken/{applicationToken}", method = RequestMethod.GET) + @RequestMapping(value = "eventClassFamilies/{applicationToken}", method = RequestMethod.GET) @ResponseBody public List getEventClassFamiliesByApplicationToken(@PathVariable String applicationToken) throws KaaAdminServiceException { return kaaAdminService.getEventClassFamiliesByApplicationToken(applicationToken); diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/services/KaaAdminServiceImpl.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/services/KaaAdminServiceImpl.java index 728eac7d0b..4f130e04e9 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/services/KaaAdminServiceImpl.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/services/KaaAdminServiceImpl.java @@ -33,11 +33,17 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import org.apache.avro.Schema; import org.apache.avro.generic.GenericRecord; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.node.ArrayNode; +import org.codehaus.jackson.node.ObjectNode; import org.hibernate.StaleObjectStateException; import org.kaaproject.avro.ui.converter.CtlSource; import org.kaaproject.avro.ui.converter.FormAvroConverter; @@ -119,17 +125,14 @@ import org.kaaproject.kaa.server.admin.shared.endpoint.EndpointProfileViewDto; import org.kaaproject.kaa.server.admin.shared.plugin.PluginInfoDto; import org.kaaproject.kaa.server.admin.shared.properties.PropertiesDto; -import org.kaaproject.kaa.server.admin.shared.schema.CtlSchemaExportKey; -import org.kaaproject.kaa.server.admin.shared.schema.CtlSchemaFormDto; -import org.kaaproject.kaa.server.admin.shared.schema.CtlSchemaReferenceDto; -import org.kaaproject.kaa.server.admin.shared.schema.ProfileSchemaViewDto; -import org.kaaproject.kaa.server.admin.shared.schema.SchemaInfoDto; -import org.kaaproject.kaa.server.admin.shared.schema.ServerProfileSchemaViewDto; +import org.kaaproject.kaa.server.admin.shared.schema.*; import org.kaaproject.kaa.server.admin.shared.services.KaaAdminService; import org.kaaproject.kaa.server.admin.shared.services.KaaAdminServiceException; import org.kaaproject.kaa.server.admin.shared.services.ServiceErrorCode; +import org.kaaproject.kaa.server.common.core.schema.BaseSchema; import org.kaaproject.kaa.server.common.core.schema.KaaSchemaFactoryImpl; import org.kaaproject.kaa.server.common.dao.exception.NotFoundException; +import org.kaaproject.kaa.server.common.dao.model.sql.CTLSchema; import org.kaaproject.kaa.server.common.plugin.KaaPluginConfig; import org.kaaproject.kaa.server.common.plugin.PluginConfig; import org.kaaproject.kaa.server.common.plugin.PluginType; @@ -229,18 +232,18 @@ public EndpointProfileViewDto getEndpointProfileViewByKeyHash(String endpointPro } } EndpointProfileSchemaDto clientProfileSchema = controlService.getProfileSchemaByApplicationIdAndVersion( - endpointProfile.getApplicationId(), - endpointProfile.getClientProfileVersion()); + endpointProfile.getApplicationId(), + endpointProfile.getClientProfileVersion()); ServerProfileSchemaDto serverProfileSchema = controlService.getServerProfileSchemaByApplicationIdAndVersion( - endpointProfile.getApplicationId(), - endpointProfile.getServerProfileVersion()); + endpointProfile.getApplicationId(), + endpointProfile.getServerProfileVersion()); endpointProfileView.setProfileSchemaName(clientProfileSchema.getName()); endpointProfileView.setProfileSchemaVersion(clientProfileSchema.toVersionDto()); endpointProfileView.setServerProfileSchemaName(serverProfileSchema.getName()); endpointProfileView.setServerProfileSchemaVersion(serverProfileSchema.toVersionDto()); - endpointProfileView.setProfileRecord(createRecordFieldFromCtlSchemaAndBody(clientProfileSchema.getCtlSchemaId(), + endpointProfileView.setProfileRecord(createRecordFieldFromCtlSchemaAndBody(clientProfileSchema.getCtlSchemaId(), endpointProfile.getClientProfileBody())); - endpointProfileView.setServerProfileRecord(createRecordFieldFromCtlSchemaAndBody(serverProfileSchema.getCtlSchemaId(), + endpointProfileView.setServerProfileRecord(createRecordFieldFromCtlSchemaAndBody(serverProfileSchema.getCtlSchemaId(), endpointProfile.getServerProfileBody())); List topics = new ArrayList<>(); if (endpointProfile.getSubscriptions() != null) { @@ -262,7 +265,7 @@ public EndpointProfileViewDto getEndpointProfileViewByKeyHash(String endpointPro throw Utils.handleException(e); } } - + private RecordField createRecordFieldFromCtlSchemaAndBody(String ctlSchemaId, String body) throws KaaAdminServiceException { try { RecordField recordField; @@ -350,34 +353,34 @@ public EndpointProfileBodyDto getEndpointProfileBodyByKeyHash(String endpointPro checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { EndpointProfileBodyDto profileBodyDto = controlService.getEndpointProfileBodyByKeyHash(endpointProfileKeyHash); - if (profileBodyDto == null) { - throw new KaaAdminServiceException("Requested item was not found!", ServiceErrorCode.ITEM_NOT_FOUND); - } + Utils.checkNotNull(profileBodyDto); checkApplicationId(profileBodyDto.getAppId()); return profileBodyDto; } catch (Exception e) { throw Utils.handleException(e); } } - + @Override public EndpointProfileDto updateServerProfile(String endpointKeyHash, - int serverProfileVersion, String serverProfileBody) + int serverProfileVersion, String serverProfileBody) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { - EndpointProfileDto profileDto = controlService.getEndpointProfileByKeyHash(endpointKeyHash); + EndpointProfileDto profileDto = controlService.getEndpointProfileByKeyHash(endpointKeyHash); + Utils.checkNotNull(profileDto); checkApplicationId(profileDto.getApplicationId()); ServerProfileSchemaDto serverProfileSchema = controlService.getServerProfileSchemaByApplicationIdAndVersion( profileDto.getApplicationId(), serverProfileVersion); + Utils.checkNotNull(serverProfileSchema); RecordField record; try { - record = createRecordFieldFromCtlSchemaAndBody(serverProfileSchema.getCtlSchemaId(), + record = createRecordFieldFromCtlSchemaAndBody(serverProfileSchema.getCtlSchemaId(), serverProfileBody); } catch (Exception e) { LOG.error("Provided server profile body is not valid: ", e); - throw new KaaAdminServiceException("Provided server profile body is not valid: " - + e.getMessage(), ServiceErrorCode.BAD_REQUEST_PARAMS); + throw new KaaAdminServiceException("Provided server profile body is not valid: " + + e.getMessage(), ServiceErrorCode.BAD_REQUEST_PARAMS); } if (!record.isValid()) { throw new KaaAdminServiceException("Provided server profile body is not valid!", ServiceErrorCode.BAD_REQUEST_PARAMS); @@ -391,7 +394,7 @@ record = createRecordFieldFromCtlSchemaAndBody(serverProfileSchema.getCtlSchemaI @Override public EndpointProfileDto updateServerProfile(String endpointKeyHash, - int serverProfileVersion, RecordField serverProfileRecord) + int serverProfileVersion, RecordField serverProfileRecord) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { @@ -410,6 +413,7 @@ public void removeEndpointProfileByKeyHash(String endpointKeyHash) throws KaaAdm try { EndpointProfileDto endpointProfile = controlService.getEndpointProfileByKeyHash(endpointKeyHash); Utils.checkNotNull(endpointProfile); + checkApplicationId(endpointProfile.getApplicationId()); controlService.removeEndpointProfile(endpointProfile); } catch (Exception cause) { throw Utils.handleException(cause); @@ -547,7 +551,7 @@ public org.kaaproject.kaa.common.dto.admin.UserDto getUserProfile() throws KaaAd Utils.checkNotNull(user); org.kaaproject.kaa.common.dto.admin.UserDto result = new org.kaaproject.kaa.common.dto.admin.UserDto(user.getId().toString(), user.getUsername(), user.getFirstName(), user.getLastName(), user.getMail(), KaaAuthorityDto.valueOf(user - .getAuthorities().iterator().next().getAuthority())); + .getAuthorities().iterator().next().getAuthority())); return result; } catch (Exception e) { throw Utils.handleException(e); @@ -578,7 +582,7 @@ public org.kaaproject.kaa.common.dto.admin.UserDto editUserProfile(org.kaaprojec User user = userFacade.findById(userId); org.kaaproject.kaa.common.dto.admin.UserDto result = new org.kaaproject.kaa.common.dto.admin.UserDto(user.getId().toString(), user.getUsername(), user.getFirstName(), user.getLastName(), user.getMail(), KaaAuthorityDto.valueOf(user - .getAuthorities().iterator().next().getAuthority())); + .getAuthorities().iterator().next().getAuthority())); return result; } catch (Exception e) { throw Utils.handleException(e); @@ -661,6 +665,9 @@ public void deleteUser(String userId) throws KaaAdminServiceException { UserDto user = controlService.getUser(userId); Utils.checkNotNull(user); checkTenantId(user.getTenantId()); + if (KaaAuthorityDto.KAA_ADMIN.equals(user.getAuthority()) || KaaAuthorityDto.TENANT_ADMIN.equals(user.getAuthority())) { + throw new KaaAdminServiceException("Can't delete KAA admin or Tenant admin user!", ServiceErrorCode.PERMISSION_DENIED); + } userFacade.deleteUser(Long.valueOf(user.getExternalUid())); controlService.deleteUser(user.getId()); } catch (Exception e) { @@ -767,7 +774,7 @@ public SdkProfileDto createSdkProfile(SdkProfileDto sdkProfile) throws KaaAdminS this.checkApplicationId(sdkProfile.getApplicationId()); sdkProfile.setCreatedUsername(getCurrentUser().getUsername()); sdkProfile.setCreatedTime(System.currentTimeMillis()); - + ApplicationDto application = controlService.getApplication(sdkProfile.getApplicationId()); if (application == null) { throw new NotFoundException("Application not found!"); @@ -836,7 +843,7 @@ public SdkProfileViewDto getSdkProfileView(String sdkProfileId) throws KaaAdminS getApplicationEventFamilyMapsByApplicationId(applicationId); List aefMapIds = sdkProfile.getAefMapIds(); for (ApplicationEventFamilyMapDto aefDto : aefMaps) { - if (aefMapIds.contains(aefDto.getId())){ + if (aefMapIds.contains(aefDto.getId())) { aefDtoList.add(aefDto); } } @@ -1037,7 +1044,7 @@ public List getProfileSchemasByApplicationId(String ap @Override public List getProfileSchemasByApplicationToken(String applicationToken) throws KaaAdminServiceException { - return getProfileSchemasByApplicationId(checkApplicationToken(applicationToken)); + return getProfileSchemasByApplicationId(checkApplicationToken(applicationToken)); } @Override @@ -1094,8 +1101,8 @@ public ProfileSchemaViewDto saveProfileSchemaView(ProfileSchemaViewDto profileSc if (isEmpty(ctlSchemaId)) { if (profileSchemaView.useExistingCtlSchema()) { CtlSchemaReferenceDto metaInfo = profileSchemaView.getExistingMetaInfo(); - CTLSchemaDto schema = getCTLSchemaByFqnVersionTenantIdAndApplicationId(metaInfo.getMetaInfo().getFqn(), - metaInfo.getVersion(), + CTLSchemaDto schema = getCTLSchemaByFqnVersionTenantIdAndApplicationId(metaInfo.getMetaInfo().getFqn(), + metaInfo.getVersion(), metaInfo.getMetaInfo().getTenantId(), metaInfo.getMetaInfo().getApplicationId()); profileSchema.setCtlSchemaId(schema.getId()); @@ -1110,7 +1117,7 @@ public ProfileSchemaViewDto saveProfileSchemaView(ProfileSchemaViewDto profileSc throw Utils.handleException(e); } } - + @Override public ProfileSchemaViewDto createProfileSchemaFormCtlSchema(CtlSchemaFormDto ctlSchemaForm) throws KaaAdminServiceException { @@ -1165,7 +1172,7 @@ public List getServerProfileSchemaInfosByApplicationId(String app throw Utils.handleException(e); } } - + @Override public List getServerProfileSchemaInfosByEndpointKey(String endpointKeyHash) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); @@ -1203,7 +1210,7 @@ public ServerProfileSchemaDto getServerProfileSchema(String serverProfileSchemaI throw Utils.handleException(e); } } - + @Override public ServerProfileSchemaDto saveServerProfileSchema(ServerProfileSchemaDto serverProfileSchema) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); @@ -1245,8 +1252,8 @@ public ServerProfileSchemaViewDto saveServerProfileSchemaView(ServerProfileSchem if (isEmpty(ctlSchemaId)) { if (serverProfileSchemaView.useExistingCtlSchema()) { CtlSchemaReferenceDto metaInfo = serverProfileSchemaView.getExistingMetaInfo(); - CTLSchemaDto schema = getCTLSchemaByFqnVersionTenantIdAndApplicationId(metaInfo.getMetaInfo().getFqn(), - metaInfo.getVersion(), + CTLSchemaDto schema = getCTLSchemaByFqnVersionTenantIdAndApplicationId(metaInfo.getMetaInfo().getFqn(), + metaInfo.getVersion(), metaInfo.getMetaInfo().getTenantId(), metaInfo.getMetaInfo().getApplicationId()); serverProfileSchema.setCtlSchemaId(schema.getId()); @@ -1261,7 +1268,7 @@ public ServerProfileSchemaViewDto saveServerProfileSchemaView(ServerProfileSchem throw Utils.handleException(e); } } - + @Override public ServerProfileSchemaViewDto createServerProfileSchemaFormCtlSchema(CtlSchemaFormDto ctlSchemaForm) throws KaaAdminServiceException { @@ -1280,7 +1287,7 @@ public ServerProfileSchemaViewDto createServerProfileSchemaFormCtlSchema(CtlSche throw Utils.handleException(e); } } - + private void convertToSchemaForm(AbstractSchemaDto dto, SchemaFormAvroConverter converter) throws IOException { Schema schema = new Schema.Parser().parse(dto.getSchema()); RecordField schemaForm = converter.createSchemaFormFromSchema(schema); @@ -1293,7 +1300,7 @@ private void convertToStringSchema(AbstractSchemaDto dto, SchemaFormAvroConverte String schemaString = SchemaFormAvroConverter.createSchemaString(schema, true); dto.setSchema(schemaString); } - + @Override public SchemaInfoDto getEndpointProfileSchemaInfo(String endpointProfileSchemaId) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); @@ -1487,8 +1494,8 @@ public List getUserNotificationSchemaInfosByApplicationId(String List schemaInfos = new ArrayList<>(notificationSchemas.size()); for (NotificationSchemaDto notificationSchema : notificationSchemas) { SchemaInfoDto schemaInfo = new SchemaInfoDto(notificationSchema); - Schema schema = new Schema.Parser().parse(notificationSchema.getSchema()); - RecordField schemaForm = FormAvroConverter.createRecordFieldFromSchema(schema); + CTLSchemaDto ctlSchemaDto = controlService.getCTLSchemaById(notificationSchema.getCtlSchemaId()); + RecordField schemaForm = createRecordFieldFromCtlSchemaAndBody(notificationSchema.getCtlSchemaId(), ctlSchemaDto.getBody()); schemaInfo.setSchemaForm(schemaForm); schemaInfos.add(schemaInfo); } @@ -1497,8 +1504,36 @@ public List getUserNotificationSchemaInfosByApplicationId(String throw Utils.handleException(e); } } - + +// @Override +// public NotificationSchemaDto getNotificationSchema(String notificationSchemaId) throws KaaAdminServiceException { +// checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); +// try { +// NotificationSchemaDto notificationSchema = controlService.getNotificationSchema(notificationSchemaId); +// Utils.checkNotNull(notificationSchema); +// checkApplicationId(notificationSchema.getApplicationId()); +// return notificationSchema; +// } catch (Exception e) { +// throw Utils.handleException(e); +// } +// } + + @Override + public NotificationSchemaViewDto getNotificationSchemaView(String notificationSchemaId) throws KaaAdminServiceException { + checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); + try { + NotificationSchemaDto notificationSchema = controlService.getNotificationSchema(notificationSchemaId); + Utils.checkNotNull(notificationSchema); + checkApplicationId(notificationSchema.getApplicationId()); + CTLSchemaDto ctlSchemaDto = controlService.getCTLSchemaById(notificationSchema.getCtlSchemaId()); + NotificationSchemaViewDto notificationSchemaViewDto = new NotificationSchemaViewDto(notificationSchema, toCtlSchemaForm(ctlSchemaDto)); + return notificationSchemaViewDto; + } catch (Exception e) { + throw Utils.handleException(e); + } + } + public NotificationSchemaDto getNotificationSchema(String notificationSchemaId) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { @@ -1512,19 +1547,17 @@ public NotificationSchemaDto getNotificationSchema(String notificationSchemaId) } @Override - public NotificationSchemaDto editNotificationSchema(NotificationSchemaDto notificationSchema, byte[] schema) + public NotificationSchemaDto editNotificationSchema(NotificationSchemaDto notificationSchema) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { if (isEmpty(notificationSchema.getId())) { notificationSchema.setCreatedUsername(getCurrentUser().getUsername()); checkApplicationId(notificationSchema.getApplicationId()); - setSchema(notificationSchema, schema); } else { NotificationSchemaDto storedNotificationSchema = controlService.getNotificationSchema(notificationSchema.getId()); Utils.checkNotNull(storedNotificationSchema); checkApplicationId(storedNotificationSchema.getApplicationId()); - notificationSchema.setSchema(storedNotificationSchema.getSchema()); } notificationSchema.setType(NotificationTypeDto.USER); return controlService.editNotificationSchema(notificationSchema); @@ -1533,34 +1566,72 @@ public NotificationSchemaDto editNotificationSchema(NotificationSchemaDto notifi } } +// @Override +// public NotificationSchemaDto getNotificationSchemaForm(String notificationSchemaId) throws KaaAdminServiceException { +// checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); +// try { +// NotificationSchemaDto notificationSchema = getNotificationSchema(notificationSchemaId); +// +// +// NotificationSchemaDto notificationSchema = getNotificationSchema(notificationSchemaId); +// CTLSchemaDto ctlSchemaDto = controlService.getCTLSchemaById(notificationSchema.getCtlSchemaId()); +// return new NotificationSchemaViewDto(notificationSchema, toCtlSchemaForm(ctlSchemaDto)); +//// +//// //convertToSchemaForm(configurationSchema, configurationSchemaFormAvroConverter); +//// +//// CTLSchema schema = new CTLSchema();//new Schema.Parser().parse(notificationSchema.get); +//// schema.getBody(); +////// RecordField schemaForm = converter.createSchemaFormFromSchema(schema); +////// notificationSchema.setSchemaForm(schemaForm); +//// +//// return notificationSchema; +// } catch (Exception e) { +// throw Utils.handleException(e); +// } +// } + @Override - public NotificationSchemaDto getNotificationSchemaForm(String notificationSchemaId) throws KaaAdminServiceException { + public NotificationSchemaViewDto saveNotificationSchemaView(NotificationSchemaViewDto notificationSchemaView) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { - NotificationSchemaDto notificationSchema = getNotificationSchema(notificationSchemaId); - convertToSchemaForm(notificationSchema, commonSchemaFormAvroConverter); - return notificationSchema; + NotificationSchemaDto notificationSchema = notificationSchemaView.getSchema(); + String applicationId = notificationSchema.getApplicationId(); + checkApplicationId(applicationId); + String ctlSchemaId = notificationSchema.getCtlSchemaId(); + if (isEmpty(ctlSchemaId)) { + if (notificationSchemaView.useExistingCtlSchema()) { + CtlSchemaReferenceDto metaInfo = notificationSchemaView.getExistingMetaInfo(); + CTLSchemaDto schema = getCTLSchemaByFqnVersionTenantIdAndApplicationId(metaInfo.getMetaInfo().getFqn(), + metaInfo.getVersion(), + metaInfo.getMetaInfo().getTenantId(), + metaInfo.getMetaInfo().getApplicationId()); + notificationSchema.setCtlSchemaId(schema.getId()); + } else { + CtlSchemaFormDto ctlSchemaForm = saveCTLSchemaForm(notificationSchemaView.getCtlSchemaForm()); + notificationSchema.setCtlSchemaId(ctlSchemaForm.getId()); + } + } + NotificationSchemaDto savedNotificationSchema = editNotificationSchema(notificationSchema); + return getNotificationSchemaView(savedNotificationSchema.getId()); } catch (Exception e) { throw Utils.handleException(e); } } @Override - public NotificationSchemaDto editNotificationSchemaForm(NotificationSchemaDto notificationSchema) throws KaaAdminServiceException { + public NotificationSchemaViewDto createNotificationSchemaFormCtlSchema(CtlSchemaFormDto ctlSchemaForm) throws KaaAdminServiceException { + LOG.error("createNotificationSchemaFormCtlSchema [{}]", ctlSchemaForm.getSchema().getDisplayString()); checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { - if (isEmpty(notificationSchema.getId())) { - notificationSchema.setCreatedUsername(getCurrentUser().getUsername()); - checkApplicationId(notificationSchema.getApplicationId()); - convertToStringSchema(notificationSchema, commonSchemaFormAvroConverter); - } else { - NotificationSchemaDto storedNotificationSchema = controlService.getNotificationSchema(notificationSchema.getId()); - Utils.checkNotNull(storedNotificationSchema); - checkApplicationId(storedNotificationSchema.getApplicationId()); - notificationSchema.setSchema(storedNotificationSchema.getSchema()); - } - notificationSchema.setType(NotificationTypeDto.USER); - return controlService.editNotificationSchema(notificationSchema); + checkApplicationId(ctlSchemaForm.getMetaInfo().getApplicationId()); + NotificationSchemaDto notificationSchema = new NotificationSchemaDto(); + notificationSchema.setApplicationId(ctlSchemaForm.getMetaInfo().getApplicationId()); + notificationSchema.setName(ctlSchemaForm.getSchema().getDisplayNameFieldValue()); + notificationSchema.setDescription(ctlSchemaForm.getSchema().getDescriptionFieldValue()); + CtlSchemaFormDto savedCtlSchemaForm = saveCTLSchemaForm(ctlSchemaForm); + notificationSchema.setCtlSchemaId(savedCtlSchemaForm.getId()); + NotificationSchemaDto savedNotificationSchema = editNotificationSchema(notificationSchema); + return getNotificationSchemaView(savedNotificationSchema.getId()); } catch (Exception e) { throw Utils.handleException(e); } @@ -1736,7 +1807,7 @@ public void deleteEndpointGroup(String endpointGroupId) throws KaaAdminServiceEx @Override public List getProfileFilterRecordsByEndpointGroupId(String endpointGroupId, - boolean includeDeprecated) throws KaaAdminServiceException { + boolean includeDeprecated) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { checkEndpointGroupId(endpointGroupId); @@ -1754,12 +1825,11 @@ private void checkSchemaId(String schemaId) throws IllegalArgumentException { @Override public ProfileFilterRecordDto getProfileFilterRecord(String endpointProfileSchemaId, String serverProfileSchemaId, - String endpointGroupId) throws KaaAdminServiceException { + String endpointGroupId) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { checkEndpointGroupId(endpointGroupId); ProfileFilterRecordDto record = controlService.getProfileFilterRecord(endpointProfileSchemaId, serverProfileSchemaId, endpointGroupId); - Utils.checkNotNull(record); return record; } catch (Exception e) { throw Utils.handleException(e); @@ -1789,7 +1859,7 @@ public ProfileFilterDto editProfileFilter(ProfileFilterDto profileFilter) throws profileFilter.setModifiedUsername(username); ProfileFilterDto storedProfileFilter = controlService.getProfileFilter(profileFilter.getId()); Utils.checkNotNull(storedProfileFilter); - checkEndpointGroupId(storedProfileFilter.getEndpointGroupId()); + checkEndpointGroupId(storedProfileFilter.getEndpointGroupId()); } validateProfileFilterBody(profileFilter.getEndpointProfileSchemaId(), profileFilter.getServerProfileSchemaId(), @@ -1799,9 +1869,9 @@ public ProfileFilterDto editProfileFilter(ProfileFilterDto profileFilter) throws throw Utils.handleException(e); } } - - private void validateProfileFilterBody(String endpointProfileSchemaId, String serverProfileSchemaId, - String filterBody) throws KaaAdminServiceException { + + private void validateProfileFilterBody(String endpointProfileSchemaId, String serverProfileSchemaId, + String filterBody) throws KaaAdminServiceException { GenericRecord endpointProfileRecord = null; GenericRecord serverProfileRecord = null; try { @@ -1835,7 +1905,7 @@ private void validateProfileFilterBody(String endpointProfileSchemaId, String se throw new KaaAdminServiceException("Invalid profile filter body!", e, ServiceErrorCode.BAD_REQUEST_PARAMS); } } - + private GenericRecord getDefaultRecordFromCtlSchema(String ctlSchemaId) throws Exception { CTLSchemaDto ctlSchema = controlService.getCTLSchemaById(ctlSchemaId); Schema schema = controlService.exportCTLSchemaFlatAsSchema(ctlSchema); @@ -1878,7 +1948,6 @@ public void deleteProfileFilterRecord(String endpointProfileSchemaId, String ser try { ProfileFilterRecordDto record = controlService.getProfileFilterRecord(endpointProfileSchemaId, serverProfileSchemaId, endpointGroupId); - Utils.checkNotNull(record); checkEndpointGroupId(record.getEndpointGroupId()); String username = getCurrentUser().getUsername(); controlService.deleteProfileFilterRecord(endpointProfileSchemaId, serverProfileSchemaId, endpointGroupId, username); @@ -1889,7 +1958,7 @@ public void deleteProfileFilterRecord(String endpointProfileSchemaId, String ser @Override public List getConfigurationRecordsByEndpointGroupId(String endpointGroupId, - boolean includeDeprecated) throws KaaAdminServiceException { + boolean includeDeprecated) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { checkEndpointGroupId(endpointGroupId); @@ -1906,7 +1975,6 @@ public ConfigurationRecordDto getConfigurationRecord(String schemaId, String end try { checkEndpointGroupId(endpointGroupId); ConfigurationRecordDto record = controlService.getConfigurationRecord(schemaId, endpointGroupId); - Utils.checkNotNull(record); return record; } catch (Exception e) { throw Utils.handleException(e); @@ -2127,7 +2195,6 @@ public void deleteConfigurationRecord(String schemaId, String endpointGroupId) t checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { StructureRecordDto record = controlService.getConfigurationRecord(schemaId, endpointGroupId); - Utils.checkNotNull(record); checkEndpointGroupId(record.getEndpointGroupId()); String username = getCurrentUser().getUsername(); controlService.deleteConfigurationRecord(schemaId, endpointGroupId, username); @@ -2534,6 +2601,42 @@ public RecordField getRecordDataFromFile(String schema, String fileItemName) thr } } + @Override + public RecordField getConfigurationRecordDataFromFile(String schema, String fileItemName) throws KaaAdminServiceException { + checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); + try { + byte[] body = getFileContent(fileItemName); + + JsonNode json = new ObjectMapper().readTree(body); + json = injectUuids(json); + body = json.toString().getBytes(); + + GenericAvroConverter converter = new GenericAvroConverter<>(schema); + GenericRecord record = converter.decodeJson(body); + RecordField recordData = FormAvroConverter.createRecordFieldFromGenericRecord(record); + return recordData; + } catch (Exception e) { + throw Utils.handleException(e); + } + } + + private JsonNode injectUuids(JsonNode json) { + boolean containerWithoutId = json.isContainerNode() && !json.has("__uuid"); + boolean notArray = !(json instanceof ArrayNode); + boolean childIsNotArray = !(json.size() == 1 && json.getElements().next() instanceof ArrayNode); + + if (containerWithoutId && notArray && childIsNotArray) { + ((ObjectNode)json).put("__uuid", (Integer)null); + } + + for (JsonNode node : json) { + if (node.isContainerNode()) + injectUuids(node); + } + + return json; + } + private void checkExpiredDate(NotificationDto notification) throws KaaAdminServiceException { if (null != notification.getExpiredAt() && notification.getExpiredAt().before(new Date())) { throw new IllegalArgumentException("Overdue expiry time for notification!"); @@ -2603,7 +2706,7 @@ public EndpointNotificationDto sendUnicastNotification(NotificationDto notificat GenericRecord record = FormAvroConverter.createGenericRecordFromRecordField(notificationData); GenericAvroConverter converter = new GenericAvroConverter<>(record.getSchema()); byte[] body = converter.encodeToJsonBytes(record); - return sendUnicastNotification(notification,clientKeyHash,body); + return sendUnicastNotification(notification, clientKeyHash, body); } catch (Exception e) { throw Utils.handleException(e); } @@ -2845,7 +2948,7 @@ private org.kaaproject.kaa.common.dto.admin.UserDto toUser(UserDto tenantUser) { User user = userFacade.findById(Long.valueOf(tenantUser.getExternalUid())); org.kaaproject.kaa.common.dto.admin.UserDto result = new org.kaaproject.kaa.common.dto.admin.UserDto(user.getId().toString(), user.getUsername(), user.getFirstName(), user.getLastName(), user.getMail(), KaaAuthorityDto.valueOf(user.getAuthorities() - .iterator().next().getAuthority())); + .iterator().next().getAuthority())); result.setId(tenantUser.getId()); result.setTenantId(tenantUser.getTenantId()); return result; @@ -2892,12 +2995,12 @@ private Schema validateSchema(String avroSchema, boolean isCtl) throws KaaAdminS throw new KaaAdminServiceException(spe.getMessage(), ServiceErrorCode.INVALID_SCHEMA); } } - + private void validateRecordSchema(String avroSchema, boolean isCtl) throws KaaAdminServiceException { Schema schema = validateSchema(avroSchema, isCtl); validateRecordSchema(schema); } - + private void validateRecordSchema(Schema schema) throws KaaAdminServiceException { SchemaUtil.compileAvroSchema(schema); if (schema.getType() != Schema.Type.RECORD) { @@ -3032,7 +3135,7 @@ public List getUserConfigurationSchemaInfosByApplicationId(String @Override public void editUserConfiguration(EndpointUserConfigurationDto endpointUserConfiguration, String applicationId, - RecordField configurationData) throws KaaAdminServiceException { + RecordField configurationData) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.TENANT_DEVELOPER, KaaAuthorityDto.TENANT_USER); try { ApplicationDto application = checkApplicationId(applicationId); @@ -3064,27 +3167,27 @@ private void checkCTLSchemaReadScope(String tenantId, String applicationId) thro CTLSchemaScopeDto scope = detectScope(tenantId, applicationId); boolean allowed = false; switch (currentUser.getAuthority()) { - case KAA_ADMIN: - allowed = scope == CTLSchemaScopeDto.SYSTEM; - break; - case TENANT_ADMIN: - if (scope == CTLSchemaScopeDto.TENANT) { - checkTenantId(tenantId); - } - allowed = scope.getLevel() <= CTLSchemaScopeDto.TENANT.getLevel(); - break; - case TENANT_DEVELOPER: - case TENANT_USER: - if (scope == CTLSchemaScopeDto.TENANT) { - checkTenantId(tenantId); - } - if (scope.getLevel() >= CTLSchemaScopeDto.APPLICATION.getLevel()) { - checkApplicationId(applicationId); - } - allowed = scope.getLevel() >= CTLSchemaScopeDto.SYSTEM.getLevel(); - break; - default: - break; + case KAA_ADMIN: + allowed = scope == CTLSchemaScopeDto.SYSTEM; + break; + case TENANT_ADMIN: + if (scope == CTLSchemaScopeDto.TENANT) { + checkTenantId(tenantId); + } + allowed = scope.getLevel() <= CTLSchemaScopeDto.TENANT.getLevel(); + break; + case TENANT_DEVELOPER: + case TENANT_USER: + if (scope == CTLSchemaScopeDto.TENANT) { + checkTenantId(tenantId); + } + if (scope.getLevel() >= CTLSchemaScopeDto.APPLICATION.getLevel()) { + checkApplicationId(applicationId); + } + allowed = scope.getLevel() >= CTLSchemaScopeDto.SYSTEM.getLevel(); + break; + default: + break; } if (!allowed) { throw new KaaAdminServiceException(ServiceErrorCode.PERMISSION_DENIED); @@ -3096,23 +3199,23 @@ private void checkCTLSchemaEditScope(String tenantId, String applicationId) thro CTLSchemaScopeDto scope = detectScope(tenantId, applicationId); boolean allowed = false; switch (currentUser.getAuthority()) { - case KAA_ADMIN: - allowed = scope == CTLSchemaScopeDto.SYSTEM; - break; - case TENANT_ADMIN: - checkTenantId(tenantId); - allowed = scope == CTLSchemaScopeDto.TENANT; - break; - case TENANT_DEVELOPER: - case TENANT_USER: - checkTenantId(tenantId); - if (scope.getLevel() >= CTLSchemaScopeDto.APPLICATION.getLevel()) { - checkApplicationId(applicationId); - } - allowed = scope.getLevel() >= CTLSchemaScopeDto.TENANT.getLevel(); - break; - default: - break; + case KAA_ADMIN: + allowed = scope == CTLSchemaScopeDto.SYSTEM; + break; + case TENANT_ADMIN: + checkTenantId(tenantId); + allowed = scope == CTLSchemaScopeDto.TENANT; + break; + case TENANT_DEVELOPER: + case TENANT_USER: + checkTenantId(tenantId); + if (scope.getLevel() >= CTLSchemaScopeDto.APPLICATION.getLevel()) { + checkApplicationId(applicationId); + } + allowed = scope.getLevel() >= CTLSchemaScopeDto.TENANT.getLevel(); + break; + default: + break; } if (!allowed) { throw new KaaAdminServiceException(ServiceErrorCode.PERMISSION_DENIED); @@ -3149,11 +3252,9 @@ private void checkCTLSchemaVersion(Integer version) throws KaaAdminServiceExcept * Returns a string that contains fully qualified names and version numbers * of the given CTL schemas. * - * @param types - * A collection of CTL schemas - * + * @param types A collection of CTL schemas * @return A string that contains fully qualified names and version numbers - * of the given CTL schemas + * of the given CTL schemas */ private String asText(Collection types) { StringBuilder message = new StringBuilder(); @@ -3170,7 +3271,7 @@ private String asText(Collection types) { public CTLSchemaDto saveCTLSchema(String body, String tenantId, String applicationId) throws KaaAdminServiceException { this.checkAuthority(KaaAuthorityDto.values()); try { - checkCTLSchemaEditScope(tenantId, applicationId); + checkCTLSchemaEditScope(tenantId, applicationId); CTLSchemaParser parser = new CTLSchemaParser(controlService, tenantId); CTLSchemaDto schema = parser.parse(body, applicationId); checkCTLSchemaVersion(schema.getVersion()); @@ -3199,15 +3300,16 @@ public CTLSchemaDto saveCTLSchema(CTLSchemaDto schema) throws KaaAdminServiceExc Utils.checkNotNull(schema); checkCTLSchemaVersion(schema.getVersion()); checkCTLSchemaEditScope(schema.getMetaInfo().getTenantId(), schema.getMetaInfo().getApplicationId()); + // Check if the schema dependencies are present in the database List missingDependencies = new ArrayList<>(); Set dependencies = new HashSet<>(); if (schema.getDependencySet() != null) { for (CTLSchemaDto dependency : schema.getDependencySet()) { - CTLSchemaDto schemaFound = + CTLSchemaDto schemaFound = controlService.getAnyCTLSchemaByFqnVersionTenantIdAndApplicationId( dependency.getMetaInfo().getFqn(), dependency.getVersion(), - schema.getMetaInfo().getTenantId(), schema.getMetaInfo().getApplicationId()); + schema.getMetaInfo().getTenantId(), schema.getMetaInfo().getApplicationId()); if (schemaFound == null) { missingDependencies.add(new FqnVersion(dependency.getMetaInfo().getFqn(), dependency.getVersion())); } else { @@ -3350,12 +3452,43 @@ public boolean checkFqnExistsWithAppToken(String fqn, String tenantId, String ap return checkFqnExists(fqn, tenantId, applicationId); } + @Override - public CTLSchemaMetaInfoDto updateCTLSchemaMetaInfoScope(CTLSchemaMetaInfoDto ctlSchemaMetaInfo) + public CTLSchemaMetaInfoDto promoteScopeToTenant(String applicationId, String fqn) throws KaaAdminServiceException { - this.checkAuthority(KaaAuthorityDto.values()); + checkAuthority(KaaAuthorityDto.values()); + final String tenantId = getTenantId(); + checkApplicationId(applicationId); + checkCTLSchemaEditScope(tenantId, applicationId); + try { - checkCTLSchemaEditScope(ctlSchemaMetaInfo.getTenantId(), ctlSchemaMetaInfo.getApplicationId()); + Set dependencies = new HashSet<>(); + List versions = controlService.getAllCTLSchemaVersionsByFqnTenantIdAndApplicationId(fqn, tenantId, applicationId); + + if(versions.isEmpty()) { + throw new KaaAdminServiceException("The requested item was not found!", ServiceErrorCode.ITEM_NOT_FOUND); + } + + // meta info same for all versions + CTLSchemaMetaInfoDto ctlSchemaMetaInfo = controlService.getCTLSchemaByFqnVersionTenantIdAndApplicationId(fqn, versions.get(0), tenantId, applicationId).getMetaInfo(); + ctlSchemaMetaInfo.setApplicationId(null); //promote to tenant + + // get dep of all versions + for (Integer version : versions) { + CTLSchemaDto schema = controlService.getCTLSchemaByFqnVersionTenantIdAndApplicationId(fqn, version, tenantId, applicationId); + Set schemaDependents = schema.getDependencySet(); + dependencies.addAll(schemaDependents.stream() + .filter(dep -> dep.getMetaInfo().getScope() == CTLSchemaScopeDto.APPLICATION) + .collect(Collectors.toList())); + } + + // check if CT has dependencies with application scope + if (!dependencies.isEmpty()) { + String message = "Can't promote the common type version as it has references on following common type(s) with application scope: " + + asText(dependencies); + throw new KaaAdminServiceException(message, ServiceErrorCode.CONFLICT); + } + return controlService.updateCTLSchemaMetaInfoScope(ctlSchemaMetaInfo); } catch (Exception cause) { throw Utils.handleException(cause); @@ -3467,8 +3600,8 @@ public CtlSchemaFormDto getCTLSchemaFormByMetaInfoIdAndVer(String metaInfoId, in } @Override - public CtlSchemaFormDto createNewCTLSchemaFormInstance(String metaInfoId, - Integer sourceVersion, String applicationId) throws KaaAdminServiceException { + public CtlSchemaFormDto createNewCTLSchemaFormInstance(String metaInfoId, + Integer sourceVersion, String applicationId) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.values()); try { SchemaFormAvroConverter converter = getCtlSchemaConverterForScope(getCurrentUser().getTenantId(), applicationId); @@ -3483,7 +3616,7 @@ public CtlSchemaFormDto createNewCTLSchemaFormInstance(String metaInfoId, ctlSchemaForm = new CtlSchemaFormDto(); ctlSchemaForm.setMetaInfo(sourceCtlSchema.getMetaInfo()); RecordField form = sourceCtlSchema.getSchema(); - form.updateVersion(form.getContext().getMaxVersion(new Fqn(sourceCtlSchema.getMetaInfo().getFqn()))+1); + form.updateVersion(form.getContext().getMaxVersion(new Fqn(sourceCtlSchema.getMetaInfo().getFqn())) + 1); ctlSchemaForm.setSchema(form); } else { checkCTLSchemaEditScope(getCurrentUser().getTenantId(), applicationId); @@ -3491,7 +3624,7 @@ public CtlSchemaFormDto createNewCTLSchemaFormInstance(String metaInfoId, RecordField form = converter.getEmptySchemaFormInstance(); form.updateVersion(1); ctlSchemaForm.setSchema(form); - CTLSchemaMetaInfoDto metaInfo = new CTLSchemaMetaInfoDto(null, + CTLSchemaMetaInfoDto metaInfo = new CTLSchemaMetaInfoDto(null, getCurrentUser().getTenantId(), applicationId); ctlSchemaForm.setMetaInfo(metaInfo); } @@ -3549,7 +3682,7 @@ public CtlSchemaFormDto saveCTLSchemaForm(CtlSchemaFormDto ctlSchemaForm) throws List missingDependencies = new ArrayList<>(); for (FqnVersion fqnVersion : dependenciesList) { CTLSchemaDto dependency = controlService.getAnyCTLSchemaByFqnVersionTenantIdAndApplicationId( - fqnVersion.getFqnString(), fqnVersion.getVersion(), + fqnVersion.getFqnString(), fqnVersion.getVersion(), ctlSchema.getMetaInfo().getTenantId(), ctlSchema.getMetaInfo().getApplicationId()); if (dependency != null) { dependencies.add(dependency); @@ -3562,13 +3695,13 @@ public CtlSchemaFormDto saveCTLSchemaForm(CtlSchemaFormDto ctlSchemaForm) throws throw new IllegalArgumentException(message); } ctlSchema.setDependencySet(dependencies); - SchemaFormAvroConverter converter = getCtlSchemaConverterForScope(ctlSchema.getMetaInfo().getTenantId(), + SchemaFormAvroConverter converter = getCtlSchemaConverterForScope(ctlSchema.getMetaInfo().getTenantId(), ctlSchema.getMetaInfo().getApplicationId()); Schema avroSchema = converter.createSchemaFromSchemaForm(schemaForm); String schemaBody = SchemaFormAvroConverter.createSchemaString(avroSchema, true); ctlSchema.setBody(schemaBody); } - + CTLSchemaDto savedCtlSchema = saveCTLSchema(ctlSchema); if (savedCtlSchema != null) { CtlSchemaFormDto result = new CtlSchemaFormDto(savedCtlSchema); @@ -3577,7 +3710,7 @@ public CtlSchemaFormDto saveCTLSchemaForm(CtlSchemaFormDto ctlSchemaForm) throws RecordField form = converter.createSchemaFormFromSchema(savedCtlSchema.getBody()); result.setSchema(form); List availableVersions = controlService.getAllCTLSchemaVersionsByFqnTenantIdAndApplicationId( - savedCtlSchema.getMetaInfo().getFqn(), savedCtlSchema.getMetaInfo().getTenantId(), savedCtlSchema.getMetaInfo().getApplicationId()); + savedCtlSchema.getMetaInfo().getFqn(), savedCtlSchema.getMetaInfo().getTenantId(), savedCtlSchema.getMetaInfo().getApplicationId()); availableVersions = availableVersions == null ? Collections.emptyList() : availableVersions; Collections.sort(availableVersions); result.getMetaInfo().setVersions(availableVersions); @@ -3588,7 +3721,7 @@ public CtlSchemaFormDto saveCTLSchemaForm(CtlSchemaFormDto ctlSchemaForm) throws } return null; } - + @Override public boolean checkFqnExists(CtlSchemaFormDto ctlSchemaForm) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.values()); @@ -3605,11 +3738,11 @@ public boolean checkFqnExists(CtlSchemaFormDto ctlSchemaForm) throws KaaAdminSer } return false; } - + private SchemaFormAvroConverter getCtlSchemaConverterForScope(String tenantId, String applicationId) throws KaaAdminServiceException { try { if (isEmpty(tenantId)) { - return getCtlSchemaConverterForSystem(); + return getCtlSchemaConverterForSystem(); } if (isEmpty(applicationId)) { return getCtlSchemaConverterForTenant(tenantId); @@ -3619,7 +3752,7 @@ private SchemaFormAvroConverter getCtlSchemaConverterForScope(String tenantId, S throw Utils.handleException(cause); } } - + private SchemaFormAvroConverter getCtlSchemaConverterForSystem() throws KaaAdminServiceException { try { return createSchemaConverterFromCtlTypes(controlService.getAvailableCTLSchemaVersionsForSystem()); @@ -3635,7 +3768,7 @@ private SchemaFormAvroConverter getCtlSchemaConverterForTenant(String tenantId) throw Utils.handleException(cause); } } - + private SchemaFormAvroConverter getCtlSchemaConverterForApplication(String tenantId, String applicationId) throws KaaAdminServiceException { try { return createSchemaConverterFromCtlTypes(controlService.getAvailableCTLSchemaVersionsForApplication(tenantId, applicationId)); @@ -3643,7 +3776,7 @@ private SchemaFormAvroConverter getCtlSchemaConverterForApplication(String tenan throw Utils.handleException(cause); } } - + private SchemaFormAvroConverter createSchemaConverterFromCtlTypes(final Map> ctlTypes) throws KaaAdminServiceException { try { CtlSource ctlSource = new CtlSource() { @@ -3668,14 +3801,14 @@ public FileData exportCTLSchema(String fqn, int version, String applicationId, C Utils.checkNotNull(schemaFound); checkCTLSchemaReadScope(schemaFound.getMetaInfo().getTenantId(), schemaFound.getMetaInfo().getApplicationId()); switch (method) { - case SHALLOW: - return controlService.exportCTLSchemaShallow(schemaFound); - case FLAT: - return controlService.exportCTLSchemaFlat(schemaFound); - case DEEP: - return controlService.exportCTLSchemaDeep(schemaFound); - default: - throw new IllegalArgumentException("The export method " + method.name() + " is not currently supported!"); + case SHALLOW: + return controlService.exportCTLSchemaShallow(schemaFound); + case FLAT: + return controlService.exportCTLSchemaFlat(schemaFound); + case DEEP: + return controlService.exportCTLSchemaDeep(schemaFound); + default: + throw new IllegalArgumentException("The export method " + method.name() + " is not currently supported!"); } } catch (Exception cause) { throw Utils.handleException(cause); @@ -3693,7 +3826,7 @@ public FileData exportCTLSchemaByAppToken(String fqn, int version, String applic @Override public String prepareCTLSchemaExport(String ctlSchemaId, - CTLSchemaExportMethod method) throws KaaAdminServiceException { + CTLSchemaExportMethod method) throws KaaAdminServiceException { checkAuthority(KaaAuthorityDto.values()); try { CTLSchemaDto schemaFound = controlService.getCTLSchemaById(ctlSchemaId); @@ -3773,7 +3906,7 @@ public void revokeCredentials(String applicationToken, String credentialsId) thr throw Utils.handleException(cause); } } - + @Override public void onCredentialsRevoked(String applicationToken, String credentialsId) throws KaaAdminServiceException { @@ -3786,7 +3919,7 @@ public void onCredentialsRevoked(String applicationToken, String credentialsId) this.controlService.onCredentailsRevoked(applicationId, credentials.get().getId()); } catch (Exception cause) { throw Utils.handleException(cause); - } + } } @Override @@ -3795,7 +3928,7 @@ public void provisionRegistration( String credentialsId, Integer serverProfileVersion, String serverProfileBody) - throws KaaAdminServiceException { + throws KaaAdminServiceException { this.checkAuthority(KaaAuthorityDto.TENANT_ADMIN); try { String applicationId = checkApplicationToken(applicationToken); diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/shared/schema/NotificationSchemaViewDto.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/shared/schema/NotificationSchemaViewDto.java new file mode 100644 index 0000000000..0b488e31ba --- /dev/null +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/shared/schema/NotificationSchemaViewDto.java @@ -0,0 +1,23 @@ +package org.kaaproject.kaa.server.admin.shared.schema; + + +import org.kaaproject.kaa.common.dto.NotificationSchemaDto; + +public class NotificationSchemaViewDto extends BaseSchemaViewDto{ + + private static final long serialVersionUID = -5289268279407697111L; + + public NotificationSchemaViewDto() { + super(); + } + + public NotificationSchemaViewDto(NotificationSchemaDto schema, + CtlSchemaFormDto ctlSchemaForm) { + super(schema, ctlSchemaForm); + } + + @Override + protected NotificationSchemaDto createEmptySchema() { + return new NotificationSchemaDto(); + } +} diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/admin/shared/services/KaaAdminService.java b/server/node/src/main/java/org/kaaproject/kaa/server/admin/shared/services/KaaAdminService.java index 85bc53caf5..953ad6d7dd 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/admin/shared/services/KaaAdminService.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/admin/shared/services/KaaAdminService.java @@ -65,11 +65,7 @@ import org.kaaproject.kaa.server.admin.shared.endpoint.EndpointProfileViewDto; import org.kaaproject.kaa.server.admin.shared.plugin.PluginInfoDto; import org.kaaproject.kaa.server.admin.shared.properties.PropertiesDto; -import org.kaaproject.kaa.server.admin.shared.schema.CtlSchemaFormDto; -import org.kaaproject.kaa.server.admin.shared.schema.CtlSchemaReferenceDto; -import org.kaaproject.kaa.server.admin.shared.schema.ProfileSchemaViewDto; -import org.kaaproject.kaa.server.admin.shared.schema.SchemaInfoDto; -import org.kaaproject.kaa.server.admin.shared.schema.ServerProfileSchemaViewDto; +import org.kaaproject.kaa.server.admin.shared.schema.*; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; @@ -209,11 +205,15 @@ public interface KaaAdminService extends RemoteService { NotificationSchemaDto getNotificationSchema(String notificationSchemaId) throws KaaAdminServiceException; - NotificationSchemaDto editNotificationSchema(NotificationSchemaDto notificationSchema, byte[] schema) throws KaaAdminServiceException; + NotificationSchemaViewDto getNotificationSchemaView(String notificationSchemaId) throws KaaAdminServiceException; - NotificationSchemaDto getNotificationSchemaForm(String notificationSchemaId) throws KaaAdminServiceException; + NotificationSchemaDto editNotificationSchema(NotificationSchemaDto notificationSchema) throws KaaAdminServiceException; - NotificationSchemaDto editNotificationSchemaForm(NotificationSchemaDto notificationSchema) throws KaaAdminServiceException; + //NotificationSchemaDto getNotificationSchemaForm(String notificationSchemaId) throws KaaAdminServiceException; + + NotificationSchemaViewDto saveNotificationSchemaView(NotificationSchemaViewDto notificationSchema) throws KaaAdminServiceException; + + NotificationSchemaViewDto createNotificationSchemaFormCtlSchema(CtlSchemaFormDto ctlSchemaForm) throws KaaAdminServiceException; List getLogSchemasByApplicationId(String applicationId) throws KaaAdminServiceException; @@ -297,6 +297,8 @@ public interface KaaAdminService extends RemoteService { RecordField getRecordDataFromFile(String schema, String fileItemName) throws KaaAdminServiceException; + RecordField getConfigurationRecordDataFromFile(String schema, String fileItemName) throws KaaAdminServiceException; + void sendNotification(NotificationDto notification, RecordField notificationData) throws KaaAdminServiceException; NotificationDto sendNotification(NotificationDto notification, byte[] body) throws KaaAdminServiceException; @@ -411,7 +413,7 @@ public interface KaaAdminService extends RemoteService { boolean checkFqnExistsWithAppToken(String fqn, String tenantId, String applicationToken) throws KaaAdminServiceException; - CTLSchemaMetaInfoDto updateCTLSchemaMetaInfoScope(CTLSchemaMetaInfoDto ctlSchemaMetaInfo) throws KaaAdminServiceException; + CTLSchemaMetaInfoDto promoteScopeToTenant(String applicationToken, String fqn) throws KaaAdminServiceException; List getSystemLevelCTLSchemas() throws KaaAdminServiceException; diff --git a/server/node/src/main/java/org/kaaproject/kaa/server/control/service/DefaultControlService.java b/server/node/src/main/java/org/kaaproject/kaa/server/control/service/DefaultControlService.java index 1e4f6d3231..f884fe98fa 100644 --- a/server/node/src/main/java/org/kaaproject/kaa/server/control/service/DefaultControlService.java +++ b/server/node/src/main/java/org/kaaproject/kaa/server/control/service/DefaultControlService.java @@ -37,39 +37,7 @@ import org.apache.thrift.TException; import org.kaaproject.avro.ui.shared.Fqn; import org.kaaproject.kaa.common.avro.GenericAvroConverter; -import org.kaaproject.kaa.common.dto.AbstractSchemaDto; -import org.kaaproject.kaa.common.dto.ApplicationDto; -import org.kaaproject.kaa.common.dto.ChangeConfigurationNotification; -import org.kaaproject.kaa.common.dto.ChangeNotificationDto; -import org.kaaproject.kaa.common.dto.ChangeProfileFilterNotification; -import org.kaaproject.kaa.common.dto.ChangeType; -import org.kaaproject.kaa.common.dto.ConfigurationDto; -import org.kaaproject.kaa.common.dto.ConfigurationRecordDto; -import org.kaaproject.kaa.common.dto.ConfigurationSchemaDto; -import org.kaaproject.kaa.common.dto.EndpointGroupDto; -import org.kaaproject.kaa.common.dto.EndpointNotificationDto; -import org.kaaproject.kaa.common.dto.EndpointProfileBodyDto; -import org.kaaproject.kaa.common.dto.EndpointProfileDto; -import org.kaaproject.kaa.common.dto.EndpointProfileSchemaDto; -import org.kaaproject.kaa.common.dto.EndpointProfilesBodyDto; -import org.kaaproject.kaa.common.dto.EndpointProfilesPageDto; -import org.kaaproject.kaa.common.dto.EndpointUserConfigurationDto; -import org.kaaproject.kaa.common.dto.EndpointUserDto; -import org.kaaproject.kaa.common.dto.HasId; -import org.kaaproject.kaa.common.dto.NotificationDto; -import org.kaaproject.kaa.common.dto.NotificationSchemaDto; -import org.kaaproject.kaa.common.dto.NotificationTypeDto; -import org.kaaproject.kaa.common.dto.PageLinkDto; -import org.kaaproject.kaa.common.dto.ProfileFilterDto; -import org.kaaproject.kaa.common.dto.ProfileFilterRecordDto; -import org.kaaproject.kaa.common.dto.ProfileVersionPairDto; -import org.kaaproject.kaa.common.dto.ServerProfileSchemaDto; -import org.kaaproject.kaa.common.dto.TenantAdminDto; -import org.kaaproject.kaa.common.dto.TenantDto; -import org.kaaproject.kaa.common.dto.TopicDto; -import org.kaaproject.kaa.common.dto.UpdateNotificationDto; -import org.kaaproject.kaa.common.dto.UserDto; -import org.kaaproject.kaa.common.dto.VersionDto; +import org.kaaproject.kaa.common.dto.*; import org.kaaproject.kaa.common.dto.admin.RecordKey; import org.kaaproject.kaa.common.dto.admin.RecordKey.RecordFiles; import org.kaaproject.kaa.common.dto.admin.SdkPlatform; @@ -1091,8 +1059,14 @@ public FileData generateSdk(SdkProfileDto sdkProfile, SdkPlatform platform) thro } String profileSchemaBodyString = ctlService.flatExportAsString(profileCtlSchema); + CTLSchemaDto notificationCtlSchema = ctlService.findCTLSchemaById(notificationSchema.getCtlSchemaId()); + if (notificationCtlSchema == null) { + throw new NotFoundException("Profile CTL schema not found!"); + } + String notificationSchemaBodyString = ctlService.flatExportAsString(notificationCtlSchema); + DataSchema profileDataSchema = new DataSchema(profileSchemaBodyString); - DataSchema notificationDataSchema = new DataSchema(notificationSchema.getSchema()); + DataSchema notificationDataSchema = new DataSchema(notificationSchemaBodyString); ProtocolSchema protocolSchema = new ProtocolSchema(configurationSchema.getProtocolSchema()); DataSchema logDataSchema = new DataSchema(logSchema.getSchema()); @@ -1957,14 +1931,7 @@ public FileData getRecordStructureData(RecordKey key) throws ControlServiceExcep case LOG_SCHEMA: schemaDto = logSchemaService.findLogSchemaByAppIdAndVersion(key.getApplicationId(), key.getSchemaVersion()); checkSchema(schemaDto, RecordFiles.LOG_SCHEMA); - Schema recordWrapperSchema = null; - try { - recordWrapperSchema = RecordWrapperSchemaGenerator.generateRecordWrapperSchema(schemaDto.getSchema()); - } catch (IOException e) { - LOG.error("Unable to get Record Structure Schema", e); - throw new ControlServiceException(e); - } - schema = recordWrapperSchema.toString(true); + schema = schemaDto.getSchema(); fileName = MessageFormatter.arrayFormat(DATA_NAME_PATTERN, new Object[] { "log", key.getSchemaVersion() }).getMessage(); break; case CONFIGURATION_SCHEMA: @@ -1989,12 +1956,12 @@ public FileData getRecordStructureData(RecordKey key) throws ControlServiceExcep .arrayFormat(DATA_NAME_PATTERN, new Object[] { "configuration-override", key.getSchemaVersion() }).getMessage(); break; case NOTIFICATION_SCHEMA: - schemaDto = notificationService.findNotificationSchemaByAppIdAndTypeAndVersion(key.getApplicationId(), - NotificationTypeDto.USER, key.getSchemaVersion()); - checkSchema(schemaDto, RecordFiles.NOTIFICATION_SCHEMA); - schema = schemaDto.getSchema(); - fileName = MessageFormatter.arrayFormat(DATA_NAME_PATTERN, new Object[] { "notification", key.getSchemaVersion() }) - .getMessage(); +// schemaDto = notificationService.findNotificationSchemaByAppIdAndTypeAndVersion(key.getApplicationId(), +// NotificationTypeDto.USER, key.getSchemaVersion()); +// checkSchema(schemaDto, RecordFiles.NOTIFICATION_SCHEMA); +// schema = schemaDto.getCtlSchemaId(); +// fileName = MessageFormatter.arrayFormat(DATA_NAME_PATTERN, new Object[] { "notification", key.getSchemaVersion() }) +// .getMessage(); break; case PROFILE_SCHEMA: throw new RuntimeException("Not implemented!"); diff --git a/server/node/src/main/resources/kaa-node.properties b/server/node/src/main/resources/kaa-node.properties index 5fcaa5fe39..a8bd5e5f93 100644 --- a/server/node/src/main/resources/kaa-node.properties +++ b/server/node/src/main/resources/kaa-node.properties @@ -31,7 +31,7 @@ admin_port=8080 zk_enabled=true # Zookeeper service url list. -zk_host_port_list=localhost:2181 +zk_host_port_list=10.2.3.80:2181 # The max retry time in milliseconds. zk_max_retry_time=3000 diff --git a/server/node/src/main/resources/org/kaaproject/kaa/server/admin/client/i18n/KaaAdminConstants.properties b/server/node/src/main/resources/org/kaaproject/kaa/server/admin/client/i18n/KaaAdminConstants.properties index 69253e6955..72172a6afd 100644 --- a/server/node/src/main/resources/org/kaaproject/kaa/server/admin/client/i18n/KaaAdminConstants.properties +++ b/server/node/src/main/resources/org/kaaproject/kaa/server/admin/client/i18n/KaaAdminConstants.properties @@ -521,6 +521,8 @@ uploadFromFile = Upload from file user = User +updateConflict = Conflict occurred during update: + userDetails = User details userEmailNotDefined = Requested user does not have email diff --git a/server/node/src/test/java/org/kaaproject/kaa/server/control/AbstractTestControlServer.java b/server/node/src/test/java/org/kaaproject/kaa/server/control/AbstractTestControlServer.java index b303644ab1..9e9d3dcd17 100644 --- a/server/node/src/test/java/org/kaaproject/kaa/server/control/AbstractTestControlServer.java +++ b/server/node/src/test/java/org/kaaproject/kaa/server/control/AbstractTestControlServer.java @@ -942,19 +942,22 @@ protected TopicDto createTopic(String appId, TopicTypeDto type) throws Exception */ protected NotificationSchemaDto createNotificationSchema(String appId, NotificationTypeDto type) throws Exception { NotificationSchemaDto notificationSchema = new NotificationSchemaDto(); - notificationSchema.setType(type); notificationSchema.setName(generateString("Test Schema")); notificationSchema.setDescription(generateString("Test Desc")); + notificationSchema.setType(type); if (strIsEmpty(appId)) { ApplicationDto applicationDto = createApplication(tenantAdminDto); notificationSchema.setApplicationId(applicationDto.getId()); } else { notificationSchema.setApplicationId(appId); } + CTLSchemaDto ctlSchema = this.createCTLSchema(this.ctlRandomFieldType(), CTL_DEFAULT_NAMESPACE, 1, tenantAdminDto.getTenantId(), null, null, null); + notificationSchema.setCtlSchemaId(ctlSchema.getId()); + loginTenantDeveloper(tenantDeveloperDto.getUsername()); NotificationSchemaDto savedSchema = client - .createNotificationSchema(notificationSchema, - AdminClient.getStringResource("BasicSystemNotification", BasicSystemNotification.SCHEMA$.toString())); + .createNotificationSchema(notificationSchema/*, + AdminClient.getStringResource("BasicSystemNotification", BasicSystemNotification.SCHEMA$.toString())*/); return savedSchema; } @@ -974,9 +977,11 @@ protected NotificationSchemaDto createUserNotificationSchema(String appId) throw } else { notificationSchema.setApplicationId(appId); } + CTLSchemaDto ctlSchema = this.createCTLSchema(this.ctlRandomFieldType(), CTL_DEFAULT_NAMESPACE, 1, tenantAdminDto.getTenantId(), null, null, null); + notificationSchema.setCtlSchemaId(ctlSchema.getId()); loginTenantDeveloper(tenantDeveloperDto.getUsername()); NotificationSchemaDto savedSchema = client - .createNotificationSchema(notificationSchema, TEST_USER_NOTIFICATION_SCHEMA); + .createNotificationSchema(notificationSchema); return savedSchema; } @@ -1185,23 +1190,23 @@ protected ApplicationEventFamilyMapDto createApplicationEventFamilyMap() throws /** * Creates the application event family map. * - * @param applicationId the application id + * @param applicationToken the application token * @param ecfId the ecf id * @param version the version * @return the application event family map dto * @throws Exception the exception */ - protected ApplicationEventFamilyMapDto createApplicationEventFamilyMap(String applicationId, String ecfId, int version) throws Exception { + protected ApplicationEventFamilyMapDto createApplicationEventFamilyMap(String applicationToken, String ecfId, int version) throws Exception { ApplicationEventFamilyMapDto applicationEventFamilyMap = new ApplicationEventFamilyMapDto(); String tenantId = null; - if (strIsEmpty(applicationId)) { + if (strIsEmpty(applicationToken)) { ApplicationDto application = createApplication(tenantAdminDto); tenantId = application.getTenantId(); applicationEventFamilyMap.setApplicationId(application.getId()); } else { - applicationEventFamilyMap.setApplicationId(applicationId); - ApplicationDto application = client.getApplication(applicationId); + ApplicationDto application = client.getApplicationByApplicationToken(applicationToken); + applicationEventFamilyMap.setApplicationId(application.getId()); tenantId = application.getTenantId(); } EventClassFamilyDto eventClassFamily = null; @@ -1252,7 +1257,7 @@ protected String ctlRandomFieldType() { } protected CTLSchemaDto createCTLSchema(String name, String namespace, int version, - String tenantId, String applicationId, Set dependencies, + String tenantId, String applicationToken, Set dependencies, Map fields) throws Exception { LOG.debug("Generating CTL schema..."); @@ -1289,7 +1294,7 @@ protected CTLSchemaDto createCTLSchema(String name, String namespace, int versio LOG.debug("CTL schema generated: " + body); - return client.saveCTLSchema(body.toString(), tenantId, applicationId); + return client.saveCTLSchemaWithAppToken(body.toString(), tenantId, applicationToken); } /** diff --git a/test-gh-pages-current b/test-gh-pages-current new file mode 160000 index 0000000000..cbe7d24fed --- /dev/null +++ b/test-gh-pages-current @@ -0,0 +1 @@ +Subproject commit cbe7d24fed3a971ce0ba80125b16d84e6f0389cf diff --git a/test-gh-pages.sh b/test-gh-pages.sh new file mode 100755 index 0000000000..07ae3687d7 --- /dev/null +++ b/test-gh-pages.sh @@ -0,0 +1,54 @@ +#!/bin/sh +# +# Copyright 2014-2016 CyberVision, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +curr_tag=$(git tag --contains) +git_root=$(pwd) + +# if current tag is empty we should use alias current instead of tag name +if [ x"$curr_tag" = x ]; then + curr_tag="current" +fi + +if [ -d doc ]; then + echo "Local deploing gh-pages for $curr_tag tag" + jekyll_root=test-gh-pages-$curr_tag + if [ ! -d $jekyll_root ]; then + git clone .git --branch gh-pages-stub $jekyll_root --single-branch + + mkdir -p $jekyll_root/kaa + mkdir -p $jekyll_root/kaa/m + mkdir -p $jekyll_root/_data + ln -s "$PWD/doc" "$PWD/$jekyll_root/kaa/$curr_tag" + ln -s "$PWD/doc" "$PWD/$jekyll_root/kaa/latest" + + submodule_cmd=$(printf 'echo $(basename $name) $(pwd) %s' "$git_root") + git submodule foreach "$submodule_cmd" + + submodule_cmd=$(printf '[ -d doc ] + && ln -sfn $(pwd)/doc %s/%s/%s/$(basename $name) && echo "Created submodule doc symlink for $(basename $name)" + || echo "Missing doc folder for $(basename $name)"' \ + "$git_root" "$jekyll_root" 'kaa/m') + git submodule foreach "$submodule_cmd" + fi + cd $jekyll_root + ruby scripts/create_global_toc.rb + jekyll serve --host=0.0.0.0 +else + echo "Nothing to do" +fi