diff --git a/CMakeLists.txt b/CMakeLists.txt index bf84577..55df93e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,13 @@ set(CMAKE_CXX_STANDARD 17) if(UNIX AND APPLE) set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) + set(MYSQL_ROOT_DIR /usr/local/opt/mysql-client) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) +endif() + +if(UNIX AND NOT APPLE) + set(PostgreSQL_TYPE_INCLUDE_DIR /usr/include/postgresql) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) endif() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/build) @@ -19,7 +26,6 @@ else() endif() set(BUILD_SHARED_LIBS 1) - add_definitions(-DAPP_VERSION="${CMAKE_PROJECT_VERSION}" -DBUILD_NUMBER="${BUILD_NUM}" -DAWS_CUSTOM_MEMORY_MANAGEMENT) set(Boost_USE_STATIC_LIBS OFF) @@ -29,29 +35,36 @@ set(Boost_USE_STATIC_RUNTIME OFF) find_package(Boost REQUIRED system) find_package(OpenSSL REQUIRED) find_package(AWSSDK REQUIRED COMPONENTS s3) -find_package(CppKafka REQUIRED) find_package(Poco REQUIRED COMPONENTS Crypto JWT Net Util NetSSL Data DataSQLite) -include_directories(/usr/local/include /usr/local/opt/openssl/include src include/kafka) +if(SMALL_BUILD) + find_package(Poco REQUIRED COMPONENTS Crypto JWT Net Util NetSSL Data DataSQLite) +else() + find_package(CppKafka REQUIRED) + find_package(PostgreSQL REQUIRED) + find_package(MySQL REQUIRED) + find_package(ODBC REQUIRED) + find_package(Poco REQUIRED COMPONENTS JSON Crypto JWT Net Util NetSSL Data DataSQLite DataPostgreSQL DataMySQL DataODBC) +endif() + +include_directories(/usr/local/include /usr/local/opt/openssl/include src include/kafka /usr/local/opt/mysql-client/include) add_executable( ucentralfms build - src/Daemon.cpp src/Daemon.h src/RESTAPI_objects.h src/RESTAPI_objects.cpp - src/StorageService.cpp src/StorageService.h src/storage_tables.cpp - src/storage_sqlite.cpp src/SubSystemServer.cpp src/SubSystemServer.h + src/Daemon.cpp src/Daemon.h + src/StorageService.cpp src/StorageService.h + src/storage_tables.cpp src/storage_sqlite.cpp + src/SubSystemServer.cpp src/SubSystemServer.h src/RESTAPI_handler.cpp src/RESTAPI_handler.h - src/Utils.cpp src/Utils.h src/storage_callbacks.cpp src/storage_firmwares.cpp - src/storage_latestfirmware.cpp src/RESTAPI_server.cpp src/RESTAPI_server.h + src/storage_firmwares.cpp + src/storage_mysql.cpp src/storage_pgql.cpp src/storage_odbc.cpp + src/Utils.cpp src/Utils.h + src/RESTAPI_server.cpp src/RESTAPI_server.h src/RESTAPI_firmwaresHandler.cpp src/RESTAPI_firmwaresHandler.h - src/RESTAPI_callbacksHandler.cpp src/RESTAPI_callbacksHandler.h - src/RESTAPI_callbackHandler.cpp src/RESTAPI_callbackHandler.h src/RESTAPI_firmwareHandler.cpp src/RESTAPI_firmwareHandler.h - src/RESTAPI_latestFirmwareListHandler.cpp src/RESTAPI_latestFirmwareListHandler.h + src/RESTAPI_GWobjects.cpp src/RESTAPI_GWobjects.h src/ALBHealthCheckServer.h - src/RESTAPI_callbackChannel.cpp src/RESTAPI_callbackChannel.h - src/NotificationMgr.cpp src/NotificationMgr.h src/s3bucketreader.cpp src/s3bucketreader.h - src/RESTAPI_newFirmwareAvailable.cpp src/RESTAPI_newFirmwareAvailable.h src/ManifestCreator.cpp src/ManifestCreator.h src/KafkaManager.cpp src/KafkaManager.h src/MicroService.h src/MicroService.cpp @@ -61,8 +74,13 @@ add_executable( ucentralfms src/OpenAPIRequest.h src/OpenAPIRequest.cpp src/RESTAPI_InternalServer.cpp src/RESTAPI_InternalServer.h src/RESTAPI_utils.cpp src/RESTAPI_utils.h - ) + src/RESTAPI_FMSObjects.cpp src/RESTAPI_FMSObjects.h + src/storage_firmwares.h src/storage_history.cpp + src/storage_history.h src/storage_deviceTypes.cpp src/storage_deviceTypes.h) target_link_libraries(ucentralfms PUBLIC - ${Poco_LIBRARIES} ${Boost_LIBRARIES} ${ZLIB_LIBRARIES} ${AWSSDK_LINK_LIBRARIES} CppKafka::cppkafka ) + ${Poco_LIBRARIES} ${MySQL_LIBRARIES} + ${ODBC_LIBRARIES} ${Boost_LIBRARIES} + ${ZLIB_LIBRARIES} ${AWSSDK_LINK_LIBRARIES} + CppKafka::cppkafka ) diff --git a/cmake/CppKafkaConfig.cmake b/cmake/CppKafkaConfig.cmake new file mode 100644 index 0000000..656d2d6 --- /dev/null +++ b/cmake/CppKafkaConfig.cmake @@ -0,0 +1,57 @@ + +####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() ####### +####### Any changes to this file will be overwritten by the next CMake run #### +####### The input file was config.cmake.in ######## + +get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() + +#################################################################################### + +include(CMakeFindDependencyMacro) + +# Add FindRdKafka.cmake +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}") + +set(RDKAFKA_MIN_VERSION_HEX "0x00090400") + +# Find boost optional +find_dependency(Boost REQUIRED) + +# Try to find the RdKafka configuration file if present. +# This will search default system locations as well as RdKafka_ROOT and RdKafka_DIR paths if specified. +find_package(RdKafka QUIET CONFIG) +set(RDKAFKA_TARGET_IMPORTS ${RdKafka_FOUND}) +if (NOT RdKafka_FOUND) + find_dependency(RdKafka REQUIRED MODULE) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/CppKafkaTargets.cmake") + +# Export 'CppKafka_ROOT' +set_and_check(CppKafka_ROOT "${PACKAGE_PREFIX_DIR}") + +# Export 'CppKafka_INSTALL_INCLUDE_DIR' +set_and_check(CppKafka_INSTALL_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include") + +# Export 'CppKafka_INSTALL_LIB_DIR' +set_and_check(CppKafka_INSTALL_LIB_DIR "${PACKAGE_PREFIX_DIR}/lib") + +# Validate installed components +check_required_components("CppKafka") diff --git a/cmake/FindMySQL.cmake b/cmake/FindMySQL.cmake new file mode 100644 index 0000000..614c667 --- /dev/null +++ b/cmake/FindMySQL.cmake @@ -0,0 +1,133 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#.rst: +# FindMySQL +# ------- +# +# Find MySQL Runtime +# +# This will define the following variables:: +# +# MYSQL_FOUND - True if the system has the libraries +# MYSQL_INCLUDE_DIRS - where to find the headers +# MYSQL_LIBRARIES - where to find the libraries +# MYSQL_DEFINITIONS - compile definitons +# +# Hints: +# Set ``MYSQL_ROOT_DIR`` to the root directory of an installation. +# +include(FindPackageHandleStandardArgs) + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_MYSQL QUIET mysqlclient) +pkg_check_modules(PC_MARIADB QUIET mariadb) + +SET(BINDIR32_ENV_NAME "ProgramFiles(x86)") +SET(BINDIR32 $ENV{${BINDIR32_ENV_NAME}}) + +find_path(MYSQL_INCLUDE_DIR mysql/mysql.h + HINTS + ${MYSQL_ROOT_DIR}/include + ${MYSQL_ROOT_INCLUDE_DIRS} + PATHS + ${PC_MYSQL_INCLUDE_DIRS} + ${PC_MARIADB_INCLUDE_DIRS} + /usr/include + /usr/local/include + /opt/mysql/mysql/include + /usr/local/mysql/include + $ENV{MYSQL_INCLUDE_DIR} + $ENV{MYSQL_DIR}/include + $ENV{ProgramFiles}/MySQL/*/include + ${BINDIR32}/MySQL/*/include + $ENV{SystemDrive}/MySQL/*/include + $ENV{MARIADB_INCLUDE_DIR} + $ENV{MARIADB_DIR}/include + ${MARIADB_INCLUDE_DIR} + ${MARIADB_DIR}/include + PATH_SUFFIXES + mysql + mariadb +) + +if (MSVC) + if (CMAKE_BUILD_TYPE STREQUAL Debug) + set(libsuffixDist debug) + set(libsuffixBuild Debug) + else (CMAKE_BUILD_TYPE STREQUAL Debug) + set(libsuffixDist opt) + set(libsuffixBuild Release) + set(WIN_MYSQL_DEFINITONS " -DDBUG_OFF") + endif (CMAKE_BUILD_TYPE STREQUAL Debug) + + find_library(MYSQL_LIBRARY NAMES mysqlclient + HINTS + ${MYSQL_ROOT_DIR}/lib + ${MYSQL_ROOT_LIBRARY_DIRS} + PATHS + ${PC_MYSQL_LIBRARY_DIRS} + ${PC_MARIADB_LIBRARY_DIRS} + $ENV{MYSQL_DIR}/lib + $ENV{MYSQL_DIR}/libmysql + $ENV{MYSQL_DIR}/client + $ENV{ProgramFiles}/MySQL/*/lib + ${BINDIR32}/MySQL/*/lib + $ENV{SystemDrive}/MySQL/*/lib + PATH_SUFFIXES + vs12 + vs11 + vs10 + ${libsuffixDist} + ${libsuffixBuild} + ) +else() + find_library(MYSQL_LIBRARY NAMES mysqlclient mysqlclient_r mariadbclient + HINTS + ${MYSQL_ROOT_DIR}/lib + ${MYSQL_ROOT_LIBRARY_DIRS} + PATHS + ${PC_MYSQL_LIBRARY_DIRS} + ${PC_MARIADB_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + /usr/local/mysql/lib + /opt/mysql/mysql/lib + $ENV{MYSQL_DIR}/libmysql_r/.libs + $ENV{MYSQL_DIR}/lib + ${MYSQL_DIR}/lib + PATH_SUFFIXES + mysql + mariadb + ) +endif() + +set(MYSQL_VERSION ${PC_MYSQL_VERSION}) + +find_package_handle_standard_args(MySQL + FOUND_VAR MYSQL_FOUND + REQUIRED_VARS + MYSQL_INCLUDE_DIR + MYSQL_LIBRARY + VERSION_VAR MYSQL_VERSION +) + +if(MYSQL_FOUND) + set(MYSQL_LIBRARIES ${MYSQL_LIBRARY}) + set(MYSQL_INCLUDE_DIRS ${MYSQL_INCLUDE_DIR}) + set(MYSQL_DEFINITIONS "${PC_MYSQL_CFLAGS_OTHER}${WIN_MYSQL_DEFINITONS}") +endif() + +if(MYSQL_FOUND AND NOT TARGET MySQL::client) + add_library(MySQL::client UNKNOWN IMPORTED) + set_target_properties(MySQL::client PROPERTIES + IMPORTED_LOCATION "${MYSQL_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${PC_MYSQL_CFLAGS_OTHER}${WIN_MYSQL_DEFINITONS}" + INTERFACE_INCLUDE_DIRECTORIES "${MYSQL_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced( + MYSQL_LIBRARY + MYSQL_INCLUDE_DIR +) diff --git a/cmake/FindPostgreSQL.cmake b/cmake/FindPostgreSQL.cmake new file mode 100644 index 0000000..ccd7688 --- /dev/null +++ b/cmake/FindPostgreSQL.cmake @@ -0,0 +1,212 @@ +# TODO(Bjoe) This is taken from cmake 3.10. For poco we need some changes here. Maybe we create an issue on cmake project +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#.rst: +# FindPostgreSQL +# -------------- +# +# Find the PostgreSQL installation. +# +# This module defines +# +# :: +# +# PostgreSQL_LIBRARIES - the PostgreSQL libraries needed for linking +# PostgreSQL_INCLUDE_DIRS - the directories of the PostgreSQL headers +# PostgreSQL_LIBRARY_DIRS - the link directories for PostgreSQL libraries +# PostgreSQL_VERSION - the version of PostgreSQL found (since CMake 2.8.8) + +# ---------------------------------------------------------------------------- +# History: +# This module is derived from the module originally found in the VTK source tree. +# +# ---------------------------------------------------------------------------- +# Note: +# PostgreSQL_ADDITIONAL_VERSIONS is a variable that can be used to set the +# version mumber of the implementation of PostgreSQL. +# In Windows the default installation of PostgreSQL uses that as part of the path. +# E.g C:\Program Files\PostgreSQL\8.4. +# Currently, the following version numbers are known to this module: +# "9.6" "9.5" "9.4" "9.3" "9.2" "9.1" "9.0" "8.4" "8.3" "8.2" "8.1" "8.0" +# +# To use this variable just do something like this: +# set(PostgreSQL_ADDITIONAL_VERSIONS "9.2" "8.4.4") +# before calling find_package(PostgreSQL) in your CMakeLists.txt file. +# This will mean that the versions you set here will be found first in the order +# specified before the default ones are searched. +# +# ---------------------------------------------------------------------------- +# You may need to manually set: +# PostgreSQL_ROOT_DIR - that points to the root of where you have installed PostgreSQL +# PostgreSQL_INCLUDE_DIR - the path to where the PostgreSQL include files are. +# PostgreSQL_LIBRARY_DIR - The path to where the PostgreSQL library files are. +# If FindPostgreSQL.cmake cannot find the include files or the library files. +# +# ---------------------------------------------------------------------------- +# The following variables are set if PostgreSQL is found: +# PostgreSQL_FOUND - Set to true when PostgreSQL is found. +# PostgreSQL_INCLUDE_DIRS - Include directories for PostgreSQL +# PostgreSQL_LIBRARY_DIRS - Link directories for PostgreSQL libraries +# PostgreSQL_LIBRARIES - The PostgreSQL libraries. +# +# ---------------------------------------------------------------------------- +# If you have installed PostgreSQL in a non-standard location. +# (Please note that in the following comments, it is assumed that +# points to the root directory of the include directory of PostgreSQL.) +# Then you have three options. +# 1) After CMake runs, set PostgreSQL_INCLUDE_DIR to /include and +# PostgreSQL_LIBRARY_DIR to wherever the library pq (or libpq in windows) is +# 2) Use CMAKE_INCLUDE_PATH to set a path to /PostgreSQL<-version>. This will allow find_path() +# to locate PostgreSQL_INCLUDE_DIR by utilizing the PATH_SUFFIXES option. e.g. In your CMakeLists.txt file +# set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "/include") +# 3) Set an environment variable called ${PostgreSQL_ROOT} / ${PostgreSQL_ROOT_DIR} that points to the root of where you have +# installed PostgreSQL, e.g. . +# +# ---------------------------------------------------------------------------- + +set(PostgreSQL_INCLUDE_PATH_DESCRIPTION "top-level directory containing the PostgreSQL include directories. E.g /usr/local/include/PostgreSQL/8.4 or C:/Program Files/PostgreSQL/8.4/include") +set(PostgreSQL_INCLUDE_DIR_MESSAGE "Set the PostgreSQL_INCLUDE_DIR cmake cache entry to the ${PostgreSQL_INCLUDE_PATH_DESCRIPTION}") +set(PostgreSQL_LIBRARY_PATH_DESCRIPTION "top-level directory containing the PostgreSQL libraries.") +set(PostgreSQL_LIBRARY_DIR_MESSAGE "Set the PostgreSQL_LIBRARY_DIR cmake cache entry to the ${PostgreSQL_LIBRARY_PATH_DESCRIPTION}") +set(PostgreSQL_ROOT_DIR_MESSAGE "Set the PostgreSQL_ROOT system variable to where PostgreSQL is found on the machine E.g C:/Program Files/PostgreSQL/8.4") + + +set(PostgreSQL_KNOWN_VERSIONS ${PostgreSQL_ADDITIONAL_VERSIONS} + "10" "9.6" "9.5" "9.4" "9.3" "9.2" "9.1" "9.0" "8.4" "8.3" "8.2" "8.1" "8.0") + +# Define additional search paths for root directories. +set(PostgreSQL_ROOT_DIRECTORIES + ENV PostgreSQL_ROOT + ${PostgreSQL_ROOT} + ${PostgreSQL_ROOT_DIR} +) +foreach(suffix ${PostgreSQL_KNOWN_VERSIONS}) + if(WIN32) + list(APPEND PostgreSQL_LIBRARY_ADDITIONAL_SEARCH_SUFFIXES + "PostgreSQL/${suffix}/lib") + list(APPEND PostgreSQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES + "PostgreSQL/${suffix}/include") + list(APPEND PostgreSQL_TYPE_ADDITIONAL_SEARCH_SUFFIXES + "PostgreSQL/${suffix}/include/server") + endif() + if(UNIX) + list(APPEND PostgreSQL_LIBRARY_ADDITIONAL_SEARCH_SUFFIXES + "pgsql-${suffix}/lib") + list(APPEND PostgreSQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES + "pgsql-${suffix}/include") + list(APPEND PostgreSQL_TYPE_ADDITIONAL_SEARCH_SUFFIXES + "postgresql/${suffix}/server" + "pgsql-${suffix}/include/server") + endif() +endforeach() + +if(UNIX) + list(APPEND PostgreSQL_ROOT_DIRECTORIES + "/usr") + list(APPEND PostgreSQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES + "include/postgresql") +endif() + +# +# Look for an installation. +# +find_path(PostgreSQL_INCLUDE_DIR + NAMES libpq-fe.h + HINTS + ${PostgreSQL_ROOT_INCLUDE_DIRS} + PATHS + # Look in other places. + ${PostgreSQL_ROOT_DIRECTORIES} + PATH_SUFFIXES + pgsql + postgresql + include + ${PostgreSQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES} + # Help the user find it if we cannot. + DOC "The ${PostgreSQL_INCLUDE_DIR_MESSAGE}" +) + +# TODO(Bjoe) It is not needed to build an PostgreSQL client. Maybe create an issue on cmake project +# find_path(PostgreSQL_TYPE_INCLUDE_DIR +# NAMES catalog/pg_type.h +# PATHS +# # Look in other places. +# ${PostgreSQL_ROOT_DIRECTORIES} +# PATH_SUFFIXES +# postgresql +# pgsql/server +# postgresql/server +# include/server +# ${PostgreSQL_TYPE_ADDITIONAL_SEARCH_SUFFIXES} +# # Help the user find it if we cannot. +# DOC "The ${PostgreSQL_INCLUDE_DIR_MESSAGE}" +# ) + +# The PostgreSQL library. +set(PostgreSQL_LIBRARY_TO_FIND pq) +# Setting some more prefixes for the library +set(PostgreSQL_LIB_PREFIX "") +if(WIN32) + set(PostgreSQL_LIB_PREFIX ${PostgreSQL_LIB_PREFIX} "lib") + set(PostgreSQL_LIBRARY_TO_FIND ${PostgreSQL_LIB_PREFIX}${PostgreSQL_LIBRARY_TO_FIND}) +endif() + +find_library(PostgreSQL_LIBRARY + NAMES ${PostgreSQL_LIBRARY_TO_FIND} + HINTS + ${PostgreSQL_ROOT_LIBRARY_DIRS} + PATHS + ${PostgreSQL_ROOT_DIRECTORIES} + PATH_SUFFIXES + lib + ${PostgreSQL_LIBRARY_ADDITIONAL_SEARCH_SUFFIXES} + # Help the user find it if we cannot. + DOC "The ${PostgreSQL_LIBRARY_DIR_MESSAGE}" +) +get_filename_component(PostgreSQL_LIBRARY_DIR ${PostgreSQL_LIBRARY} PATH) + +if(PostgreSQL_INCLUDE_DIR) + # Some platforms include multiple pg_config.hs for multi-lib configurations + # This is a temporary workaround. A better solution would be to compile + # a dummy c file and extract the value of the symbol. + file(GLOB _PG_CONFIG_HEADERS "${PostgreSQL_INCLUDE_DIR}/pg_config*.h") + foreach(_PG_CONFIG_HEADER ${_PG_CONFIG_HEADERS}) + if(EXISTS "${_PG_CONFIG_HEADER}") + file(STRINGS "${_PG_CONFIG_HEADER}" pgsql_version_str + REGEX "^#define[\t ]+PG_VERSION[\t ]+\".*\"") + if(pgsql_version_str) + string(REGEX REPLACE "^#define[\t ]+PG_VERSION[\t ]+\"([^\"]*)\".*" + "\\1" PostgreSQL_VERSION "${pgsql_version_str}") + break() + endif() + endif() + endforeach() + unset(pgsql_version_str) +endif() + +# Did we find anything? +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + PostgreSQL + REQUIRED_VARS PostgreSQL_LIBRARY PostgreSQL_INCLUDE_DIR #PostgreSQL_TYPE_INCLUDE_DIR + VERSION_VAR PostgreSQL_VERSION +) +set(PostgreSQL_FOUND ${POSTGRESQL_FOUND}) + +# Now try to get the include and library path. +if(PostgreSQL_FOUND) + set(PostgreSQL_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIR} ) #${PostgreSQL_TYPE_INCLUDE_DIR} ) + set(PostgreSQL_LIBRARY_DIRS ${PostgreSQL_LIBRARY_DIR} ) + set(PostgreSQL_LIBRARIES ${PostgreSQL_LIBRARY}) +endif() + +if(PostgreSQL_FOUND AND NOT TARGET PostgreSQL::PostgreSQL) + add_library(PostgreSQL::PostgreSQL UNKNOWN IMPORTED) + set_target_properties(PostgreSQL::PostgreSQL PROPERTIES + IMPORTED_LOCATION "${PostgreSQL_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${PostgreSQL_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(PostgreSQL_INCLUDE_DIR PostgreSQL_LIBRARY ) #PostgreSQL_TYPE_INCLUDE_DIR diff --git a/cmake/FindRdKafka.cmake b/cmake/FindRdKafka.cmake new file mode 100644 index 0000000..d402fe1 --- /dev/null +++ b/cmake/FindRdKafka.cmake @@ -0,0 +1,73 @@ +# This find module helps find the RdKafka module. It exports the following variables: +# - RdKafka_INCLUDE_DIR : The directory where rdkafka.h is located. +# - RdKafka_LIBNAME : The name of the library, i.e. librdkafka.a, librdkafka.so, etc. +# - RdKafka_LIBRARY_PATH : The full library path i.e. /${RdKafka_LIBNAME} +# - RdKafka::rdkafka : Imported library containing all above properties set. + +if (CPPKAFKA_RDKAFKA_STATIC_LIB) + set(RDKAFKA_PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX}) + set(RDKAFKA_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(RDKAFKA_LIBRARY_TYPE STATIC) +else() + set(RDKAFKA_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX}) + set(RDKAFKA_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(RDKAFKA_LIBRARY_TYPE SHARED) +endif() + +set(RdKafka_LIBNAME ${RDKAFKA_PREFIX}rdkafka${RDKAFKA_SUFFIX}) + +find_path(RdKafka_INCLUDE_DIR + NAMES librdkafka/rdkafka.h + HINTS ${RdKafka_ROOT}/include +) + +find_library(RdKafka_LIBRARY_PATH + NAMES ${RdKafka_LIBNAME} rdkafka + HINTS ${RdKafka_ROOT}/lib ${RdKafka_ROOT}/lib64 +) + +# Check lib paths +if (CPPKAFKA_CMAKE_VERBOSE) + get_property(FIND_LIBRARY_32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS) + get_property(FIND_LIBRARY_64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS) + message(STATUS "RDKAFKA search 32-bit library paths: ${FIND_LIBRARY_32}") + message(STATUS "RDKAFKA search 64-bit library paths: ${FIND_LIBRARY_64}") + message(STATUS "RdKafka_ROOT = ${RdKafka_ROOT}") + message(STATUS "RdKafka_INCLUDE_DIR = ${RdKafka_INCLUDE_DIR}") + message(STATUS "RdKafka_LIBNAME = ${RdKafka_LIBNAME}") + message(STATUS "RdKafka_LIBRARY_PATH = ${RdKafka_LIBRARY_PATH}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(RdKafka DEFAULT_MSG + RdKafka_LIBNAME + RdKafka_LIBRARY_PATH + RdKafka_INCLUDE_DIR +) + +set(CONTENTS "#include \n #if RD_KAFKA_VERSION >= ${RDKAFKA_MIN_VERSION_HEX}\n int main() { }\n #endif") +set(FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/rdkafka_version_test.cpp) +file(WRITE ${FILE_NAME} ${CONTENTS}) + +try_compile(RdKafka_FOUND ${CMAKE_CURRENT_BINARY_DIR} + SOURCES ${FILE_NAME} + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${RdKafka_INCLUDE_DIR}") + +if (RdKafka_FOUND) + add_library(RdKafka::rdkafka ${RDKAFKA_LIBRARY_TYPE} IMPORTED GLOBAL) + set(RDKAFKA_DEPENDENCIES pthread) +# set(RDKAFKA_DEPENDENCIES pthread rt crypto dl z) + set_target_properties(RdKafka::rdkafka PROPERTIES + IMPORTED_NAME RdKafka + IMPORTED_LOCATION "${RdKafka_LIBRARY_PATH}" + INTERFACE_INCLUDE_DIRECTORIES "${RdKafka_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES "${RDKAFKA_DEPENDENCIES}") + message(STATUS "Found valid rdkafka version") + mark_as_advanced( + RDKAFKA_LIBRARY + RdKafka_INCLUDE_DIR + RdKafka_LIBRARY_PATH + ) +else() + message(FATAL_ERROR "Failed to find valid rdkafka version") +endif() diff --git a/openapi/ucentralfws.yaml b/openapi/ucentralfws.yaml index 78fbcd9..2f6d893 100644 --- a/openapi/ucentralfws.yaml +++ b/openapi/ucentralfws.yaml @@ -2,7 +2,7 @@ openapi: 3.0.1 info: title: uCentral Firmware Service API description: A process to manage new uCentral firmware distribution. - version: 0.0.2 + version: 0.1.0 license: name: BSD3 url: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE @@ -11,7 +11,7 @@ info: url: https://www.ucentral.info/support servers: - - url: 'https://localhost:15055/api/v1' + - url: 'https://localhost:16003/api/v1' security: - ApiKeyAuth: [] @@ -74,8 +74,8 @@ components: description: Definition of a firmware release properties: id: - type: integer - format: int64 + type: string + format: uuid deviceType: type: string description: @@ -110,7 +110,9 @@ components: latest: type: boolean notes: - type: string + type: array + items: + $ref: '#/components/schemas/NoteInfo' created: type: integer format: int64 @@ -127,8 +129,8 @@ components: type: object properties: id: - type: integer - format: int64 + type: string + format: uuid deviceType: type: string manufacturer: @@ -138,7 +140,9 @@ components: policy: type: string notes: - type: string + type: array + items: + $ref: '#/components/schemas/NoteInfo' lastUpdate: type: integer format: int64 @@ -154,44 +158,12 @@ components: items: $ref: '#/components/schemas/DeviceType' - DeviceEntry: - type: object - properties: - id: - type: integer - format: int64 - campaignId: - type: integer - format: int64 - serialNumber: - type: string - commandUUID: - type: string - format: uuid - runAt: - type: integer - format: int64 - submitted: - type: integer - format: int64 - completed: - type: integer - format: int64 - - DeviceEntryList: - type: object - properties: - deviceEntries: - type: array - items: - $ref: '#/components/schemas/DeviceEntry' - RevisionHistoryEntry: type: object properties: id: - type: integer - format: int64 + type: string + format: uuid serialNumber: type: string revisionId: @@ -207,78 +179,11 @@ components: RevisionHistoryEntryList: type: object properties: - entries: + history: type: array items: $ref: '#/components/schemas/RevisionHistoryEntry' - Campaign: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - description: - type: string - started: - type: integer - format: int64 - canceled: - type: integer - format: int64 - startDate: - type: integer - format: int64 - schedule: - type: string - completed: - type: integer - format: int64 - deviceCount: - type: integer - completedDevices: - type: integer - submittedDevices: - type: integer - notes: - type: string - lastUpdate: - type: integer - format: int64 - created: - type: integer - format: int64 - - CampaignList: - type: object - properties: - campaigns: - type: array - items: - $ref: '#/components/schemas/Campaign' - - GenericErrorResponse: - description: Typical error response - properties: - ErrorCode: - type: integer - ErrorDetails: - type: string - ErrorDescription: - type: string - - GenericGoodAnswer: - description: used for all succesful responses. - properties: - Operation: - type: string - Details: - type: string - Code: - type: integer - ######################################################################################### ## ## These are endpoints that all services in the uCentral stack must provide @@ -335,11 +240,23 @@ components: oneOf: - $ref: '#/components/schemas/StringList' - $ref: '#/components/schemas/TagValuePairList' - ######################################################################################### - ## - ## End of uCentral system wide values - ## - ######################################################################################### + + NoteInfo: + type: object + properties: + created: + type: integer + format: int64 + createdBy: + type: string + note: + type: string + +######################################################################################### +## +## End of uCentral system wide values +## +######################################################################################### paths: /firmwares: @@ -374,6 +291,11 @@ paths: schema: type: boolean required: false + - in: query + name: deviceType + schema: + type: string + required: false responses: 200: description: List firmwares @@ -397,8 +319,8 @@ paths: - in: path name: id schema: - type: integer - format: int64 + type: string + format: uuid required: true responses: 200: @@ -421,8 +343,8 @@ paths: - in: path name: id schema: - type: integer - format: int64 + type: string + format: uuid required: true requestBody: required: true @@ -451,8 +373,8 @@ paths: - in: path name: id schema: - type: integer - format: int64 + type: string + format: uuid required: true requestBody: description: Firmware details @@ -482,8 +404,8 @@ paths: - in: path name: id schema: - type: integer - format: int64 + type: string + format: uuid required: true responses: 200: @@ -491,7 +413,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/GenericGoodAnswer' + $ref: '#/components/responses/Success' 403: $ref: '#/components/responses/Unauthorized' 404: @@ -545,8 +467,8 @@ paths: description: ID of deviceType to retrieve name: id schema: - type: integer - format: int64 + type: string + format: uuid required: true responses: 200: @@ -570,8 +492,8 @@ paths: description: ID of deviceType to retrieve name: id schema: - type: integer - format: int64 + type: string + format: uuid required: true responses: 200: @@ -591,8 +513,8 @@ paths: description: ID of deviceType to modify name: id schema: - type: integer - format: int64 + type: string + format: uuid required: true requestBody: description: Modifications for the DeviceType @@ -655,265 +577,6 @@ paths: 404: $ref: '#/components/responses/NotFound' - /campaigns: - get: - tags: - - Campaign - summary: List all cmapaigns - operationId: getCampaigns - parameters: - - in: query - description: Pagination start (starts at 1. If not specified, 1 is assumed) - name: offset - schema: - type: integer - required: false - - in: query - description: Maximum number of entries to return (if absent, no limit is assumed) - name: limit - schema: - type: integer - required: false - - in: query - description: Maximum number of entries to return (if absent, no limit is assumed) - name: startDate - schema: - type: integer - format: int64 - required: false - - in: query - description: Maximum number of entries to return (if absent, no limit is assumed) - name: endDate - schema: - type: integer - format: int64 - required: false - - in: query - description: Filter the results - name: filter - schema: - type: string - required: false - - responses: - 200: - description: List of campaigns. - content: - application/json: - schema: - $ref: '#/components/schemas/CampaignList' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - - /campaign/{id}: - get: - tags: - - Campaign - summary: Get a single campaign definition - operationId: getCampaign - parameters: - - in: path - description: ID of the campaign - name: id - schema: - type: integer - format: int64 - required: true - responses: - 200: - $ref: '#/components/schemas/Campaign' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - - delete: - tags: - - Campaign - summary: Delete a single campaign definition - operationId: deleteCampaign - parameters: - - in: path - description: ID of the campaign - name: id - schema: - type: integer - format: int64 - required: true - responses: - 200: - $ref: '#/components/responses/Success' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - - post: - tags: - - Campaign - summary: Create a single campaign definition - operationId: createCampaign - parameters: - - in: path - description: ID of the campaign - name: id - schema: - type: integer - format: int64 - required: true - requestBody: - description: Canpaign creation details - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Campaign' - responses: - 200: - $ref: '#/components/schemas/Campaign' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - - put: - tags: - - Campaign - summary: Create a single campaign definition - operationId: updateCampaign - parameters: - - in: path - description: ID of the campaign - name: id - schema: - type: integer - format: int64 - required: true - requestBody: - description: Canpaign modification details - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Campaign' - responses: - 200: - $ref: '#/components/schemas/Campaign' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - - /campaign/{id}/entries: - get: - tags: - - Campaign - summary: List all campaign entries - operationId: getCampaignEntries - parameters: - - in: path - name: id - schema: - type: integer - format: int64 - required: true - - in: query - description: Pagination start (starts at 1. If not specified, 1 is assumed) - name: offset - schema: - type: integer - required: false - - in: query - description: Maximum number of entries to return (if absent, no limit is assumed) - name: limit - schema: - type: integer - required: false - - in: query - description: Maximum number of entries to return (if absent, no limit is assumed) - name: startDate - schema: - type: integer - format: int64 - required: false - - in: query - description: Maximum number of entries to return (if absent, no limit is assumed) - name: endDate - schema: - type: integer - format: int64 - required: false - - in: query - description: Filter the results - name: filter - schema: - type: string - required: false - responses: - 200: - $ref: '#/components/schemas/DeviceEntryList' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - - /campaign/{id}/entry/{serialNumber}: - post: - tags: - - Campaign - operationId: createSingleEntry - parameters: - - in: path - name: id - schema: - type: integer - format: int64 - required: true - - in: path - name: serialNumber - schema: - type: string - required: true - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DeviceEntryList' - responses: - 200: - $ref: '#/components/schemas/DeviceEntryList' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - - delete: - tags: - - Campaign - operationId: deleteSingleEntry - parameters: - - in: path - name: id - schema: - type: integer - format: int64 - required: true - - in: path - name: serialNumber - schema: - type: string - required: true - responses: - 200: - $ref: '#/components/responses/Success' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - ######################################################################################### ## ## These are endpoints that all services in the uCentral stack must provide @@ -944,51 +607,3 @@ paths: 404: $ref: '#/components/responses/NotFound' - /callbackChannel: - post: - tags: - - Callback - summary: Generic callback hook - operationId: postCallback - parameters: - - in: query - name: subscribe - schema: - type: boolean - required: false - - in: query - name: uri - schema: - type: string - format: uri - - in: query - name: key - schema: - type: string - - in: query - name: topics - schema: - type: string - - in: query - name: id - schema: - type: string - - in: query - name: topic - schema: - type: string - requestBody: - description: A generic JSONDocument, may be empty too {} - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AnyPayload' - - responses: - 200: - $ref: '#/components/responses/Success' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' diff --git a/src/Daemon.cpp b/src/Daemon.cpp index cae1178..2df20f0 100644 --- a/src/Daemon.cpp +++ b/src/Daemon.cpp @@ -19,7 +19,6 @@ #include "StorageService.h" #include "RESTAPI_server.h" #include "RESTAPI_InternalServer.h" -#include "NotificationMgr.h" #include "ManifestCreator.h" #include "ALBHealthCheckServer.h" #include "KafkaManager.h" @@ -37,7 +36,6 @@ namespace uCentral { Types::SubSystemVec{Storage(), RESTAPI_server(), RESTAPI_InternalServer(), - NotificationMgr(), ManifestCreator() }); } diff --git a/src/FWManager.cpp b/src/FWManager.cpp deleted file mode 100644 index 04f6ab8..0000000 --- a/src/FWManager.cpp +++ /dev/null @@ -1,246 +0,0 @@ -// -// Created by stephane bourque on 2021-05-10. -// - -#include -#include - -#include "Poco/File.h" -#include "Poco/JSON/Parser.h" -#include "Poco/JSON/Object.h" -#include "Poco/JSON/Stringifier.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "FWManager.h" -#include "Daemon.h" -#include "NotificationMgr.h" -#include "StorageService.h" -#include "RESTAPI_objects.h" -#include "Utils.h" - -namespace uCentral::FWManager { - Service *Service::instance_ = nullptr; - - int Start() { - return Service::instance()->Start(); - } - - void Stop() { - Service::instance()->Stop(); - } - - bool AddJob(const std::string &UUID, const uCentral::Auth::APIKeyEntry & Entry) { - return Service::instance()->AddJob(UUID, Entry); - } - - Service::Service() noexcept: - uSubSystemServer("FirmwareMgr", "FWR-MGR", "firmwaremgr") - { - } - - int Service::Start() { - SubMutexGuard Guard(Mutex_); - - S3BucketName_ = uCentral::ServiceConfig::GetString("s3.bucketname"); - S3Region_ = uCentral::ServiceConfig::GetString("s3.region"); - S3Secret_ = uCentral::ServiceConfig::GetString("s3.secret"); - S3Key_ = uCentral::ServiceConfig::GetString("s3.key"); - S3Retry_ = uCentral::ServiceConfig::GetInt("s3.retry",60); - AwsConfig_ = Aws::MakeUnique("fws"); - if(!S3Region_.empty()) - AwsConfig_->region = S3Region_; - AwsCreds_ = Aws::MakeUnique("fws"); - AwsCreds_->SetAWSAccessKeyId(S3Key_.c_str()); - AwsCreds_->SetAWSSecretKey(S3Secret_.c_str()); - S3Client_ = Aws::MakeUnique("fws",*AwsCreds_,*AwsConfig_); - Logger_.information("Starting "); - Worker_.start(*this); - return 0; - } - - void Service::Stop() { - SubMutexGuard Guard(Mutex_); - - Logger_.information("Stopping "); - Running_ = false; - Worker_.join(); - - /*delete S3Client_; - delete AwsCreds_; - delete AwsConfig_;*/ - } - - void Service::run() { - - Running_ = true; - - // const std::string & Path = uCentral::uFileUploader::Path(); - std::string Path; - - auto Uploads=0; - - while(Running_) { - bool RemoveJob = false; - - if(Jobs_.empty()) { - if (Uploads!=0) { - uCentral::NotificationMgr::Update(); - Uploads = 0; - } - Poco::Thread::sleep(10000); - } else { - JobId JobEntry; - { - SubMutexGuard G(Mutex_); - JobEntry = Jobs_.front(); - } - - try { - Poco::File JSONFileName(Path + "/" + JobEntry.UUID + "/latest-upgrade.json"); - - if (JSONFileName.exists() && JSONFileName.isFile()) { - std::ifstream in(JSONFileName.path(),std::ios_base::in); - - Poco::JSON::Parser parser; - Poco::JSON::Object::Ptr Obj = parser.parse(in).extract(); - Poco::DynamicStruct ds = *Obj; - in.close(); - - if( ds.contains("image") && ds.contains("compatible") - && ds.contains("revision") && ds.contains("timestamp")) { - - // let's see if the image file exists - std::string ImageName{ds["image"].toString()}; - Poco::File ImageFileName(Path + "/" + JobEntry.UUID + "/" + ImageName); - if(ImageFileName.exists() && ImageFileName.isFile()) { - Poco::File JSONRealFileName{ ImageFileName.path() + ".json"}; - std::string JSONObjectName{ImageName+".json"}; - JSONFileName.copyTo(JSONRealFileName.path()); - Logger_.information(Poco::format("JOB(%s): Processing...",JobEntry.UUID)); - if(SendToS3(JSONObjectName, JSONRealFileName.path(), - ImageName, ImageFileName.path())) { - RemoveJob = true; - // create the new firmware entry - uCentral::Objects::Firmware F; - - F.UUID = uCentral::instance()->CreateUUID(); - F.Owner = JobEntry.Entry.Owner; - F.FirmwareDate = ds["timestamp"]; - F.Size = ImageFileName.getSize(); - F.DownloadCount = 0; - F.Uploaded = time(nullptr); - F.Compatible = ds["compatible"].toString(); - F.FirmwareVersion = ds["revision"].toString(); - F.FirmwareFileName = ds["image"].toString(); - F.Uploader = JobEntry.Entry.Description; - F.S3URI = "https://s3-" + S3Region_ + ".amazonaws.com/" + S3BucketName_ + "/" + ImageName; - F.Latest = 1; - - if(uCentral::Storage::AddFirmware(F)) { - Logger_.information( - Poco::format("JOB(%s): Added to firmware DB.", JobEntry.UUID)); - RemoveJob = true; - Uploads++; - } else { - Logger_.error(Poco::format("JOB(%s): Could not add the DB entry.",JobEntry.UUID)); - } - } else { - RemoveJob = false; - } - - } else { - RemoveJob = true; - Logger_.information(Poco::format("JOB(%s): Missing image file %s",JobEntry.UUID,ImageFileName.path())); - } - - } else { - Logger_.information(Poco::format("JOB(%s): missing some JSON field(s).",JobEntry.UUID)); - RemoveJob = true; - } - } else { - Logger_.information(Poco::format("JOB(%s): No JSON document.",JobEntry.UUID)); - RemoveJob = true; - } - } catch (const Poco::Exception &E) { - Logger_.log(E); - RemoveJob = true; - } - - if(RemoveJob) { - SubMutexGuard G(Mutex_); - Jobs_.pop(); - } - } - } - } - - bool Service::SendObjectToS3(const std::string &ObjectName, const std::string & ObjectFileName) { - try { - - Aws::S3::Model::PutObjectRequest Request; - Request.SetBucket(S3BucketName_.c_str()); - Request.SetKey(ObjectName.c_str()); - Request.SetACL(Aws::S3::Model::ObjectCannedACL::public_read); - - std::cout << "Attempting to add " << ObjectName << " to the bucket " << S3BucketName_ << " in region " - << S3Region_ << std::endl; - - std::shared_ptr Body = - Aws::MakeShared(ObjectFileName.c_str(), ObjectFileName.c_str(), - std::ios_base::in | std::ios_base::binary); - Request.SetBody(Body); - Aws::S3::Model::PutObjectOutcome outcome = S3Client_->PutObject(Request); - - if (outcome.IsSuccess()) { - Logger_.information(Poco::format("S3-UPLOADER: uploaded %s", ObjectName)); - return true; - } else { - Logger_.error(Poco::format("S3-UPLOADER: could not upload %s. Exception: %s. Message: %s", ObjectName, - outcome.GetError().GetExceptionName(), outcome.GetError().GetMessage())); - return false; - } - } catch (...) { - Logger_.error("Exception while uploading to S3."); - } - return false; - } - - - bool Service::SendToS3(const std::string & JSONObjectName , const std::string & JSONDocFileName, - const std::string & ImageObjectName, const std::string & ImageFileName) { - try { - - if( SendObjectToS3(JSONObjectName,JSONDocFileName) && - SendObjectToS3(ImageObjectName,ImageFileName) ) { - std::cout << "All objects sent..." << std::endl; - return true; - } - } catch(const Poco::Exception &E) { - Logger_.log(E); - } catch(...) { - Logger_.error(Poco::format("S3 Exception while sending %s",JSONDocFileName)); - } - return false; - } - - bool Service::AddJob(const std::string &UUID, const uCentral::Auth::APIKeyEntry & Entry) { - SubMutexGuard Guard(Mutex_); - - JobId NewJob{ .UUID = UUID, - .Entry = Entry}; - - Jobs_.push(NewJob); - - return true; - } - -} // namespace diff --git a/src/FWManager.h b/src/FWManager.h deleted file mode 100644 index 653ad29..0000000 --- a/src/FWManager.h +++ /dev/null @@ -1,69 +0,0 @@ -// -// Created by stephane bourque on 2021-05-10. -// - -#ifndef UCENTRALFWS_UFWMANAGER_H -#define UCENTRALFWS_UFWMANAGER_H - -#include - -#include "SubSystemServer.h" -#include -#include -#include -#include "AuthService.h" - -namespace uCentral::FWManager { - - int Start(); - void Stop(); - bool AddJob(const std::string &UUID, const uCentral::Auth::APIKeyEntry & Entry); - - struct JobId { - std::string UUID; - uCentral::Auth::APIKeyEntry Entry; - }; - -class Service : public uSubSystemServer, Poco::Runnable { - public: - - Service() noexcept; - - friend int Start(); - friend void Stop(); - - static Service *instance() { - if (instance_ == nullptr) { - instance_ = new Service; - } - return instance_; - } - friend bool AddJob(const std::string &UUID, const uCentral::Auth::APIKeyEntry & Entry); - void run() override; - - private: - static Service *instance_; - std::queue Jobs_; - Poco::Thread Worker_; - std::atomic_bool Running_=false; - std::string S3BucketName_; - std::string S3Region_; - std::string S3Key_; - std::string S3Secret_; - uint64_t S3Retry_; - Aws::UniquePtr AwsConfig_; - Aws::UniquePtr AwsCreds_; - Aws::UniquePtr S3Client_; - - int Start() override; - void Stop() override; - bool AddJob(const std::string &UUID, const uCentral::Auth::APIKeyEntry & Entry); - - bool SendToS3(const std::string & JSONObjectName , const std::string & JSONDocFileName, - const std::string & ImageObjectName, const std::string & ImageFileName); - bool SendObjectToS3(const std::string &ObjectName, const std::string & ObjectFileName); - }; - -} // namespace - -#endif //UCENTRALFWS_UFWMANAGER_H diff --git a/src/KafkaManager.cpp b/src/KafkaManager.cpp index 32f18e2..acd2d7c 100644 --- a/src/KafkaManager.cpp +++ b/src/KafkaManager.cpp @@ -106,12 +106,10 @@ namespace uCentral { cppkafka::Consumer Consumer(Config); Consumer.set_assignment_callback([this](const cppkafka::TopicPartitionList& partitions) { - std::cout << "Partition assigned: " << partitions.front().get_partition() << std::endl; - Logger_.information(Poco::format("Got assigned: %Lu...",(uint64_t )partitions.front().get_partition())); + Logger_.information(Poco::format("Partition assigned: %Lu...",(uint64_t )partitions.front().get_partition())); }); Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) { - std::cout << "Partition revocation: " << partitions.front().get_partition() << std::endl; - Logger_.information(Poco::format("Got revoked: %Lu...",(uint64_t )partitions.front().get_partition())); + Logger_.information(Poco::format("Partition revocation: %Lu...",(uint64_t )partitions.front().get_partition())); }); Types::StringVec Topics; diff --git a/src/MicroService.cpp b/src/MicroService.cpp index 4508404..cbf7716 100644 --- a/src/MicroService.cpp +++ b/src/MicroService.cpp @@ -194,6 +194,7 @@ namespace uCentral { DebugMode_ = ConfigGetBool("ucentral.system.debug",false); MyPrivateEndPoint_ = ConfigGetString("ucentral.system.uri.private"); MyPublicEndPoint_ = ConfigGetString("ucentral.system.uri.public"); + UIURI_ = ConfigGetString("ucentral.system.uri.ui"); MyHash_ = CreateHash(MyPublicEndPoint_); InitializeSubSystemServers(); ServerApplication::initialize(self); diff --git a/src/MicroService.h b/src/MicroService.h index 8bed9f3..7569c52 100644 --- a/src/MicroService.h +++ b/src/MicroService.h @@ -132,6 +132,8 @@ namespace uCentral { void SavePID(); inline uint64_t GetPID() { return Poco::Process::id(); }; + [[nodiscard]] inline const std::string GetPublicAPIEndPoint() const { return MyPublicEndPoint_ + "/api/v1"; }; + [[nodiscard]] inline const std::string & GetUIURI() const { return UIURI_;}; private: bool HelpRequested_ = false; @@ -150,6 +152,7 @@ namespace uCentral { std::string MyHash_; std::string MyPrivateEndPoint_; std::string MyPublicEndPoint_; + std::string UIURI_; std::string Version_; BusEventManager BusEventManager_; SubMutex InfraMutex_; diff --git a/src/NotificationMgr.cpp b/src/NotificationMgr.cpp deleted file mode 100644 index 405f5d7..0000000 --- a/src/NotificationMgr.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// -// Created by stephane bourque on 2021-05-11. -// -#include -#include - -#include "NotificationMgr.h" -#include "StorageService.h" -#include "RESTAPI_objects.h" - -#include "Poco/JSON/Object.h" -#include "Poco/URI.h" -#include "Poco/Net/HTTPSClientSession.h" -#include "Poco/Net/HTTPResponse.h" -#include "Poco/Net/HTTPRequest.h" -#include "Poco/Net/MediaType.h" - -namespace uCentral { - - class NotificationMgr *NotificationMgr::instance_ = nullptr; - - NotificationMgr::NotificationMgr() noexcept: - SubSystemServer("NotificationMgr", "NOTIFY-MGR", "nodifymgr") { - } - - int NotificationMgr::Start() { - SubMutexGuard Guard(Mutex_); - - Logger_.information("Starting "); - Worker_.start(*this); - - return 0; - } - - void NotificationMgr::Stop() { - SubMutexGuard Guard(Mutex_); - - Logger_.information("Stopping "); - Running_ = false; - - Worker_.wakeUp(); - Worker_.join(); - } - - void NotificationMgr::run() { - - Running_ = true; - - while (Running_) { - Poco::Thread::trySleep(2000000000); - - if(!Running_) - break; - if(!Updated_) - continue; - Updated_ = false; - - if(uCentral::Storage()->FirmwareVersion()!=ManifestVersion_) { - Poco::JSON::Object Manifest; - - uCentral::Storage()->BuildFirmwareManifest(Manifest, ManifestVersion_); - std::stringstream OS; - Poco::JSON::Stringifier stringifier; - stringifier.condense(Manifest, OS); - CurrentManifest_ = OS.str(); - } - - // Send it... - NotifyCallers(); - } - } - - void NotificationMgr::Update() { - SubMutexGuard Guard(Mutex_); - - Updated_ = true; - Worker_.wakeUp(); - } - - void NotificationMgr::NotifyCallers() { - - std::vector Callbacks; - - std::ofstream File( "latest_manifest.json" , std::ofstream::out | std::ofstream::trunc); - File << CurrentManifest_; - File.close(); - - // build the list of callbacks or update the existing callers. - if(uCentral::Storage()->GetCallbacks(0,200,Callbacks)) { - for(const auto & i:Callbacks) { - auto Index = EndPoints_.find(i.UUID); - if(Index==EndPoints_.end()) { - NotifyEndPoint E{ - .Caller = i, - .LasContact = 0, - .LastVersion = 0}; - EndPoints_[i.UUID] = E; - } else { - Index->second.Caller = i; - } - } - } - - if(EndPoints_.empty()) - return; - - for(auto & host:EndPoints_) { - if(host.second.LastVersion!=ManifestVersion_) { - if(SendManifest(host.second.Caller)) { - host.second.LastVersion = ManifestVersion_; - host.second.LasContact = time(nullptr); - } - } - } - } - - bool DoRequest(Poco::Net::HTTPSClientSession& Session, Poco::Net::HTTPRequest& Request, Poco::Net::HTTPResponse& Response, const std::string & Doc) - { - std::stringstream Body(Doc); - Request.setContentType("application/json"); - Request.setContentLength(Doc.length()); - std::ostream& OS = Session.sendRequest(Request); - Poco::StreamCopier::copyStream(Body, OS); - Session.receiveResponse(Response); - return (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK); - } - - bool NotificationMgr::SendManifest(const uCentral::Objects::Callback &Host) { - - Poco::URI Uri(Host.URI); - - Uri.addQueryParameter("topic", "ucentralfws"); - - Poco::Net::HTTPSClientSession Session(Uri.getHost(), Uri.getPort()); - Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_POST, - Uri.getPathAndQuery(), - Poco::Net::HTTPMessage::HTTP_1_1); - Request.add("X-API-KEY", Host.Token); - Poco::Net::HTTPResponse Response; - return DoRequest(Session, Request, Response, CurrentManifest_); - } - -} diff --git a/src/NotificationMgr.h b/src/NotificationMgr.h deleted file mode 100644 index 586a20d..0000000 --- a/src/NotificationMgr.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// Created by stephane bourque on 2021-05-11. -// - -#ifndef UCENTRALFWS_NOTIFICATIONMGR_H -#define UCENTRALFWS_NOTIFICATIONMGR_H - -#include - -#include "SubSystemServer.h" -#include "RESTAPI_objects.h" - -namespace uCentral { - - struct NotifyEndPoint { - uCentral::Objects::Callback Caller; - uint64_t LasContact; - uint64_t LastVersion; - }; - - class NotificationMgr : public SubSystemServer, Poco::Runnable { - public: - - NotificationMgr() noexcept; - - static NotificationMgr *instance() { - if (instance_ == nullptr) { - instance_ = new NotificationMgr; - } - return instance_; - } - void run() override; - int Start() override; - void Stop() override; - void Update(); - - void NotifyCallers(); - void SetVersion(const std::string &Manifest); - bool SendManifest(const uCentral::Objects::Callback &Host); - - private: - static NotificationMgr *instance_; - Poco::Thread Worker_; - std::atomic_bool Running_ = false; - std::atomic_bool Updated_ = false; - std::map EndPoints_; - - std::string CurrentManifest_; - uint64_t ManifestVersion_=0; - }; - - inline NotificationMgr * NotificationMgr() { return NotificationMgr::instance(); }; -} // namespace - - -#endif //UCENTRALFWS_NOTIFICATIONMGR_H diff --git a/src/RESTAPI_FMSObjects.cpp b/src/RESTAPI_FMSObjects.cpp new file mode 100644 index 0000000..26afcf8 --- /dev/null +++ b/src/RESTAPI_FMSObjects.cpp @@ -0,0 +1,154 @@ +// +// Created by stephane bourque on 2021-07-12. +// + +#include "RESTAPI_FMSObjects.h" +#include "RESTAPI_utils.h" + +using uCentral::RESTAPI_utils::field_to_json; +using uCentral::RESTAPI_utils::field_from_json; + +namespace uCentral::FMSObjects { + + void Firmware::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "id", id); + field_to_json(Obj, "deviceType", deviceType); + field_to_json(Obj, "description", description); + field_to_json(Obj, "revision", revision); + field_to_json(Obj, "uri", uri); + field_to_json(Obj, "image", image); + field_to_json(Obj, "imageDate", imageDate); + field_to_json(Obj, "size", size); + field_to_json(Obj, "downloadCount", downloadCount); + field_to_json(Obj, "firmwareHash", firmwareHash); + field_to_json(Obj, "owner", owner); + field_to_json(Obj, "location", location); + field_to_json(Obj, "uploader", uploader); + field_to_json(Obj, "digest", digest); + field_to_json(Obj, "latest", latest); + field_to_json(Obj, "notes", notes); + field_to_json(Obj, "created", created); + }; + + bool Firmware::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "id", id); + field_from_json(Obj, "deviceType", deviceType); + field_from_json(Obj, "description", description); + field_from_json(Obj, "revision", revision); + field_from_json(Obj, "uri", uri); + field_from_json(Obj, "image", image); + field_from_json(Obj, "imageDate", imageDate); + field_from_json(Obj, "size", size); + field_from_json(Obj, "downloadCount", downloadCount); + field_from_json(Obj, "firmwareHash", firmwareHash); + field_from_json(Obj, "owner", owner); + field_from_json(Obj, "location", location); + field_from_json(Obj, "uploader", uploader); + field_from_json(Obj, "digest", digest); + field_from_json(Obj, "latest", latest); + field_from_json(Obj, "notes", notes); + field_from_json(Obj, "created", created); + return true; + } catch (...) { + + } + return true; + } + + void FirmwareList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"firmwares",firmwares); + } + + bool FirmwareList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "firmwares", firmwares); + return true; + } catch (...) { + + } + return false; + } + + void DeviceType::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "id", id); + field_to_json(Obj, "deviceType", deviceType); + field_to_json(Obj, "manufacturer", manufacturer); + field_to_json(Obj, "model", model); + field_to_json(Obj, "policy", policy); + field_to_json(Obj, "notes", notes); + field_to_json(Obj, "lastUpdate", lastUpdate); + field_to_json(Obj, "created", created); + field_to_json(Obj, "id", id); + field_to_json(Obj, "id", id); + field_to_json(Obj, "id", id); + } + + bool DeviceType::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "id", id); + field_from_json(Obj, "deviceType", deviceType); + field_from_json(Obj, "manufacturer", manufacturer); + field_from_json(Obj, "model", model); + field_from_json(Obj, "policy", policy); + field_from_json(Obj, "notes", notes); + field_from_json(Obj, "lastUpdate", lastUpdate); + field_from_json(Obj, "created", created); + field_from_json(Obj, "id", id); + field_from_json(Obj, "id", id); + field_from_json(Obj, "id", id); + return true; + } catch (...) { + + } + return false; + } + + void DeviceTypeList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"deviceTypes", deviceTypes); + } + + bool DeviceTypeList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"deviceTypes", deviceTypes); + return true; + } catch(...) { + + } + return false; + } + + void RevisionHistoryEntry::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "id", id); + field_to_json(Obj, "serialNumber", serialNumber); + field_to_json(Obj, "upgraded", upgraded); + field_to_json(Obj, "commandUUID", commandUUID); + } + + bool RevisionHistoryEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "id", id); + field_from_json(Obj, "serialNumber", serialNumber); + field_from_json(Obj, "upgraded", upgraded); + field_from_json(Obj, "commandUUID", commandUUID); + return true; + } catch(...) { + + } + return false; + } + + void RevisionHistoryEntryList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"deviceTypes", history); + } + + bool RevisionHistoryEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"deviceTypes", history); + return true; + } catch(...) { + + } + return false; + } +} diff --git a/src/RESTAPI_FMSObjects.h b/src/RESTAPI_FMSObjects.h new file mode 100644 index 0000000..86d95b0 --- /dev/null +++ b/src/RESTAPI_FMSObjects.h @@ -0,0 +1,89 @@ +// +// Created by stephane bourque on 2021-07-12. +// + +#include + +#ifndef UCENTRALFMS_RESTAPI_FMSOBJECTS_H +#define UCENTRALFMS_RESTAPI_FMSOBJECTS_H + + +#include "RESTAPI_SecurityObjects.h" + +namespace uCentral::FMSObjects { + + struct Firmware { + std::string id; + std::string deviceType; + std::string description; + std::string revision; + std::string uri; + std::string image; + uint64_t imageDate; + uint64_t size; + uint64_t downloadCount; + std::string firmwareHash; + std::string owner; + std::string location; + std::string uploader; + std::string digest; + bool latest; + SecurityObjects::NoteInfoVec notes; + uint64_t created; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector FirmwareVec; + + struct FirmwareList { + FirmwareVec firmwares; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct DeviceType { + std::string id; + std::string deviceType; + std::string manufacturer; + std::string model; + std::string policy; + SecurityObjects::NoteInfoVec notes; + uint64_t lastUpdate; + uint64_t created; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector DeviceTypeVec; + + struct DeviceTypeList { + DeviceTypeVec deviceTypes; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct RevisionHistoryEntry { + std::string id; + std::string serialNumber; + uint64_t upgraded; + std::string commandUUID; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector RevisionHistoryEntryVec; + + struct RevisionHistoryEntryList { + RevisionHistoryEntryVec history; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + +} + + +#endif //UCENTRALFMS_RESTAPI_FMSOBJECTS_H diff --git a/src/RESTAPI_GWobjects.cpp b/src/RESTAPI_GWobjects.cpp new file mode 100644 index 0000000..969e69e --- /dev/null +++ b/src/RESTAPI_GWobjects.cpp @@ -0,0 +1,207 @@ +// +// License type: BSD 3-Clause License +// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE +// +// Created by Stephane Bourque on 2021-03-04. +// Arilia Wireless Inc. +// + +#include "Poco/JSON/Parser.h" +#include "Poco/JSON/Stringifier.h" + +#include "Daemon.h" +#ifdef TIP_GATEWAY_SERVICE +#include "DeviceRegistry.h" +#endif + +#include "RESTAPI_GWobjects.h" +#include "RESTAPI_handler.h" +#include "RESTAPI_utils.h" +#include "Utils.h" + +using uCentral::RESTAPI_utils::field_to_json; +using uCentral::RESTAPI_utils::field_from_json; +using uCentral::RESTAPI_utils::EmbedDocument; + +namespace uCentral::GWObjects { + + void Device::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"serialNumber", SerialNumber); +#ifdef TIP_GATEWAY_SERVICE + field_to_json(Obj,"deviceType", uCentral::Daemon::instance()->IdentifyDevice(Compatible)); +#endif + field_to_json(Obj,"macAddress", MACAddress); + field_to_json(Obj,"manufacturer", Manufacturer); + field_to_json(Obj,"UUID", UUID); + EmbedDocument("configuration", Obj, Configuration); + field_to_json(Obj,"notes", Notes); + field_to_json(Obj,"createdTimestamp", CreationTimestamp); + field_to_json(Obj,"lastConfigurationChange", LastConfigurationChange); + field_to_json(Obj,"lastConfigurationDownload", LastConfigurationDownload); + field_to_json(Obj,"lastFWUpdate", LastFWUpdate); + field_to_json(Obj,"owner", Owner); + field_to_json(Obj,"location", Location); + field_to_json(Obj,"venue", Venue); + field_to_json(Obj,"firmware", Firmware); + field_to_json(Obj,"compatible", Compatible); + field_to_json(Obj,"fwUpdatePolicy", FWUpdatePolicy); + field_to_json(Obj,"devicePassword", DevicePassword); + } + + void Device::to_json_with_status(Poco::JSON::Object &Obj) const { + to_json(Obj); + +#ifdef TIP_GATEWAY_SERVICE + ConnectionState ConState; + + if (DeviceRegistry()->GetState(SerialNumber, ConState)) { + ConState.to_json(Obj); + } else { + field_to_json(Obj,"ipAddress", "N/A"); + field_to_json(Obj,"txBytes", (uint64_t) 0); + field_to_json(Obj,"rxBytes", (uint64_t )0); + field_to_json(Obj,"messageCount", (uint64_t )0); + field_to_json(Obj,"connected", false); + field_to_json(Obj,"lastContact", "N/A"); + field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); + } +#endif + } + + bool Device::from_json(Poco::JSON::Object::Ptr Obj) { + try { + field_from_json(Obj,"serialNumber",SerialNumber); + field_from_json(Obj,"deviceType",DeviceType); + field_from_json(Obj,"macAddress",MACAddress); + field_from_json(Obj,"configuration",Configuration); + field_from_json(Obj,"notes",Notes); + field_from_json(Obj,"manufacturer",Manufacturer); + field_from_json(Obj,"owner",Owner); + field_from_json(Obj,"location",Location); + field_from_json(Obj,"venue",Venue); + field_from_json(Obj,"compatible",Compatible); + return true; + } catch (const Poco::Exception &E) { + } + return false; + } + + void Device::Print() const { + std::cout << "Device: " << SerialNumber << " DeviceType:" << DeviceType << " MACAddress:" << MACAddress << " Manufacturer:" + << Manufacturer << " " << Configuration << std::endl; + } + + void Statistics::to_json(Poco::JSON::Object &Obj) const { + EmbedDocument("data", Obj, Data); + field_to_json(Obj,"UUID", UUID); + field_to_json(Obj,"recorded", Recorded); + } + + void Capabilities::to_json(Poco::JSON::Object &Obj) const { + EmbedDocument("capabilities", Obj, Capabilities); + field_to_json(Obj,"firstUpdate", FirstUpdate); + field_to_json(Obj,"lastUpdate", LastUpdate); + } + + void DeviceLog::to_json(Poco::JSON::Object &Obj) const { + EmbedDocument("data", Obj, Data); + field_to_json(Obj,"log", Log); + field_to_json(Obj,"severity", Severity); + field_to_json(Obj,"recorded", Recorded); + field_to_json(Obj,"logType", LogType); + field_to_json(Obj,"UUID", UUID); + } + + void HealthCheck::to_json(Poco::JSON::Object &Obj) const { + EmbedDocument("values", Obj, Data); + field_to_json(Obj,"UUID", UUID); + field_to_json(Obj,"sanity", Sanity); + field_to_json(Obj,"recorded", Recorded); + } + + void DefaultConfiguration::to_json(Poco::JSON::Object &Obj) const { + EmbedDocument("configuration", Obj, Configuration); + field_to_json(Obj,"name", Name); + field_to_json(Obj,"modelIds", Models); + field_to_json(Obj,"description", Description); + field_to_json(Obj,"created", Created); + field_to_json(Obj,"lastModified", LastModified); + } + + void CommandDetails::to_json(Poco::JSON::Object &Obj) const { + EmbedDocument("details", Obj, Details); + EmbedDocument("results", Obj, Results); + field_to_json(Obj,"UUID", UUID); + field_to_json(Obj,"serialNumber", SerialNumber); + field_to_json(Obj,"command", Command); + field_to_json(Obj,"errorText", ErrorText); + field_to_json(Obj,"submittedBy", SubmittedBy); + field_to_json(Obj,"status", Status); + field_to_json(Obj,"submitted", Submitted); + field_to_json(Obj,"executed", Executed); + field_to_json(Obj,"completed", Completed); + field_to_json(Obj,"when", RunAt); + field_to_json(Obj,"errorCode", ErrorCode); + field_to_json(Obj,"custom", Custom); + field_to_json(Obj,"waitingForFile", WaitingForFile); + field_to_json(Obj,"attachFile", AttachDate); + } + + bool DefaultConfiguration::from_json(Poco::JSON::Object::Ptr Obj) { + try { + field_from_json(Obj,"name",Name); + field_from_json(Obj,"configuration",Configuration); + field_from_json(Obj,"modelIds",Models); + field_from_json(Obj,"description",Description); + return true; + } catch (const Poco::Exception &E) { + } + return false; + } + + void BlackListedDevice::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"serialNumber", SerialNumber); + field_to_json(Obj,"author", Author); + field_to_json(Obj,"reason", Reason); + field_to_json(Obj,"created", Created); + } + + void ConnectionState::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"serialNumber", SerialNumber); + field_to_json(Obj,"ipAddress", Address); + field_to_json(Obj,"txBytes", TX); + field_to_json(Obj,"rxBytes", RX); + field_to_json(Obj,"messageCount", MessageCount); + field_to_json(Obj,"UUID", UUID); + field_to_json(Obj,"connected", Connected); + field_to_json(Obj,"firmware", Firmware); + field_to_json(Obj,"lastContact", LastContact); + switch(VerifiedCertificate) { + case NO_CERTIFICATE: + field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); break; + case VALID_CERTIFICATE: + field_to_json(Obj,"verifiedCertificate", "VALID_CERTIFICATE"); break; + case MISMATCH_SERIAL: + field_to_json(Obj,"verifiedCertificate", "MISMATCH_SERIAL"); break; + case VERIFIED: + field_to_json(Obj,"verifiedCertificate", "VERIFIED"); break; + default: + field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); break; + } + } + + void RttySessionDetails::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"serialNumber", SerialNumber); + field_to_json(Obj,"server", Server); + field_to_json(Obj,"port", Port); + field_to_json(Obj,"token",Token); + field_to_json(Obj,"timeout", TimeOut); + field_to_json(Obj,"connectionId",ConnectionId); + field_to_json(Obj,"commandUUID",CommandUUID); + field_to_json(Obj,"started", Started); + field_to_json(Obj,"viewport",ViewPort); + field_to_json(Obj,"password",DevicePassword); + } + +} + diff --git a/src/RESTAPI_GWobjects.h b/src/RESTAPI_GWobjects.h new file mode 100644 index 0000000..d26cba3 --- /dev/null +++ b/src/RESTAPI_GWobjects.h @@ -0,0 +1,161 @@ +// +// License type: BSD 3-Clause License +// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE +// +// Created by Stephane Bourque on 2021-03-04. +// Arilia Wireless Inc. +// + +#ifndef UCENTRAL_RESTAPI_OBJECTS_H +#define UCENTRAL_RESTAPI_OBJECTS_H + +#include "Poco/JSON/Object.h" +#include "RESTAPI_SecurityObjects.h" + +namespace uCentral::GWObjects { + + enum CertificateValidation { + NO_CERTIFICATE, + VALID_CERTIFICATE, + MISMATCH_SERIAL, + VERIFIED + }; + + struct ConnectionState { + uint64_t MessageCount = 0 ; + std::string SerialNumber; + std::string Address = "N/A"; + uint64_t UUID = 0 ; + uint64_t PendingUUID = 0 ; + uint64_t TX = 0, RX = 0; + bool Connected = false; + uint64_t LastContact=0; + std::string Firmware; + CertificateValidation VerifiedCertificate = NO_CERTIFICATE; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct Device { + std::string SerialNumber; + std::string DeviceType; + std::string MACAddress; + std::string Manufacturer; + std::string Configuration; + SecurityObjects::NoteInfoVec Notes; + std::string Owner; + std::string Location; + std::string Firmware; + std::string Compatible; + std::string FWUpdatePolicy; + uint64_t UUID; + uint64_t CreationTimestamp; + uint64_t LastConfigurationChange; + uint64_t LastConfigurationDownload; + uint64_t LastFWUpdate; + std::string Venue; + std::string DevicePassword; + void to_json(Poco::JSON::Object &Obj) const; + void to_json_with_status(Poco::JSON::Object &Obj) const; + bool from_json(Poco::JSON::Object::Ptr Obj); + void Print() const; + }; + + struct Statistics { + uint64_t UUID; + std::string Data; + uint64_t Recorded; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct HealthCheck { + uint64_t UUID; + std::string Data; + uint64_t Recorded; + uint64_t Sanity; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct Capabilities { + std::string Capabilities; + uint64_t FirstUpdate; + uint64_t LastUpdate; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct DeviceLog { + enum Level { + LOG_EMERG = 0, /* system is unusable */ + LOG_ALERT = 1, /* action must be taken immediately */ + LOG_CRIT = 2, /* critical conditions */ + LOG_ERR = 3, /* error conditions */ + LOG_WARNING = 4, /* warning conditions */ + LOG_NOTICE = 5, /* normal but significant condition */ + LOG_INFO = 6, /* informational */ + LOG_DEBUG = 7 /* debug-level messages */ + }; + std::string Log; + std::string Data; + uint64_t Severity; + uint64_t Recorded; + uint64_t LogType; + uint64_t UUID; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct DefaultConfiguration { + std::string Name; + std::string Configuration; + std::string Models; + std::string Description; + uint64_t Created; + uint64_t LastModified; + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(Poco::JSON::Object::Ptr Obj); + }; + + struct CommandDetails { + std::string UUID; + std::string SerialNumber; + std::string Command; + std::string Status; + std::string SubmittedBy; + std::string Results; + std::string Details; + std::string ErrorText; + uint64_t Submitted = time(nullptr); + uint64_t Executed = 0; + uint64_t Completed = 0 ; + uint64_t RunAt = 0 ; + uint64_t ErrorCode = 0 ; + uint64_t Custom = 0 ; + uint64_t WaitingForFile = 0 ; + uint64_t AttachDate = 0 ; + uint64_t AttachSize = 0 ; + std::string AttachType; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct BlackListedDevice { + std::string SerialNumber; + std::string Reason; + std::string Author; + uint64_t Created; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct RttySessionDetails { + std::string SerialNumber; + std::string Server; + uint64_t Port; + std::string Token; + uint64_t TimeOut; + std::string ConnectionId; + uint64_t Started; + std::string CommandUUID; + uint64_t ViewPort; + std::string DevicePassword; + void to_json(Poco::JSON::Object &Obj) const; + }; +} + +#endif //UCENTRAL_RESTAPI_OBJECTS_H diff --git a/src/RESTAPI_InternalServer.cpp b/src/RESTAPI_InternalServer.cpp index bb5b1e1..730f105 100644 --- a/src/RESTAPI_InternalServer.cpp +++ b/src/RESTAPI_InternalServer.cpp @@ -6,13 +6,8 @@ #include "Poco/URI.h" -#include "RESTAPI_callbackHandler.h" -#include "RESTAPI_callbacksHandler.h" #include "RESTAPI_firmwareHandler.h" #include "RESTAPI_firmwaresHandler.h" -#include "RESTAPI_latestFirmwareListHandler.h" -#include "RESTAPI_callbackChannel.h" -#include "RESTAPI_newFirmwareAvailable.h" #include "RESTAPI_system_command.h" #include "Utils.h" @@ -69,11 +64,6 @@ namespace uCentral { return RESTAPI_Router< RESTAPI_firmwaresHandler, RESTAPI_firmwareHandler, - RESTAPI_callbacksHandler, - RESTAPI_callbackHandler, - RESTAPI_latestFirmwareListHandler, - RESTAPI_callbackChannel, - RESTAPI_newFirmwareAvailable, RESTAPI_system_command >(Path, Bindings, Logger_); } diff --git a/src/RESTAPI_callbackChannel.cpp b/src/RESTAPI_callbackChannel.cpp deleted file mode 100644 index ccf9bc3..0000000 --- a/src/RESTAPI_callbackChannel.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// -// Created by stephane bourque on 2021-05-11. -// - -#include "RESTAPI_callbackChannel.h" -#include "StorageService.h" -#include "Daemon.h" - -#include "Poco/JSON/Parser.h" - -namespace uCentral { - void RESTAPI_callbackChannel::handleRequest(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) { - - ParseParameters(Request); - if (Request.getMethod() == Poco::Net::HTTPRequest::HTTP_POST) - DoPost(Request, Response); - else - BadRequest(Request, Response); - } - - void - RESTAPI_callbackChannel::DoPost(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { - try { - if (!ValidateAPIKey(Request, Response)) { - UnAuthorized(Request, Response); - return; - } - - auto Subscribe = GetParameter("subscribe", ""); - if (Subscribe == "true") { - // subscribing - // we must have uri, msgs, id, key - auto URI = GetParameter("uri", ""); - auto Topics = GetParameter("topics", ""); - auto ID = GetParameter("id", ""); - auto Key = GetParameter("key", ""); - - if (URI.empty() || Topics.empty() || ID.empty() || Key.empty()) { - BadRequest(Request, Response); - return; - } - - uCentral::Objects::Callback C; - - C.UUID = ID; - C.TokenType = "X-API-KEY"; - C.Token = Key; - C.URI = URI; - C.Created = time(nullptr); - C.Creator = UserInfo_.userinfo.name; - C.Topics = Topics; - - if (uCentral::Storage()->AddOrUpdateCallback(C)) { - Logger_.information(Poco::format("CALLBACK(%s): Just subscribed.", UserInfo_.userinfo.name)); - OK(Request, Response); - return; - } else { - Logger_.error(Poco::format("CALLBACK(%s): Could not register.", UserInfo_.userinfo.name)); - BadRequest(Request, Response); - return; - } - } else if (Subscribe == "false") { - // removing a subscription - auto ID = GetParameter("id", ""); - if (ID.empty()) { - BadRequest(Request, Response); - return; - } - if (uCentral::Storage()->DeleteCallback(ID)) { - OK(Request, Response); - return; - } - Logger_.error(Poco::format("CALLBACK(%s): Cannot remove subscription.", UserInfo_.userinfo.name)); - NotFound(Request, Response); - return; - } else { - // not a subscription message - auto Topic = GetParameter("topic", ""); - if (Topic.empty()) { - BadRequest(Request, Response); - return; - } - Poco::JSON::Parser parser; - Poco::JSON::Object::Ptr Obj = parser.parse(Request.stream()).extract(); - OK(Request, Response); - return; - } - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - BadRequest(Request, Response); - } - -} \ No newline at end of file diff --git a/src/RESTAPI_callbackChannel.h b/src/RESTAPI_callbackChannel.h deleted file mode 100644 index 58631f7..0000000 --- a/src/RESTAPI_callbackChannel.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by stephane bourque on 2021-05-11. -// - -#ifndef UCENTRALFWS_RESTAPI_CALLBACKCHANNEL_H -#define UCENTRALFWS_RESTAPI_CALLBACKCHANNEL_H - -#include "RESTAPI_handler.h" - -namespace uCentral { - class RESTAPI_callbackChannel : public RESTAPIHandler { - public: - RESTAPI_callbackChannel(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L) - : RESTAPIHandler(bindings, L, - std::vector - {Poco::Net::HTTPRequest::HTTP_POST, - Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} - static const std::list PathName() { return std::list{"/api/v1/callbackChannel"};} - void handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response) override; - void DoPost(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response); - }; -} - -#endif //UCENTRALFWS_RESTAPI_CALLBACKCHANNEL_H diff --git a/src/RESTAPI_callbackHandler.cpp b/src/RESTAPI_callbackHandler.cpp deleted file mode 100644 index db502ac..0000000 --- a/src/RESTAPI_callbackHandler.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// -// Created by stephane bourque on 2021-05-09. -// - -#include "RESTAPI_callbackHandler.h" -#include "StorageService.h" -#include "Daemon.h" - -#include "Poco/JSON/Parser.h" - -namespace uCentral { - void RESTAPI_callbackHandler::handleRequest(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) { - if (!ContinueProcessing(Request, Response)) - return; - - if (!IsAuthorized(Request, Response)) - return; - - ParseParameters(Request); - if (Request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET) - DoGet(Request, Response); - else if (Request.getMethod() == Poco::Net::HTTPRequest::HTTP_POST) - DoPost(Request, Response); - else if (Request.getMethod() == Poco::Net::HTTPRequest::HTTP_PUT) - DoPut(Request, Response); - else if (Request.getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE) - DoDelete(Request, Response); - else - BadRequest(Request, Response); - } - - void - RESTAPI_callbackHandler::DoPost(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { - try { - Poco::JSON::Parser parser; - Poco::JSON::Object::Ptr Obj = parser.parse(Request.stream()).extract(); - - uCentral::Objects::Callback C; - - if (C.from_json(Obj)) { - C.UUID = Daemon()->CreateUUID(); - - } - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - BadRequest(Request, Response); - } - - void - RESTAPI_callbackHandler::DoPut(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { - try { - - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - BadRequest(Request, Response); - } - - void - RESTAPI_callbackHandler::DoGet(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { - try { - auto UUID = GetBinding("uuid", ""); - - if (!UUID.empty()) { - uCentral::Objects::Callback C; - if (uCentral::Storage()->GetCallback(UUID, C)) { - Poco::JSON::Object Object; - - C.to_json(Object); - ReturnObject(Request, Object, Response); - } else { - NotFound(Request, Response); - - } - return; - } - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - BadRequest(Request, Response); - } - - void - RESTAPI_callbackHandler::DoDelete(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { - try { - auto UUID = GetBinding("uuid", ""); - - if (!UUID.empty()) { - if (uCentral::Storage()->DeleteCallback(UUID)) { - OK(Request, Response); - } else { - NotFound(Request, Response); - } - return; - } - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - BadRequest(Request, Response); - } -} \ No newline at end of file diff --git a/src/RESTAPI_callbackHandler.h b/src/RESTAPI_callbackHandler.h deleted file mode 100644 index c6096d4..0000000 --- a/src/RESTAPI_callbackHandler.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// Created by stephane bourque on 2021-05-09. -// - -#ifndef UCENTRALFWS_RESTAPI_CALLBACKHANDLER_H -#define UCENTRALFWS_RESTAPI_CALLBACKHANDLER_H - -#include "RESTAPI_handler.h" - -namespace uCentral { - class RESTAPI_callbackHandler : public RESTAPIHandler { - public: - RESTAPI_callbackHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L) - : RESTAPIHandler(bindings, L, - std::vector - {Poco::Net::HTTPRequest::HTTP_GET, - Poco::Net::HTTPRequest::HTTP_POST, - Poco::Net::HTTPRequest::HTTP_PUT, - Poco::Net::HTTPRequest::HTTP_DELETE, - Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} - static const std::list PathName() { return std::list{"/api/v1/callback/{uuid}"};} - void handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response) override; - void DoPost(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response); - void DoGet(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response); - void DoDelete(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response); - void DoPut(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response); - }; -} - -#endif //UCENTRALFWS_RESTAPI_CALLBACKHANDLER_H diff --git a/src/RESTAPI_callbacksHandler.cpp b/src/RESTAPI_callbacksHandler.cpp deleted file mode 100644 index 91f3624..0000000 --- a/src/RESTAPI_callbacksHandler.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// -// Created by stephane bourque on 2021-05-09. -// - -#include "RESTAPI_callbacksHandler.h" -#include "StorageService.h" - -namespace uCentral { - void RESTAPI_callbacksHandler::handleRequest(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) { - if (!ContinueProcessing(Request, Response)) - return; - - if (!IsAuthorized(Request, Response)) - return; - - ParseParameters(Request); - if (Request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET) - DoGet(Request, Response); - else - BadRequest(Request, Response); - - } - - void RESTAPI_callbacksHandler::DoGet(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { - try { - auto Offset = GetParameter("offset", 0); - auto Limit = GetParameter("limit", 100); - - std::vector List; - if (uCentral::Storage()->GetCallbacks(Offset, Limit, List)) { - - Poco::JSON::Array ObjectArray; - - for (const auto &i:List) { - Poco::JSON::Object Obj; - i.to_json(Obj); - ObjectArray.add(Obj); - } - Poco::JSON::Object RetObj; - RetObj.set("callbacks", ObjectArray); - ReturnObject(Request, RetObj, Response); - return; - } - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - BadRequest(Request, Response); - } -} diff --git a/src/RESTAPI_callbacksHandler.h b/src/RESTAPI_callbacksHandler.h deleted file mode 100644 index 6166756..0000000 --- a/src/RESTAPI_callbacksHandler.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by stephane bourque on 2021-05-09. -// - -#ifndef UCENTRALFWS_RESTAPI_CALLBACKSHANDLER_H -#define UCENTRALFWS_RESTAPI_CALLBACKSHANDLER_H - -#include "RESTAPI_handler.h" - -namespace uCentral { - class RESTAPI_callbacksHandler : public RESTAPIHandler { - public: - RESTAPI_callbacksHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L) - : RESTAPIHandler(bindings, L, - std::vector - {Poco::Net::HTTPRequest::HTTP_GET, - Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} - static const std::list PathName() { return std::list{"/api/v1/callbacks"};} - void handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response) override; - void DoGet(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response); - }; -} -#endif //UCENTRALFWS_RESTAPI_CALLBACKSHANDLER_H diff --git a/src/RESTAPI_firmwareHandler.cpp b/src/RESTAPI_firmwareHandler.cpp index 5745f98..a823786 100644 --- a/src/RESTAPI_firmwareHandler.cpp +++ b/src/RESTAPI_firmwareHandler.cpp @@ -42,8 +42,8 @@ namespace uCentral { auto UUID = GetBinding("uuid", ""); if (!UUID.empty()) { - uCentral::Objects::Firmware F; - if (uCentral::Storage()->GetFirmware(UUID, F)) { + FMSObjects::Firmware F; + if (Storage()->GetFirmware(UUID, F)) { Poco::JSON::Object Object; F.to_json(Object); ReturnObject(Request, Object, Response); @@ -64,7 +64,7 @@ namespace uCentral { auto UUID = GetBinding("uuid", ""); if (!UUID.empty()) { - if (uCentral::Storage()->DeleteFirmware(UUID)) { + if (Storage()->DeleteFirmware(UUID)) { OK(Request, Response); } else { NotFound(Request, Response); diff --git a/src/RESTAPI_firmwaresHandler.cpp b/src/RESTAPI_firmwaresHandler.cpp index 246ee82..8a2f47e 100644 --- a/src/RESTAPI_firmwaresHandler.cpp +++ b/src/RESTAPI_firmwaresHandler.cpp @@ -27,7 +27,7 @@ namespace uCentral { auto Offset = GetParameter("offset", 0); auto Limit = GetParameter("limit", 100); - std::vector List; + std::vector List; if (uCentral::Storage()->GetFirmwares(Offset, Limit, List)) { Poco::JSON::Array ObjectArray; diff --git a/src/RESTAPI_handler.cpp b/src/RESTAPI_handler.cpp index f697e4e..5f29f59 100644 --- a/src/RESTAPI_handler.cpp +++ b/src/RESTAPI_handler.cpp @@ -194,12 +194,13 @@ namespace uCentral { } void RESTAPIHandler::UnAuthorized(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) { + Poco::Net::HTTPServerResponse &Response, + const std::string & Reason) { PrepareResponse(Request, Response, Poco::Net::HTTPResponse::HTTP_FORBIDDEN); Poco::JSON::Object ErrorObject; ErrorObject.set("ErrorCode",403); ErrorObject.set("ErrorDetails",Request.getMethod()); - ErrorObject.set("ErrorDescription","You do not have access to this resource."); + ErrorObject.set("ErrorDescription",Reason.empty() ? "No access allowed." : Reason) ; std::ostream &Answer = Response.send(); Poco::JSON::Stringifier::stringify(ErrorObject, Answer); } @@ -244,7 +245,37 @@ namespace uCentral { Response.sendFile(File.path(),"application/octet-stream"); } - void RESTAPIHandler::ReturnStatus(Poco::Net::HTTPServerRequest &Request, + void RESTAPIHandler::SendFile(Poco::File & File, Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { + Response.set("Content-Type",Utils::FindMediaType(File)); + Poco::Path P(File.path()); + Response.set("Content-Disposition", "attachment; filename=" + P.getBaseName() ); + Response.set("Content-Transfer-Encoding","binary"); + Response.set("Accept-Ranges", "bytes"); + Response.set("Cache-Control", "private"); + Response.set("Pragma", "private"); + Response.set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + Response.set("Content-Length", std::to_string(File.getSize())); + AddCORS(Request, Response); + Response.sendFile(File.path(),Utils::FindMediaType(File)); + } + + void RESTAPIHandler::SendHTMLFileBack(Poco::File & File, + Poco::Net::HTTPServerRequest &Request, + Poco::Net::HTTPServerResponse &Response , + const Types::StringPairVec & FormVars) { + Response.set("Pragma", "private"); + Response.set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + Response.set("Content-Length", std::to_string(File.getSize())); + AddCORS(Request, Response); + auto FormContent = Utils::LoadFile(File.path()); + Utils::ReplaceVariables(FormContent, FormVars); + Response.setChunkedTransferEncoding(true); + Response.setContentType("text/html"); + std::ostream& ostr = Response.send(); + ostr << FormContent; + } + + void RESTAPIHandler::ReturnStatus(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response, Poco::Net::HTTPResponse::HTTPStatus Status, bool CloseConnection) { diff --git a/src/RESTAPI_handler.h b/src/RESTAPI_handler.h index a85d5f0..e489fde 100644 --- a/src/RESTAPI_handler.h +++ b/src/RESTAPI_handler.h @@ -15,15 +15,72 @@ #include "Poco/Net/HTTPServerRequest.h" #include "Poco/Net/HTTPServerResponse.h" #include "Poco/Net/NetException.h" +#include "Poco/Net/PartHandler.h" + #include "Poco/Logger.h" #include "Poco/File.h" #include "Poco/JSON/Object.h" +#include "Poco/CountingStream.h" +#include "Poco/NullStream.h" #include "RESTAPI_SecurityObjects.h" namespace uCentral { - class RESTAPIHandler : public Poco::Net::HTTPRequestHandler { + class RESTAPI_PartHandler: public Poco::Net::PartHandler + { + public: + RESTAPI_PartHandler(): + _length(0) + { + } + + void handlePart(const Poco::Net::MessageHeader& header, std::istream& stream) override + { + _type = header.get("Content-Type", "(unspecified)"); + if (header.has("Content-Disposition")) + { + std::string disp; + Poco::Net::NameValueCollection params; + Poco::Net::MessageHeader::splitParameters(header["Content-Disposition"], disp, params); + _name = params.get("name", "(unnamed)"); + _fileName = params.get("filename", "(unnamed)"); + } + + Poco::CountingInputStream istr(stream); + Poco::NullOutputStream ostr; + Poco::StreamCopier::copyStream(istr, ostr); + _length = (int)istr.chars(); + } + + [[nodiscard]] int length() const + { + return _length; + } + + [[nodiscard]] const std::string& name() const + { + return _name; + } + + [[nodiscard]] const std::string& fileName() const + { + return _fileName; + } + + [[nodiscard]] const std::string& contentType() const + { + return _type; + } + + private: + int _length; + std::string _type; + std::string _name; + std::string _fileName; + }; + + class RESTAPIHandler : public Poco::Net::HTTPRequestHandler { public: struct QueryBlock { uint64_t StartDate = 0 , EndDate = 0 , Offset = 0 , Limit = 0, LogType = 0 ; @@ -64,7 +121,7 @@ namespace uCentral { void BadRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response, const std::string &Reason = ""); void UnAuthorized(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response); + Poco::Net::HTTPServerResponse &Response, const std::string &Reason = ""); void ReturnObject(Poco::Net::HTTPServerRequest &Request, Poco::JSON::Object &Object, Poco::Net::HTTPServerResponse &Response); void NotFound(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response); @@ -75,8 +132,14 @@ namespace uCentral { bool CloseConnection=false); void SendFile(Poco::File & File, const std::string & UUID, Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response); + void SendHTMLFileBack(Poco::File & File, + Poco::Net::HTTPServerRequest &Request, + Poco::Net::HTTPServerResponse &Response , + const Types::StringPairVec & FormVars); - const std::string &GetBinding(const std::string &Name, const std::string &Default); + void SendFile(Poco::File & File, Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response); + + const std::string &GetBinding(const std::string &Name, const std::string &Default); void InitQueryBlock(); [[nodiscard]] static uint64_t Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default=0); diff --git a/src/RESTAPI_latestFirmwareListHandler.cpp b/src/RESTAPI_latestFirmwareListHandler.cpp deleted file mode 100644 index 4e416d6..0000000 --- a/src/RESTAPI_latestFirmwareListHandler.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// -// Created by stephane bourque on 2021-05-09. -// - -#include "RESTAPI_latestFirmwareListHandler.h" -#include "StorageService.h" - -namespace uCentral { - void RESTAPI_latestFirmwareListHandler::handleRequest(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) { - if (!ContinueProcessing(Request, Response)) - return; - - if (!IsAuthorized(Request, Response)) - return; - - ParseParameters(Request); - - if (Request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET) - DoGet(Request, Response); - else - BadRequest(Request, Response); - } - - void RESTAPI_latestFirmwareListHandler::DoGet(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) { - - try { - auto Offset = GetParameter("offset", 0); - auto Limit = GetParameter("limit", 100); - - std::vector List; - if (uCentral::Storage()->GetLatestFirmwareList(Offset, Limit, List)) { - - Poco::JSON::Array ObjectArray; - - for (const auto &i:List) { - Poco::JSON::Object Obj; - i.to_json(Obj); - ObjectArray.add(Obj); - } - Poco::JSON::Object RetObj; - RetObj.set("latestFirmwareList", ObjectArray); - ReturnObject(Request, RetObj, Response); - return; - } - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - BadRequest(Request, Response); - } -} - diff --git a/src/RESTAPI_latestFirmwareListHandler.h b/src/RESTAPI_latestFirmwareListHandler.h deleted file mode 100644 index 1f44ef9..0000000 --- a/src/RESTAPI_latestFirmwareListHandler.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Created by stephane bourque on 2021-05-09. -// - -#ifndef UCENTRALFWS_RESTAPI_LATESTFIRMWARELISTHANDLER_H -#define UCENTRALFWS_RESTAPI_LATESTFIRMWARELISTHANDLER_H - -#include "RESTAPI_handler.h" - -namespace uCentral { - class RESTAPI_latestFirmwareListHandler : public RESTAPIHandler { - public: - RESTAPI_latestFirmwareListHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L) - : RESTAPIHandler(bindings, L, - std::vector - {Poco::Net::HTTPRequest::HTTP_GET, - Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} - - static const std::list PathName() { return std::list{"/api/v1/latestFirmwareList"};} - void handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response) override; - void DoGet(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response); - }; -} - -#endif //UCENTRALFWS_RESTAPI_LATESTFIRMWARELISTHANDLER_H diff --git a/src/RESTAPI_newFirmwareAvailable.cpp b/src/RESTAPI_newFirmwareAvailable.cpp deleted file mode 100644 index 27f243e..0000000 --- a/src/RESTAPI_newFirmwareAvailable.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// -// Created by stephane bourque on 2021-06-02. -// - -#include "RESTAPI_newFirmwareAvailable.h" -#include "ManifestCreator.h" - -namespace uCentral { - void RESTAPI_newFirmwareAvailable::handleRequest(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) { - - if (!ContinueProcessing(Request, Response)) - return; - - if (!ValidateAPIKey(Request, Response)) { - UnAuthorized(Request, Response); - return; - } - - ParseParameters(Request); - if (Request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET) - DoGet(Request, Response); - else - BadRequest(Request, Response); - } - - void RESTAPI_newFirmwareAvailable::DoGet(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) { - try { - - auto Op = GetParameter("operation", ""); - - if (Op != "notify") { - BadRequest(Request, Response); - return; - } - - uCentral::ManifestCreator()->Update(); - - Poco::JSON::Object O; - O.set("status", "updating manifest"); - ReturnObject(Request, O, Response); - return; - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - BadRequest(Request, Response); - } -} \ No newline at end of file diff --git a/src/RESTAPI_newFirmwareAvailable.h b/src/RESTAPI_newFirmwareAvailable.h deleted file mode 100644 index 941e63d..0000000 --- a/src/RESTAPI_newFirmwareAvailable.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by stephane bourque on 2021-06-02. -// - -#ifndef UCENTRALFWS_RESTAPI_NEWFIRMWAREAVAILABLE_H -#define UCENTRALFWS_RESTAPI_NEWFIRMWAREAVAILABLE_H - -#include "RESTAPI_handler.h" - -namespace uCentral { - class RESTAPI_newFirmwareAvailable : public RESTAPIHandler { - public: - RESTAPI_newFirmwareAvailable(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L) - : RESTAPIHandler(bindings, L, - std::vector - {Poco::Net::HTTPRequest::HTTP_GET, - Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} - static const std::list PathName() { return std::list{"/api/v1/newFirmwareAvailable"};} - void handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response) override; - void DoGet(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response); - }; -} - -#endif //UCENTRALFWS_RESTAPI_NEWFIRMWAREAVAILABLE_H diff --git a/src/RESTAPI_objects.cpp b/src/RESTAPI_objects.cpp deleted file mode 100644 index 6fd6eb9..0000000 --- a/src/RESTAPI_objects.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// -// Created by stephane bourque on 2021-05-07. -// - -#include "RESTAPI_objects.h" -#include "RESTAPI_handler.h" -#include "Poco/JSON/Parser.h" - -namespace uCentral::Objects { - - void EmbedDocument(const std::string & ObjName, Poco::JSON::Object & Obj, const std::string &ObjStr) { - std::string D = ObjStr.empty() ? "{}" : ObjStr; - Poco::JSON::Parser P; - Poco::Dynamic::Var result = P.parse(D); - const auto &DetailsObj = result.extract(); - Obj.set(ObjName, DetailsObj); - } - - void Firmware::to_json(Poco::JSON::Object &Obj) const { - Obj.set("uuid",UUID); - Obj.set("description", Description); - Obj.set("uploaded",Uploaded); - Obj.set("firmwareDate",FirmwareDate); - Obj.set("firmwareVersion", FirmwareVersion); - Obj.set("firmwareHash", FirmwareHash); - Obj.set("owner", Owner); - Obj.set("location", Location); - Obj.set("downloadCount", DownloadCount); - Obj.set("uploader", Uploader); - Obj.set("size", Size); - Obj.set("digest", Digest); - Obj.set("s3uri", S3URI); - Obj.set("Compatible", Compatible); - Obj.set("latest", (bool) (Latest ? true : false) ); - EmbedDocument("firmwareLatestDoc",Obj,FirmwareLatestDoc); - } - - void Callback::to_json(Poco::JSON::Object &Obj) const { - Obj.set("uri", URI); - Obj.set("uuid",UUID); - Obj.set("location",Location); - Obj.set("creator", Creator); - Obj.set("token", Token); - Obj.set("tokenType", TokenType); - Obj.set("topics",Topics); - Obj.set("created", Created); - Obj.set("expires", Expires); - } - - bool Callback::from_json(Poco::JSON::Object::Ptr Obj) { - Poco::DynamicStruct ds = *Obj; - - if(ds.contains("uri")) - URI = ds["uri"].toString(); - if(ds.contains("uuid")) - UUID = ds["uuid"].toString(); - if(ds.contains("location")) - Location = ds["location"].toString(); - if(ds.contains("token")) - Token = ds["token"].toString(); - if(ds.contains("tokenType")) - TokenType = ds["tokenType"].toString(); - - return true; - } - - void LatestFirmware::to_json(Poco::JSON::Object &Obj) const { - Obj.set("Compatible", Compatible); - Obj.set("uuid", UUID); - Obj.set("lastUpdated", LastUpdated); - } - - void AclTemplate::to_json(Poco::JSON::Object &Obj) const { - Obj.set("Read",Read_); - Obj.set("ReadWrite",ReadWrite_); - Obj.set("ReadWriteCreate",ReadWriteCreate_); - Obj.set("Delete",Delete_); - Obj.set("PortalLogin",PortalLogin_); - } - - void WebToken::to_json(Poco::JSON::Object & Obj) const { - Poco::JSON::Object AclTemplateObj; - acl_template_.to_json(AclTemplateObj); - Obj.set("access_token",access_token_); - Obj.set("refresh_token",refresh_token_); - Obj.set("token_type",token_type_); - Obj.set("expires_in",expires_in_); - Obj.set("idle_timeout",idle_timeout_); - Obj.set("created", created_); - Obj.set("username",username_); - Obj.set("aclTemplate",AclTemplateObj); - } - -} diff --git a/src/RESTAPI_objects.h b/src/RESTAPI_objects.h deleted file mode 100644 index 3e7de6d..0000000 --- a/src/RESTAPI_objects.h +++ /dev/null @@ -1,78 +0,0 @@ -// -// Created by stephane bourque on 2021-05-07. -// - -#ifndef UCENTRALFWS_RESTAPI_OBJECTS_H -#define UCENTRALFWS_RESTAPI_OBJECTS_H - -#include "Poco/JSON/Object.h" - -namespace uCentral::Objects { - - struct AclTemplate { - bool Read_ = true ; - bool ReadWrite_ = true ; - bool ReadWriteCreate_ = true ; - bool Delete_ = true ; - bool PortalLogin_ = true ; - void to_json(Poco::JSON::Object &Obj) const ; - }; - - struct WebToken { - std::string access_token_; - std::string refresh_token_; - std::string id_token_; - std::string token_type_; - std::string username_; - unsigned int expires_in_; - unsigned int idle_timeout_; - AclTemplate acl_template_; - uint64_t created_; - void to_json(Poco::JSON::Object &Obj) const ; - }; - - struct Firmware { - std::string UUID; - std::string Description; - std::string Owner; - std::string Location; - std::string Compatible; - std::string Uploader; - std::string Digest; - std::string FirmwareFileName; - std::string FirmwareVersion; - std::string FirmwareHash; - std::string FirmwareLatestDoc; - std::string S3URI; - uint64_t FirmwareDate; - uint64_t Uploaded; - uint64_t DownloadCount; - uint64_t Size; - uint64_t Latest; - void to_json(Poco::JSON::Object &Obj) const; - }; - - struct Callback { - std::string UUID; - std::string URI; - std::string Location; - std::string Token; - std::string TokenType; - std::string Creator; - std::string Topics; - uint64_t Created; - uint64_t Expires; - void to_json(Poco::JSON::Object &Obj) const; - bool from_json(Poco::JSON::Object::Ptr Obj); - }; - - struct LatestFirmware { - std::string Compatible; - std::string UUID; - uint64_t LastUpdated; - void to_json(Poco::JSON::Object &Obj) const; - }; - -} - -#endif //UCENTRALFWS_RESTAPI_OBJECTS_H diff --git a/src/RESTAPI_server.cpp b/src/RESTAPI_server.cpp index 92ba918..2c75d40 100644 --- a/src/RESTAPI_server.cpp +++ b/src/RESTAPI_server.cpp @@ -8,13 +8,8 @@ #include "Utils.h" #include "RESTAPI_handler.h" -#include "RESTAPI_callbackHandler.h" -#include "RESTAPI_callbacksHandler.h" #include "RESTAPI_firmwareHandler.h" #include "RESTAPI_firmwaresHandler.h" -#include "RESTAPI_latestFirmwareListHandler.h" -#include "RESTAPI_callbackChannel.h" -#include "RESTAPI_newFirmwareAvailable.h" #include "RESTAPI_system_command.h" namespace uCentral { @@ -67,11 +62,6 @@ namespace uCentral { return RESTAPI_Router< RESTAPI_firmwaresHandler, RESTAPI_firmwareHandler, - RESTAPI_callbacksHandler, - RESTAPI_callbackHandler, - RESTAPI_latestFirmwareListHandler, - RESTAPI_callbackChannel, - RESTAPI_newFirmwareAvailable, RESTAPI_system_command >(Path,Bindings,Logger_); } diff --git a/src/RESTAPI_utils.h b/src/RESTAPI_utils.h index 752a04f..b691a8c 100644 --- a/src/RESTAPI_utils.h +++ b/src/RESTAPI_utils.h @@ -8,8 +8,6 @@ #include "Poco/JSON/Object.h" #include "Poco/JSON/Parser.h" -#include "Poco/Net/HTTPServerRequest.h" - #include "uCentralTypes.h" #include "Utils.h" @@ -193,13 +191,6 @@ namespace uCentral::RESTAPI_utils { return Result; } - - template bool from_request(T & Obj, Poco::Net::HTTPServerRequest &Request) { - Poco::JSON::Parser IncomingParser; - auto RawObject = IncomingParser.parse(Request.stream()).extract(); - Obj.from_json(RawObject); - return true; - } } #endif // UCENTRALGW_RESTAPI_UTILS_H diff --git a/src/StorageService.cpp b/src/StorageService.cpp index 6650ec5..ff6f131 100644 --- a/src/StorageService.cpp +++ b/src/StorageService.cpp @@ -9,6 +9,9 @@ #include #include "StorageService.h" #include "Poco/Util/Application.h" + +#include "Daemon.h" + #include "Utils.h" namespace uCentral { @@ -43,12 +46,25 @@ namespace uCentral { } int Storage::Start() { - SubMutexGuard Guard(Mutex_); - Logger_.setLevel(Poco::Message::PRIO_NOTICE); + SubMutexGuard Guard(Mutex_); + + Logger_.setLevel(Poco::Message::PRIO_NOTICE); Logger_.notice("Starting."); - Setup_SQLite(); - Create_Tables(); - return 0; + std::string DBType = Daemon()->ConfigGetString("storage.type"); + + if (DBType == "sqlite") { + Setup_SQLite(); + } else if (DBType == "postgresql") { + Setup_PostgreSQL(); + } else if (DBType == "mysql") { + Setup_MySQL(); + } else if (DBType == "odbc") { + Setup_ODBC(); + } + + Create_Tables(); + + return 0; } void Storage::Stop() { diff --git a/src/StorageService.h b/src/StorageService.h index d6f68d3..2df363c 100644 --- a/src/StorageService.h +++ b/src/StorageService.h @@ -12,42 +12,49 @@ #include "Poco/Data/Session.h" #include "Poco/Data/SessionPool.h" #include "Poco/Data/SQLite/Connector.h" +#include "Poco/JSON/Object.h" -#include "RESTAPI_objects.h" +#include "RESTAPI_FMSObjects.h" #include "SubSystemServer.h" +#include "storage_firmwares.h" +#include "storage_history.h" +#include "storage_deviceTypes.h" + +#ifndef SMALL_BUILD +#include "Poco/Data/PostgreSQL/Connector.h" +#include "Poco/Data/MySQL/Connector.h" +#include "Poco/Data/ODBC/Connector.h" +#endif + namespace uCentral { class Storage : public SubSystemServer { - public: + + enum StorageType { + sqlite, + pgsql, + mysql, + odbc + }; + Storage() noexcept; int Create_Tables(); int Create_Firmwares(); - int Create_Callbacks(); + int Create_History(); + int Create_DeviceTypes(); int Create_LatestFirmwareList(); - bool AddCallback(uCentral::Objects::Callback & C); - bool AddOrUpdateCallback(uCentral::Objects::Callback & C); - bool UpdateCallback(std::string & UUID, uCentral::Objects::Callback & C); - bool DeleteCallback(std::string & UUID); - bool GetCallback(std::string & UUID, uCentral::Objects::Callback & C); - bool GetCallbacks(uint64_t From, uint64_t HowMany, std::vector & Callbacks); - - bool AddFirmware(uCentral::Objects::Firmware & F); - bool UpdateFirmware(std::string & UUID, uCentral::Objects::Firmware & C); + bool AddFirmware(FMSObjects::Firmware & F); + bool UpdateFirmware(std::string & UUID, FMSObjects::Firmware & C); bool DeleteFirmware(std::string & UUID); - bool GetFirmware(std::string & UUID, uCentral::Objects::Firmware & C); - bool GetFirmwares(uint64_t From, uint64_t HowMany, std::vector & Firmwares); + bool GetFirmware(std::string & UUID, FMSObjects::Firmware & C); + bool GetFirmwares(uint64_t From, uint64_t HowMany, std::vector & Firmwares); bool BuildFirmwareManifest(Poco::JSON::Object & Manifest, uint64_t & Version); uint64_t FirmwareVersion(); - bool AddLatestFirmware(std::string & Compatible, std::string &UUID); - bool GetLatestFirmware(std::string & Compatible, uCentral::Objects::LatestFirmware &L); - bool DeleteLatestFirmware(std::string & Compatible); - bool GetLatestFirmwareList(uint64_t From, uint64_t HowMany, std::vector & LatestFirmwareList); - int Start() override; void Stop() override; int Setup_SQLite(); @@ -60,11 +67,23 @@ namespace uCentral { return instance_; } +#ifndef SMALL_BUILD + int Setup_MySQL(); + int Setup_PostgreSQL(); + int Setup_ODBC(); +#endif + private: - static Storage *instance_; - std::unique_ptr Pool_= nullptr; - std::unique_ptr SQLiteConn_= nullptr; - std::atomic_int64_t FirmwareVersion_ = 1 ; + static Storage *instance_; + std::unique_ptr Pool_= nullptr; + StorageType dbType_ = sqlite; + std::unique_ptr SQLiteConn_= nullptr; +#ifndef SMALL_BUILD + std::unique_ptr PostgresConn_= nullptr; + std::unique_ptr MySQLConn_= nullptr; + std::unique_ptr ODBCConn_= nullptr; +#endif + }; inline Storage * Storage() { return Storage::instance(); }; diff --git a/src/SubSystemServer.cpp b/src/SubSystemServer.cpp index 1c47522..dfee0d4 100644 --- a/src/SubSystemServer.cpp +++ b/src/SubSystemServer.cpp @@ -114,7 +114,7 @@ Poco::Net::SecureServerSocket PropertiesFileServerEntry::CreateSecureSocket(Poco P.dhUse2048Bits = true; P.caLocation = cas_; - auto Context = new Poco::Net::Context(Poco::Net::Context::TLS_SERVER_USE, P); + auto Context = Poco::AutoPtr(new Poco::Net::Context(Poco::Net::Context::TLS_SERVER_USE, P)); if(!key_file_password_.empty()) { auto PassphraseHandler = Poco::SharedPtr( new MyPrivateKeyPassphraseHandler(KeyFilePassword(),L)); diff --git a/src/Utils.cpp b/src/Utils.cpp index 3033c59..87f6b87 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "Utils.h" @@ -17,9 +18,10 @@ #include "Poco/DateTime.h" #include "Poco/DateTimeParser.h" #include "Poco/StringTokenizer.h" -#include "Poco/Logger.h" #include "Poco/Message.h" #include "Poco/File.h" +#include "Poco/StreamCopier.h" +#include "Poco/Path.h" #include "uCentralProtocol.h" #include "Daemon.h" @@ -390,4 +392,80 @@ namespace uCentral::Utils { } } + bool ValidEMailAddress(const std::string &email) { + // define a regular expression + const std::regex pattern + ("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+"); + + // try to match the string with the regular expression + return std::regex_match(email, pattern); + } + + std::string LoadFile( const Poco::File & F) { + std::string Result; + try { + std::ostringstream OS; + std::ifstream IF(F.path()); + Poco::StreamCopier::copyStream(IF, OS); + Result = OS.str(); + } catch (...) { + + } + return Result; + } + + void ReplaceVariables( std::string & Content , const Types::StringPairVec & P) { + for(const auto &[Variable,Value]:P) { + Poco::replaceInPlace(Content,"${" + Variable + "}", Value); + } + } + + std::string FindMediaType(const Poco::File &F) { + Poco::Path P(F.path()); + const auto E = P.getExtension(); + if(E=="png") + return "image/png"; + if(E=="gif") + return "image/gif"; + if(E=="jpeg") + return "image/jpeg"; + if(E=="jpg") + return "image/jpeg"; + if(E=="svg") + return "image/svg"; + if(E=="html") + return "text/html"; + if(E=="css") + return "text/css"; + if(E=="js") + return "application/javascript"; + return "application/octet-stream"; + } + + std::string BinaryFileToHexString(const Poco::File &F) { + static const char hex[] = "0123456789abcdef"; + std::string Result; + try { + std::ifstream IF(F.path()); + + int Count = 0; + while (IF.good()) { + if (Count) + Result += ", "; + if ((Count % 32) == 0) + Result += "\r\n"; + Count++; + unsigned char C = IF.get(); + Result += "0x"; + Result += (char) (hex[(C & 0xf0) >> 4]); + Result += (char) (hex[(C & 0x0f)]); + } + } catch(...) { + + } + return Result; + } + + + } diff --git a/src/Utils.h b/src/Utils.h index edb2f26..1d05468 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -13,6 +13,9 @@ #include #include "Poco/Net/NetworkInterface.h" +#include "Poco/String.h" +#include "Poco/File.h" +#include "uCentralTypes.h" #define DBGLINE { std::cout << __FILE__ << ":" << __func__ << ":" << __LINE__ << std::endl; }; @@ -48,5 +51,12 @@ namespace uCentral::Utils { [[nodiscard]] uint64_t GetDefaultMacAsInt64(); [[nodiscard]] uint64_t GetSystemId(); + + [[nodiscard]] bool ValidEMailAddress(const std::string &E); + [[nodiscard]] std::string LoadFile( const Poco::File & F); + void ReplaceVariables( std::string & Content , const Types::StringPairVec & P); + + [[nodiscard]] std::string FindMediaType(const Poco::File &F); + [[nodiscard]] std::string BinaryFileToHexString( const Poco::File &F); } #endif // UCENTRALGW_UTILS_H diff --git a/src/storage_callbacks.cpp b/src/storage_callbacks.cpp deleted file mode 100644 index 222dda9..0000000 --- a/src/storage_callbacks.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// -// Created by stephane bourque on 2021-05-09. -// - -#include "StorageService.h" - -namespace uCentral { - -/* - uuid: - type: string - format: uuid - uri: - type: string - creator: - type: string - location: - type: string - format: uri - token: - type: string - tokenType: - type: string - created: - type: string - format: 'date-time' - expires: - type: string - format: 'date-time' - - */ - - typedef Poco::Tuple< - std::string, - std::string, - std::string, - std::string, - std::string, - std::string, - std::string, - uint64_t, - uint64_t - > CallbackRecordTuple; - typedef std::vector CallbackRecordList; - -/* - "UUID VARCHAR(64) PRIMARY KEY, " - "URI TEXT, " - "Location VARCHAR(128)," - "Token TEXT," - "TokenType VARCHAR(64), ", - "Creator VARCHAR(128), " - "Topics TEXT, " - "Created BIGINT, " - "Expires BIGINT", - */ - - bool Storage::AddCallback(uCentral::Objects::Callback & C) { - try { - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Insert(Sess); - - std::string st{"INSERT INTO Callbacks (" - "UUID, URI, Location, Token, TokenType, Creator, Topics, Created, Expires" - ") VALUES(?,?,?,?,?,?,?,?,?)" - }; - - Insert << ConvertParams(st), - Poco::Data::Keywords::use(C.UUID), - Poco::Data::Keywords::use(C.URI), - Poco::Data::Keywords::use(C.Location), - Poco::Data::Keywords::use(C.Token), - Poco::Data::Keywords::use(C.TokenType), - Poco::Data::Keywords::use(C.Creator), - Poco::Data::Keywords::use(C.Topics), - Poco::Data::Keywords::use(C.Created), - Poco::Data::Keywords::use(C.Expires); - Insert.execute(); - return true; - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } - - bool Storage::UpdateCallback(std::string & UUID, uCentral::Objects::Callback & C) { - try { - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Update(Sess); - - std::string st{"UPDATE Callbacks " - " SET URI=?, Location=?, Token=?, TokenType=?, Creator=?, Topics=?, Created=?, Expires=?" - " WHERE UUID=?" - }; - - Update << ConvertParams(st), - Poco::Data::Keywords::use(C.URI), - Poco::Data::Keywords::use(C.Location), - Poco::Data::Keywords::use(C.Token), - Poco::Data::Keywords::use(C.TokenType), - Poco::Data::Keywords::use(C.Creator), - Poco::Data::Keywords::use(C.Topics), - Poco::Data::Keywords::use(C.Created), - Poco::Data::Keywords::use(C.Expires), - Poco::Data::Keywords::use(C.UUID); - Update.execute(); - return true; - - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } - - bool Storage::AddOrUpdateCallback(uCentral::Objects::Callback & C) { - try { - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Select(Sess); - - std::string st{"SELECT UUID FROM Callbacks WHERE UUID=?"}; - std::string TmpUUID; - Select << ConvertParams(st), - Poco::Data::Keywords::into(TmpUUID), - Poco::Data::Keywords::use(C.UUID); - Select.execute(); - if(TmpUUID.empty()) { - return AddCallback(C); - } - else { - return UpdateCallback(TmpUUID, C); - } - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } - - bool Storage::DeleteCallback(std::string & UUID) { - try { - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Delete(Sess); - - std::string st{"DELETE FROM Callbacks WHERE UUID=?"}; - - Delete << ConvertParams(st), - Poco::Data::Keywords::use(UUID); - Delete.execute(); - - return true; - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } - - bool Storage::GetCallback(std::string & UUID, uCentral::Objects::Callback & C) { - try { - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Select(Sess); - - std::string st{"SELECT " - "UUID, URI, Location, Token, TokenType, Creator, Topics, Created, Expires" - " FROM Callbacks WHERE UUID=?" - }; - Select << ConvertParams(st), - Poco::Data::Keywords::into(C.UUID), - Poco::Data::Keywords::into(C.URI), - Poco::Data::Keywords::into(C.Location), - Poco::Data::Keywords::into(C.Token), - Poco::Data::Keywords::into(C.TokenType), - Poco::Data::Keywords::into(C.Creator), - Poco::Data::Keywords::into(C.Topics), - Poco::Data::Keywords::into(C.Created), - Poco::Data::Keywords::into(C.Expires), - Poco::Data::Keywords::use(UUID); - Select.execute(); - return true; - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } - - bool Storage::GetCallbacks(uint64_t From, uint64_t HowMany, std::vector & Callbacks) { - try { - CallbackRecordList Records; - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Select(Sess); - - std::string st{ "SELECT " - " UUID, URI, Location, Token, TokenType, Creator, Topics, Created, Expires" - " FROM Callbacks" - }; - Select << ConvertParams(st), - Poco::Data::Keywords::into(Records), - Poco::Data::Keywords::range(From,From+HowMany); - Select.execute(); - - for(const auto &i:Records) { - uCentral::Objects::Callback C{ - .UUID = i.get<0>(), - .URI = i.get<1>(), - .Location = i.get<2>(), - .Token = i.get<3>(), - .TokenType = i.get<4>(), - .Creator = i.get<5>(), - .Topics = i.get<6>(), - .Created = i.get<7>(), - .Expires = i.get<8>() - }; - - Callbacks.push_back(C); - } - - return true; - - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } -} diff --git a/src/storage_deviceTypes.cpp b/src/storage_deviceTypes.cpp new file mode 100644 index 0000000..ff92b81 --- /dev/null +++ b/src/storage_deviceTypes.cpp @@ -0,0 +1,5 @@ +// +// Created by stephane bourque on 2021-07-12. +// + +#include "storage_deviceTypes.h" diff --git a/src/storage_deviceTypes.h b/src/storage_deviceTypes.h new file mode 100644 index 0000000..760d7fb --- /dev/null +++ b/src/storage_deviceTypes.h @@ -0,0 +1,47 @@ +// +// Created by stephane bourque on 2021-07-12. +// + +#ifndef UCENTRALFMS_STORAGE_DEVICETYPES_H +#define UCENTRALFMS_STORAGE_DEVICETYPES_H + +#include + +namespace uCentral { + static const std::string DBNAME_DEVICETYPES{"deviceTypes"}; + static const std::string DBFIELDS_DEVICETYPES_CREATION { + " id varchar(36) UNIQUE PRIMARY KEY, " + "deviceType varchar, " + "manufacturer varchar, " + "model varchar, " + "policy varchar, " + "notes varchar, " + "lastUpdate bigint, " + "created bigint " + }; + + static const std::string DBFIELDS_DEVICETYPES_SELECT{ + " id, " + "deviceType, " + "manufacturer, " + "model, " + "policy, " + "notes, " + "lastUpdate, " + "created " + }; + + static const std::string DBFIELDS_DEVICETYPES_UPDATE { + " id=?, " + "deviceType=?, " + "manufacturer=?, " + "model=?, " + "policy=?, " + "notes=?, " + "lastUpdate=?, " + "created=? " + }; + +} + +#endif //UCENTRALFMS_STORAGE_DEVICETYPES_H diff --git a/src/storage_firmwares.cpp b/src/storage_firmwares.cpp index 4eef1f0..a79e4dc 100644 --- a/src/storage_firmwares.cpp +++ b/src/storage_firmwares.cpp @@ -5,114 +5,91 @@ #include "StorageService.h" #include "RESTAPI_handler.h" +#include "RESTAPI_FMSObjects.h" +#include "RESTAPI_utils.h" + namespace uCentral { - /* - std::string UUID; - std::string Description; - std::string FirmwareFileName; - std::string FirmwareVersion; - std::string FirmwareHash; - std::string FirmwareLatestDoc; - std::string Owner; - std::string Location; - std::string Compatible; - std::string Uploader; - std::string Digest; - std::string S3URI; - uint64_t DownloadCount; - uint64_t Size; - uint64_t Uploaded; - uint64_t FirmwareDate; - uint64_t Latest; - */ - - typedef Poco::Tuple< - std::string, - std::string, - std::string, - std::string, - std::string, - std::string, - std::string, - std::string, - std::string, - std::string, - std::string, - std::string, - uint64_t, - uint64_t, - uint64_t, - uint64_t, - uint64_t - > FirmwareRecordTuple; - typedef std::vector FirmwareRecordList; - - uint64_t Storage::FirmwareVersion() { - return FirmwareVersion_; + bool Convert(const FirmwaresRecord &T, FMSObjects::Firmware & F) { + F.id = T.get<0>(); + F.deviceType = T.get<1>(); + F.description = T.get<2>(); + F.revision = T.get<3>(); + F.uri = T.get<4>(); + F.image = T.get<5>(); + F.imageDate = T.get<6>(); + F.size = T.get<7>(); + F.downloadCount = T.get<8>(); + F.firmwareHash = T.get<9>(); + F.owner = T.get<10>(); + F.location = T.get<11>(); + F.uploader = T.get<12>(); + F.digest = T.get<13>(); + F.latest = T.get<14>(); + F.notes = RESTAPI_utils::to_object_array(T.get<15>()); + F.created = T.get<16>(); + return true; } - bool Storage::AddFirmware(uCentral::Objects::Firmware & F) { + bool Convert(const FMSObjects::Firmware & F, FirmwaresRecord & T) { + T.set<0>(F.id); + T.set<1>(F.deviceType); + T.set<2>(F.description); + T.set<3>(F.revision); + T.set<4>(F.uri); + T.set<5>(F.image); + T.set<6>(F.imageDate); + T.set<7>(F.size); + T.set<8>(F.downloadCount); + T.set<9>(F.firmwareHash); + T.set<10>(F.owner); + T.set<11>(F.location); + T.set<12>(F.uploader); + T.set<13>(F.digest); + T.set<14>(F.latest); + T.set<15>(RESTAPI_utils::to_string(F.notes)); + T.set<16>(F.created); + return true; + } + + bool Storage::AddFirmware(FMSObjects::Firmware & F) { try { Poco::Data::Session Sess = Pool_->get(); Poco::Data::Statement Insert(Sess); -/* - "UUID VARCHAR(64) PRIMARY KEY, " - "Description VARCHAR(128), " - "Owner VARCHAR(128), " - "Location TEXT, ", - "Compatible VARCHAR(128), " - "Uploader VARCHAR(128), " - "Digest TEXT, " - "FirmwareFileName TEXT, " - "FirmwareVersion VARCHAR(128), " - "FirmwareHash VARCHAR(32), " - "FirmwareLatestDoc TEXT, " - "S3URI TEXT " - "FirmwareDate BIGINT, " - "Uploaded BIGINT, " - "DownloadCount BIGINT, " - "Size BIGINT, - "Latest BIGINT" - */ - // find the older software and change to latest = 0 - if(F.Latest) + if(F.latest) { Poco::Data::Statement Update(Sess); - std::string st{"UPDATE Firmwares SET Latest=0 WHERE Compatible=? AND Latest=1"}; + std::string st{"UPDATE " + DBNAME_FIRMWARES + " SET latest=0 WHERE deviceType=? AND Latest=1"}; Update << ConvertParams(st), - Poco::Data::Keywords::use(F.Compatible); + Poco::Data::Keywords::use(F.deviceType); Update.execute(); } - std::string st{"INSERT INTO Firmwares (" - "UUID, Description, Owner, Location, Compatible, Uploader, Digest, " - "FirmwareFileName, FirmwareVersion, FirmwareHash, FirmwareLatestDoc, " - "S3URI, FirmwareDate, Uploaded, DownloadCount, Size, Latest " + auto Notes = RESTAPI_utils::to_string(F.notes); + std::string st{"INSERT INTO " + DBNAME_FIRMWARES + " (" + + DBFIELDS_FIRMWARES_SELECT + ") VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"}; - Insert << ConvertParams(st), - Poco::Data::Keywords::use(F.UUID), - Poco::Data::Keywords::use(F.Description), - Poco::Data::Keywords::use(F.Owner), - Poco::Data::Keywords::use(F.Location), - Poco::Data::Keywords::use(F.Compatible), - Poco::Data::Keywords::use(F.Uploader), - Poco::Data::Keywords::use(F.Digest), - Poco::Data::Keywords::use(F.FirmwareFileName), - Poco::Data::Keywords::use(F.FirmwareVersion), - Poco::Data::Keywords::use(F.FirmwareHash), - Poco::Data::Keywords::use(F.FirmwareLatestDoc), - Poco::Data::Keywords::use(F.S3URI), - Poco::Data::Keywords::use(F.FirmwareDate), - Poco::Data::Keywords::use(F.Uploaded), - Poco::Data::Keywords::use(F.DownloadCount), - Poco::Data::Keywords::use(F.Size), - Poco::Data::Keywords::use(F.Latest); + Poco::Data::Keywords::use(F.id), + Poco::Data::Keywords::use(F.deviceType), + Poco::Data::Keywords::use(F.description), + Poco::Data::Keywords::use(F.revision), + Poco::Data::Keywords::use(F.uri), + Poco::Data::Keywords::use(F.image), + Poco::Data::Keywords::use(F.imageDate), + Poco::Data::Keywords::use(F.size), + Poco::Data::Keywords::use(F.downloadCount), + Poco::Data::Keywords::use(F.firmwareHash), + Poco::Data::Keywords::use(F.owner), + Poco::Data::Keywords::use(F.location), + Poco::Data::Keywords::use(F.uploader), + Poco::Data::Keywords::use(F.digest), + Poco::Data::Keywords::use(F.latest), + Poco::Data::Keywords::use(Notes), + Poco::Data::Keywords::use(F.created); Insert.execute(); - FirmwareVersion_++; return true; } catch (const Poco::Exception &E) { @@ -121,36 +98,35 @@ namespace uCentral { return false; } - bool Storage::UpdateFirmware(std::string & UUID, uCentral::Objects::Firmware & F) { + bool Storage::UpdateFirmware(std::string & ID, FMSObjects::Firmware & F) { try { Poco::Data::Session Sess = Pool_->get(); Poco::Data::Statement Update(Sess); - std::string st{"UPDATE Firmwares " - " Description, Owner, Location, Compatible, Uploader, Digest, " - " FirmwareFileName, FirmwareVersion, FirmwareHash, FirmwareLatestDoc, " - " S3URI, FirmwareDate, Uploaded, DownloadCount, Size" - " WHERE UUID=?"}; + std::string st{"UPDATE " + DBNAME_FIRMWARES + " set " + DBFIELDS_DEVICETYPES_UPDATE + + " WHERE id=?"}; + auto Notes = RESTAPI_utils::to_string(F.notes); - Update << ConvertParams(st), - Poco::Data::Keywords::use(F.Description), - Poco::Data::Keywords::use(F.Owner), - Poco::Data::Keywords::use(F.Location), - Poco::Data::Keywords::use(F.Compatible), - Poco::Data::Keywords::use(F.Uploader), - Poco::Data::Keywords::use(F.Digest), - Poco::Data::Keywords::use(F.FirmwareFileName), - Poco::Data::Keywords::use(F.FirmwareVersion), - Poco::Data::Keywords::use(F.FirmwareHash), - Poco::Data::Keywords::use(F.FirmwareLatestDoc), - Poco::Data::Keywords::use(F.S3URI), - Poco::Data::Keywords::use(F.FirmwareDate), - Poco::Data::Keywords::use(F.Uploaded), - Poco::Data::Keywords::use(F.DownloadCount), - Poco::Data::Keywords::use(F.Size), - Poco::Data::Keywords::use(F.UUID); + Update << ConvertParams(st), + Poco::Data::Keywords::use(F.id), + Poco::Data::Keywords::use(F.deviceType), + Poco::Data::Keywords::use(F.description), + Poco::Data::Keywords::use(F.revision), + Poco::Data::Keywords::use(F.uri), + Poco::Data::Keywords::use(F.image), + Poco::Data::Keywords::use(F.imageDate), + Poco::Data::Keywords::use(F.size), + Poco::Data::Keywords::use(F.downloadCount), + Poco::Data::Keywords::use(F.firmwareHash), + Poco::Data::Keywords::use(F.owner), + Poco::Data::Keywords::use(F.location), + Poco::Data::Keywords::use(F.uploader), + Poco::Data::Keywords::use(F.digest), + Poco::Data::Keywords::use(F.latest), + Poco::Data::Keywords::use(Notes), + Poco::Data::Keywords::use(F.created); + Poco::Data::Keywords::use(ID); Update.execute(); - FirmwareVersion_++; return true; } catch (const Poco::Exception &E) { @@ -159,17 +135,15 @@ namespace uCentral { return false; } - bool Storage::DeleteFirmware(std::string & UUID) { + bool Storage::DeleteFirmware(std::string & ID) { try { - Poco::Data::Session Sess = Pool_->get(); Poco::Data::Statement Delete(Sess); - std::string st{"DELETE FROM Firmwares WHERE UUID=?"}; + std::string st{"DELETE FROM " + DBNAME_FIRMWARES + " WHERE id=?"}; Delete << ConvertParams(st), - Poco::Data::Keywords::use(UUID); + Poco::Data::Keywords::use(ID); Delete.execute(); - FirmwareVersion_++; return true; } catch (const Poco::Exception &E) { @@ -178,39 +152,25 @@ namespace uCentral { return false; } - bool Storage::GetFirmware(std::string & UUID, uCentral::Objects::Firmware & F) { + bool Storage::GetFirmware(std::string & ID, FMSObjects::Firmware & F) { try { Poco::Data::Session Sess = Pool_->get(); Poco::Data::Statement Select(Sess); - std::string st{"SELECT " - "UUID, Description, Owner, Location, Compatible, Uploader, Digest, " - "FirmwareFileName, FirmwareVersion, FirmwareHash, FirmwareLatestDoc, " - "S3URI, FirmwareDate, Uploaded, DownloadCount, Size, Latest " - " FROM Firmwares WHERE UUID=?"}; + std::string st{"SELECT " + DBFIELDS_FIRMWARES_SELECT + + " FROM " + DBNAME_FIRMWARES + " WHERE id=?"}; + FirmwaresRecordList Records; Select << ConvertParams(st), - Poco::Data::Keywords::into(F.UUID), - Poco::Data::Keywords::into(F.Description), - Poco::Data::Keywords::into(F.Owner), - Poco::Data::Keywords::into(F.Location), - Poco::Data::Keywords::into(F.Compatible), - Poco::Data::Keywords::into(F.Uploader), - Poco::Data::Keywords::into(F.Digest), - Poco::Data::Keywords::into(F.FirmwareFileName), - Poco::Data::Keywords::into(F.FirmwareVersion), - Poco::Data::Keywords::into(F.FirmwareHash), - Poco::Data::Keywords::into(F.FirmwareLatestDoc), - Poco::Data::Keywords::into(F.S3URI), - Poco::Data::Keywords::into(F.FirmwareDate), - Poco::Data::Keywords::into(F.Uploaded), - Poco::Data::Keywords::into(F.DownloadCount), - Poco::Data::Keywords::into(F.Size), - Poco::Data::Keywords::into(F.Latest), - Poco::Data::Keywords::use(UUID); + Poco::Data::Keywords::into(Records), + Poco::Data::Keywords::use(ID); Select.execute(); - return !F.UUID.empty(); + if(Records.empty()) + return false; + + Convert(Records[0],F); + return true; } catch (const Poco::Exception &E) { Logger_.log(E); @@ -218,118 +178,31 @@ namespace uCentral { return false; } - bool Storage::GetFirmwares(uint64_t From, uint64_t HowMany, std::vector & Firmwares) { + bool Storage::GetFirmwares(uint64_t From, uint64_t HowMany, FMSObjects::FirmwareVec & Firmwares) { try { - FirmwareRecordList Records; + FirmwaresRecordList Records; Poco::Data::Session Sess = Pool_->get(); Poco::Data::Statement Select(Sess); - std::string st{"SELECT " - "UUID, Description, Owner, Location, Compatible, Uploader, Digest, " - "FirmwareFileName, FirmwareVersion, FirmwareHash, FirmwareLatestDoc, " - "S3URI, FirmwareDate, Uploaded, DownloadCount, Size, Latest " - " FROM Firmwares"}; + std::string st{"SELECT " + DBFIELDS_FIRMWARES_SELECT + + " FROM " + DBNAME_FIRMWARES }; Select << ConvertParams(st), Poco::Data::Keywords::into(Records), Poco::Data::Keywords::range(From, From + HowMany); Select.execute(); - for(auto const &i:Records) { - uCentral::Objects::Firmware F{ - .UUID = i.get<0>(), - .Description= i.get<1>(), - .Owner= i.get<2>(), - .Location= i.get<3>(), - .Compatible= i.get<4>(), - .Uploader= i.get<5>(), - .Digest= i.get<6>(), - .FirmwareFileName= i.get<7>(), - .FirmwareVersion= i.get<8>(), - .FirmwareHash= i.get<9>(), - .FirmwareLatestDoc= i.get<10>(), - .S3URI= i.get<11>(), - .FirmwareDate= i.get<12>(), - .Uploaded= i.get<13>(), - .DownloadCount= i.get<14>(), - .Size= i.get<15>(), - .Latest = (bool) (i.get<16>()!=0) - }; + for(const auto &R:Records) { + FMSObjects::Firmware F; + Convert(R,F); Firmwares.push_back(F); } - return true; - } catch (const Poco::Exception &E) { Logger_.log(E); } return false; } - -/* - "Compatible VARCHAR(128), " - "Uploader VARCHAR(128), " - "FirmwareVersion VARCHAR(128), " - "S3URI TEXT )", - "Uploaded BIGINT, " - "Size BIGINT, " - "FirmwareDate BIGINT, " - */ - - typedef Poco::Tuple< - std::string, - std::string, - std::string, - std::string, - uint64_t, - uint64_t, - uint64_t, - uint64_t - > FirmwareManifestTuple; - typedef std::vector FirmwareManifestList; - - - bool Storage::BuildFirmwareManifest(Poco::JSON::Object & Manifest, uint64_t & Version) { - try { - SubMutexGuard Guard(Mutex_); - FirmwareManifestList Records; - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Select(Sess); - - std::string st{"SELECT Compatible,Uploader,FirmwareVersion,S3URI,Uploaded,Size,FirmwareDate,Latest FROM Firmwares"}; - - Select << ConvertParams(st), - Poco::Data::Keywords::into(Records); - Select.execute(); - - Poco::JSON::Array Elements; - for(const auto &i:Records) { - Poco::JSON::Object Obj; - - Obj.set("compatible", i.get<0>()); - Obj.set("uploader",i.get<1>()); - Obj.set("version",i.get<2>()); - Obj.set("uri",i.get<3>()); - Obj.set("uploaded",i.get<4>()); - Obj.set("size",i.get<5>()); - Obj.set("date", i.get<6>()); - Obj.set("latest", (bool) (i.get<7>() != 0)); - - Elements.add(Obj); - } - - Manifest.set("firmwares",Elements); - Version = FirmwareVersion_; - - return true; - - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } - - } diff --git a/src/storage_firmwares.h b/src/storage_firmwares.h new file mode 100644 index 0000000..be70d1e --- /dev/null +++ b/src/storage_firmwares.h @@ -0,0 +1,93 @@ +// +// Created by stephane bourque on 2021-07-12. +// + +#ifndef UCENTRALFMS_STORAGE_FIRMWARES_H +#define UCENTRALFMS_STORAGE_FIRMWARES_H + +#include "Poco/Tuple.h" + +namespace uCentral { + static const std::string DBNAME_FIRMWARES{"firmwares"}; + static const std::string DBFIELDS_FIRMWARES_CREATION { + "Id varchar(36) UNIQUE PRIMARY KEY, " + "deviceType varchar, " + "description varchar, " + "revision varchar, " + "uri varchar, " + "image varchar, " + "imageDate bigint, " + "size bigint, " + "downloadCount bigint, " + "firmwareHash varchar, " + "owner varchar, " + "location varchar, " + "uploader varchar, " + "digest varchar, " + "latest int, " + "notes text, " + "created bigint" + }; + + static const std::string DBFIELDS_FIRMWARES_SELECT{ + " Id, " + "deviceType, " + "description, " + "revision, " + "uri, " + "image, " + "imageDate, " + "size, " + "downloadCount, " + "firmwareHash, " + "owner, " + "location, " + "uploader, " + "digest, " + "latest, " + "notes, " + "created " + }; + + static const std::string DBFIELDS_FIRMWARES_UPDATE { + " Id=?, " + "deviceType=?, " + "description=?, " + "revision=?, " + "uri=?, " + "image=?, " + "imageDate=?, " + "size=?, " + "downloadCount=?, " + "firmwareHash=?, " + "owner=?, " + "location=?, " + "uploader=?, " + "digest=?, " + "latest=?, " + "notes=?, " + "created=? " + }; + + typedef Poco::Tuple< + std::string, + std::string, + std::string, + std::string, + std::string, + std::string, + uint64_t, + uint64_t, + uint64_t, + std::string, + std::string, + std::string, + std::string, + std::string, + uint64_t, + std::string, + uint64_t> FirmwaresRecord; + typedef std::vector FirmwaresRecordList; + +} +#endif //UCENTRALFMS_STORAGE_FIRMWARES_H diff --git a/src/storage_history.cpp b/src/storage_history.cpp new file mode 100644 index 0000000..763a30c --- /dev/null +++ b/src/storage_history.cpp @@ -0,0 +1,5 @@ +// +// Created by stephane bourque on 2021-07-12. +// + +#include "storage_history.h" diff --git a/src/storage_history.h b/src/storage_history.h new file mode 100644 index 0000000..09befe1 --- /dev/null +++ b/src/storage_history.h @@ -0,0 +1,35 @@ +// +// Created by stephane bourque on 2021-07-12. +// + +#ifndef UCENTRALFMS_STORAGE_HISTORY_H +#define UCENTRALFMS_STORAGE_HISTORY_H + +#include + +namespace uCentral { + static const std::string DBNAME_HISTORY{"history"}; + static const std::string DBFIELDS_HISTORY_CREATION { + " id varchar(36) UNIQUE PRIMARY KEY, " + "serialNumber varchar, " + "upgraded bigint, " + "commandUUID varchar " + }; + + static const std::string DBFIELDS_HISTORY_SELECT{ + " id, " + "serialNumber, " + "upgraded, " + "commandUUID " + }; + + static const std::string DBFIELDS_HISTORY_UPDATE { + " id=?, " + "serialNumber=?, " + "upgraded=?, " + "commandUUID=? " + }; +} + + +#endif //UCENTRALFMS_STORAGE_HISTORY_H diff --git a/src/storage_latestfirmware.cpp b/src/storage_latestfirmware.cpp deleted file mode 100644 index ccee191..0000000 --- a/src/storage_latestfirmware.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// -// Created by stephane bourque on 2021-05-09. -// - -#include "StorageService.h" - -namespace uCentral { - - typedef Poco::Tuple< - std::string, - std::string, - uint64_t> LatestFirmwareRecordTuple; - typedef std::vector LatestFirmwareRecordList; - - bool Storage::AddLatestFirmware(std::string & Compatible, std::string &UUID) { - try { - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Select(Sess); - - std::string TmpUUID; - std::string st{"SELECT UUID From LatestFirmwares WHERE Compatible=?"}; - - Select << ConvertParams(st) , - Poco::Data::Keywords::into(TmpUUID), - Poco::Data::Keywords::use(Compatible); - Select.execute(); - - uint64_t LastUpdated = time(nullptr); - - if(TmpUUID.empty()) { - Poco::Data::Statement Insert(Sess); - std::string st1{"INSERT INTO LatestFirmwares (Compatible, UUID, LastUpdated) VALUES(?,?,?)"}; - Insert << ConvertParams(st1), - Poco::Data::Keywords::use(Compatible), - Poco::Data::Keywords::use(UUID), - Poco::Data::Keywords::use(LastUpdated); - Insert.execute(); - } else { - Poco::Data::Statement Update(Sess); - std::string st1{"UPDATE LatestFirmwares SET UUID=?, LastUpdated=? WHERE Compatible=?"}; - Update << ConvertParams(st1), - Poco::Data::Keywords::use(UUID), - Poco::Data::Keywords::use(LastUpdated), - Poco::Data::Keywords::use(Compatible); - Update.execute(); - } - return true; - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } - - bool Storage::GetLatestFirmware(std::string & Compatible, uCentral::Objects::LatestFirmware &L) { - try { - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Select(Sess); - - std::string TmpUUID; - std::string st{"SELECT Compatible, UUID, LastUpdated From LatestFirmwares WHERE Compatible=?"}; - - Select << ConvertParams(st) , - Poco::Data::Keywords::into(L.Compatible), - Poco::Data::Keywords::into(L.UUID), - Poco::Data::Keywords::into(L.LastUpdated), - Poco::Data::Keywords::use(Compatible); - Select.execute(); - - return !L.UUID.empty(); - - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } - - bool Storage::DeleteLatestFirmware(std::string & Compatible) { - try { - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Delete(Sess); - - std::string TmpUUID; - std::string st{"DELETE From LatestFirmwares WHERE Compatible=?"}; - - Delete << ConvertParams(st), - Poco::Data::Keywords::use(Compatible); - Delete.execute(); - return true; - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } - - bool Storage::GetLatestFirmwareList(uint64_t From, uint64_t HowMany, std::vector & LatestFirmwareList) { - try { - LatestFirmwareRecordList Records; - Poco::Data::Session Sess = Pool_->get(); - Poco::Data::Statement Select(Sess); - - std::string st{"SELECT Compatible, UUID FROM LatestFirmwares"}; - - Select << ConvertParams(st), - Poco::Data::Keywords::into(Records), - Poco::Data::Keywords::range(From, From + HowMany); - Select.execute(); - - for(const auto &i:Records) { - uCentral::Objects::LatestFirmware L{ - .Compatible = i.get<0>(), - .UUID = i.get<1>() - }; - LatestFirmwareList.push_back(L); - } - return true; - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - return false; - } - -} - - diff --git a/src/storage_mysql.cpp b/src/storage_mysql.cpp new file mode 100644 index 0000000..ca7ecfb --- /dev/null +++ b/src/storage_mysql.cpp @@ -0,0 +1,46 @@ +// +// License type: BSD 3-Clause License +// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE +// +// Created by Stephane Bourque on 2021-03-04. +// Arilia Wireless Inc. +// + +#include "Daemon.h" +#include "StorageService.h" + +namespace uCentral { + +#ifdef SMALL_BUILD + int Service::Setup_MySQL() { uCentral::instance()->exit(Poco::Util::Application::EXIT_CONFIG);} +#else + + int Storage::Setup_MySQL() { + + dbType_ = mysql ; + + Logger_.notice("MySQL Storage enabled."); + auto NumSessions = Daemon()->ConfigGetInt("storage.type.mysql.maxsessions", 64); + auto IdleTime = Daemon()->ConfigGetInt("storage.type.mysql.idletime", 60); + auto Host = Daemon()->ConfigGetString("storage.type.mysql.host"); + auto Username = Daemon()->ConfigGetString("storage.type.mysql.username"); + auto Password = Daemon()->ConfigGetString("storage.type.mysql.password"); + auto Database = Daemon()->ConfigGetString("storage.type.mysql.database"); + auto Port = Daemon()->ConfigGetString("storage.type.mysql.port"); + + std::string ConnectionStr = + "host=" + Host + + ";user=" + Username + + ";password=" + Password + + ";db=" + Database + + ";port=" + Port + + ";compress=true;auto-reconnect=true"; + + MySQLConn_ = std::make_unique(); + MySQLConn_->registerConnector(); + Pool_ = std::make_unique(MySQLConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); + + return 0; + } +#endif +} \ No newline at end of file diff --git a/src/storage_odbc.cpp b/src/storage_odbc.cpp new file mode 100644 index 0000000..cca9a60 --- /dev/null +++ b/src/storage_odbc.cpp @@ -0,0 +1,47 @@ +// +// License type: BSD 3-Clause License +// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE +// +// Created by Stephane Bourque on 2021-03-04. +// Arilia Wireless Inc. +// + +#include "Daemon.h" +#include "StorageService.h" + +namespace uCentral { + +#ifdef SMALL_BUILD + int Service::Setup_ODBC() { uCentral::instance()->exit(Poco::Util::Application::EXIT_CONFIG);} +#else + int Storage::Setup_ODBC() { + + dbType_ = odbc ; + + Logger_.notice("ODBC Storage enabled."); + + auto NumSessions = Daemon()->ConfigGetInt("storage.type.postgresql.maxsessions", 64); + auto IdleTime = Daemon()->ConfigGetInt("storage.type.postgresql.idletime", 60); + auto Host = Daemon()->ConfigGetString("storage.type.postgresql.host"); + auto Username = Daemon()->ConfigGetString("storage.type.postgresql.username"); + auto Password = Daemon()->ConfigGetString("storage.type.postgresql.password"); + auto Database = Daemon()->ConfigGetString("storage.type.postgresql.database"); + auto Port = Daemon()->ConfigGetString("storage.type.postgresql.port"); + auto ConnectionTimeout = Daemon()->ConfigGetString("storage.type.postgresql.connectiontimeout"); + + std::string ConnectionStr = + "host=" + Host + + " user=" + Username + + " password=" + Password + + " dbname=" + Database + + " port=" + Port + + " connect_timeout=" + ConnectionTimeout; + + ODBCConn_ = std::make_unique(); + ODBCConn_->registerConnector(); + Pool_ = std::make_unique(ODBCConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); + + return 0; + } +#endif +} \ No newline at end of file diff --git a/src/storage_pgql.cpp b/src/storage_pgql.cpp new file mode 100644 index 0000000..d7b2f44 --- /dev/null +++ b/src/storage_pgql.cpp @@ -0,0 +1,47 @@ +// +// License type: BSD 3-Clause License +// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE +// +// Created by Stephane Bourque on 2021-03-04. +// Arilia Wireless Inc. +// + +#include "Daemon.h" +#include "StorageService.h" + +namespace uCentral { + +#ifdef SMALL_BUILD + int Service::Setup_PostgreSQL() { uCentral::instance()->exit(Poco::Util::Application::EXIT_CONFIG);} +#else + int Storage::Setup_PostgreSQL() { + Logger_.notice("PostgreSQL Storage enabled."); + + dbType_ = pgsql ; + + auto NumSessions = Daemon()->ConfigGetInt("storage.type.postgresql.maxsessions", 64); + auto IdleTime = Daemon()->ConfigGetInt("storage.type.postgresql.idletime", 60); + auto Host = Daemon()->ConfigGetString("storage.type.postgresql.host"); + auto Username = Daemon()->ConfigGetString("storage.type.postgresql.username"); + auto Password = Daemon()->ConfigGetString("storage.type.postgresql.password"); + auto Database = Daemon()->ConfigGetString("storage.type.postgresql.database"); + auto Port = Daemon()->ConfigGetString("storage.type.postgresql.port"); + auto ConnectionTimeout = Daemon()->ConfigGetString("storage.type.postgresql.connectiontimeout"); + + std::string ConnectionStr = + "host=" + Host + + " user=" + Username + + " password=" + Password + + " dbname=" + Database + + " port=" + Port + + " connect_timeout=" + ConnectionTimeout; + + PostgresConn_ = std::make_unique(); + PostgresConn_->registerConnector(); + Pool_ = std::make_unique(PostgresConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); + + return 0; + } +#endif + +} \ No newline at end of file diff --git a/src/storage_tables.cpp b/src/storage_tables.cpp index 32ce689..8a016c3 100644 --- a/src/storage_tables.cpp +++ b/src/storage_tables.cpp @@ -13,77 +13,27 @@ namespace uCentral { int Storage::Create_Tables() { Create_Firmwares(); - Create_Callbacks(); - Create_LatestFirmwareList(); + Create_History(); + Create_DeviceTypes(); return 0; } -/* - uuid: - type: string - format: uuid - description: - type: string - uploaded: - type: string - format: 'date-time' - firmwareDate: - type: string - format: 'date-time' - firmwareFileName: - type: string - firmwareVersion: - type: string #the version the AP will report - firmwareHash: - type: string - firmwareLatestDoc: - type: string - owner: - type: string - location: - type: string - format: uri - Compatible: - type: string - downloadCount: - type: integer - format: int64 - uploader: - type: string - size: - type: integer - format: int64 - digest: - type: string - s3uri: - type: string - */ - int Storage::Create_Firmwares() { try { Poco::Data::Session Sess = Pool_->get(); - Sess << "CREATE TABLE IF NOT EXISTS Firmwares (" - "UUID VARCHAR(64) PRIMARY KEY, " - "Description VARCHAR(128), " - "Owner VARCHAR(128), " - "Location TEXT, " - "Compatible VARCHAR(128), " - "Uploader VARCHAR(128), " - "Uploaded BIGINT, " - "DownloadCount BIGINT, " - "Size BIGINT, " - "Digest TEXT, " - "FirmwareDate BIGINT, " - "FirmwareFileName TEXT, " - "FirmwareVersion VARCHAR(128), " - "FirmwareHash VARCHAR(32), " - "FirmwareLatestDoc TEXT, " - "S3URI TEXT, " - "Latest BIGINT" - ")", - Poco::Data::Keywords::now; + if(dbType_==mysql) { + Sess << "CREATE TABLE IF NOT EXISTS " + DBNAME_FIRMWARES + " (" + + DBFIELDS_FIRMWARES_CREATION + + ")", + Poco::Data::Keywords::now; + } else { + Sess << "CREATE TABLE IF NOT EXISTS " + DBNAME_FIRMWARES + " (" + + DBFIELDS_FIRMWARES_CREATION + + ")", + Poco::Data::Keywords::now; + } return 0; } catch(const Poco::Exception &E) { Logger_.log(E); @@ -91,84 +41,50 @@ namespace uCentral { return -1; } -/* - properties: - uuid: - type: string - format: uuid - uri: - type: string - format: uri - location: - type: string - format: uri - token: - type: string - creator: - type: string - tokenType: - type: string - topics: - type: string - created: - type: string - format: 'date-time' - expires: - type: string - format: 'date-time' - - */ - - int Storage::Create_Callbacks() { - try { - Poco::Data::Session Sess = Pool_->get(); - - Sess << "CREATE TABLE IF NOT EXISTS Callbacks (" - "UUID VARCHAR(64) PRIMARY KEY, " - "URI TEXT, " - "Location VARCHAR(128)," - "Token TEXT," - "TokenType VARCHAR(64), " - "Creator VARCHAR(128), " - "Topics TEXT, " - "Created BIGINT, " - "Expires BIGINT)", - Poco::Data::Keywords::now; - return 0; - } catch(const Poco::Exception &E) { - Logger_.log(E); - } - return -1; - } - - /* - LatestFirmware: - type: object - properties: - Compatible: - type: string - uuid: - type: string - format: uuid - lastUpdated: - type: string - format: 'date-time' - */ - int Storage::Create_LatestFirmwareList() { + int Storage::Create_History() { try { Poco::Data::Session Sess = Pool_->get(); - Sess << "CREATE TABLE IF NOT EXISTS LatestFirmwares (" - "Compatible VARCHAR(128) PRIMARY KEY, " - "UUID TEXT, " - "LastUpdated BIGINT" - ")", - Poco::Data::Keywords::now; + if(dbType_==mysql) { + Sess << "CREATE TABLE IF NOT EXISTS " + DBNAME_HISTORY + " (" + + DBFIELDS_HISTORY_CREATION + + ",INDEX Serial (SerialNumber ASC, upgraded ASC))" + , Poco::Data::Keywords::now; + } else { + Sess << "CREATE TABLE IF NOT EXISTS " + DBNAME_HISTORY + " (" + + DBFIELDS_HISTORY_CREATION + + ")", + Poco::Data::Keywords::now; + Sess << "CREATE INDEX IF NOT EXISTS Serial ON " + DBNAME_HISTORY + " (SerialNumber ASC, upgraded ASC)", Poco::Data::Keywords::now; + } + return 0; + } catch(const Poco::Exception &E) { + Logger_.log(E); + } + return -1; + } + + int Storage::Create_DeviceTypes() { + try { + Poco::Data::Session Sess = Pool_->get(); + + if(dbType_==mysql) { + Sess << "CREATE TABLE IF NOT EXISTS " + DBNAME_DEVICETYPES + " (" + + DBFIELDS_DEVICETYPES_CREATION + + ")", + Poco::Data::Keywords::now; + } else { + Sess << "CREATE TABLE IF NOT EXISTS " + DBNAME_DEVICETYPES + " (" + + DBFIELDS_DEVICETYPES_CREATION + + ")", + Poco::Data::Keywords::now; + } return 0; } catch(const Poco::Exception &E) { Logger_.log(E); } return -1; } + }