From c6863c8fec437ba4cb59f94dc7fdf71502d783a4 Mon Sep 17 00:00:00 2001 From: Thomas BRUNEL Date: Sun, 18 Feb 2024 18:27:16 +0100 Subject: [PATCH 01/10] [cmake] Add options to not build as a shared library, not buld sqlite3 and not install doctest headers Signed-off-by: Thomas BRUNEL --- 3rdparty/CMakeLists.txt | 10 +++++-- CMakeLists.txt | 65 ++++++++++++++++++++++------------------- CMakeLists_Options.txt | 9 ++++++ 3 files changed, 51 insertions(+), 33 deletions(-) diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 8624745a..5d239349 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -8,11 +8,15 @@ endif() target_include_directories(rapidjson INTERFACE rapidjson/include) # Doctest is an header only on library -add_library(doctest INTERFACE) -target_include_directories(doctest INTERFACE doctest/doctest) +if(${INSTALL_DOCTEST}) + add_library(doctest INTERFACE) + target_include_directories(doctest INTERFACE doctest/doctest) +endif() # SQLite 3 -add_subdirectory(sqlite3) +if(${BUILD_SQLITE}) + add_subdirectory(sqlite3) +endif() # libwebsockets if(${BUILD_LWS_LIBRARY}) diff --git a/CMakeLists.txt b/CMakeLists.txt index cff2b913..51ffa9ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,29 +89,32 @@ if(${BUILD_STATIC_LIBRARY}) endif() # Open OCPP dynamic library -add_library(open-ocpp-dynamic SHARED - src/version.cpp) -target_link_libraries(open-ocpp-dynamic - centralsystem - chargepoint - localcontroller - config - database - messages - rpc - helpers - log - version - x509 - json - ws - websockets -) -set_target_properties(open-ocpp-dynamic PROPERTIES - OUTPUT_NAME "open-ocpp" - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} -) +if (${BUILD_SHARED_LIBRARY}) + add_library(open-ocpp-dynamic SHARED + src/version.cpp) + target_link_libraries(open-ocpp-dynamic + centralsystem + chargepoint + localcontroller + config + database + messages + rpc + helpers + log + version + x509 + json + ws + websockets + ) + set_target_properties(open-ocpp-dynamic PROPERTIES + OUTPUT_NAME "open-ocpp" + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + ) + set(OPEN_OCPP_SHARED_TARGET open-ocpp-dynamic) +endif() # Install commands include(GNUInstallDirs) @@ -121,7 +124,7 @@ file(GLOB_RECURSE PUBLIC_HEADERS file(GLOB OCPP_SCHEMAS LIST_DIRECTORIES false RELATIVE ${CMAKE_SOURCE_DIR} "${CMAKE_SOURCE_DIR}/schemas/*.json") -install(TARGETS open-ocpp-dynamic ${OPEN_OCPP_STATIC_TARGET} +install(TARGETS ${OPEN_OCPP_SHARED_TARGET} ${OPEN_OCPP_STATIC_TARGET} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -145,12 +148,14 @@ set(PKG_CONFIG_INCLUDEDIR "\${prefix}/include/openocpp") set(PKG_CONFIG_LIBS "-L\${libdir}") set(PKG_CONFIG_CFLAGS "-I\${includedir}") -set(LIB_NAME "open-ocpp") -configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/deploy/libopen-ocpp.pc.in" - "${CMAKE_CURRENT_BINARY_DIR}/libopen-ocpp.pc" -) -install(FILES "${CMAKE_BINARY_DIR}/libopen-ocpp.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +if(${BUILD_SHARED_LIBRARY}) + set(LIB_NAME "open-ocpp") + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/deploy/libopen-ocpp.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/libopen-ocpp.pc" + ) + install(FILES "${CMAKE_BINARY_DIR}/libopen-ocpp.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +endif() if(${BUILD_STATIC_LIBRARY}) set(LIB_NAME "open-ocpp_static") diff --git a/CMakeLists_Options.txt b/CMakeLists_Options.txt index b6024cf3..3060238f 100644 --- a/CMakeLists_Options.txt +++ b/CMakeLists_Options.txt @@ -14,6 +14,9 @@ if(EXTERNAL_LOGGER) add_compile_definitions(EXTERNAL_LOGGER=1) endif() +# Shared library +option(BUILD_SHARED_LIBRARY "Build Open OCPP as a shared library" ON) + # Static library option(BUILD_STATIC_LIBRARY "Build Open OCPP as a static library" ON) @@ -26,6 +29,12 @@ option(BUILD_EXAMPLES "Build examples" # Build the libwebsocket library along with the Open OCPP library option(BUILD_LWS_LIBRARY "Build libwebsocket library" ON) +# Build the sqlite3 library along with the Open OCPP library +option(BUILD_SQLITE "Build sqlite3 library" ON) + +# Install the Doctest header +option(INSTALL_DOCTEST "Install doctest headers" ON) + # Use only the CrtAllocator in Rapidjson, not the MemoryPoolAllocator option(USE_CRT_ALLOC_FOR_RAPIDJSON "Use the CrtAllocator for Rapidjson instead of the MemoryPoolAllocator" OFF) if(USE_CRT_ALLOC_FOR_RAPIDJSON) From fb9826a201114737a975c876c10e9e5e3f547be3 Mon Sep 17 00:00:00 2001 From: Thomas BRUNEL Date: Sun, 18 Feb 2024 18:40:21 +0100 Subject: [PATCH 02/10] [messages] Fix include of JsonValidator as local instead of system This will fix compilation error to happened when cross-compiling an application statically linked with the library, as it cannot find the header in system include folder Signed-off-by: Thomas BRUNEL --- src/messages/MessagesValidator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/messages/MessagesValidator.h b/src/messages/MessagesValidator.h index acaeef9c..3d24e466 100644 --- a/src/messages/MessagesValidator.h +++ b/src/messages/MessagesValidator.h @@ -19,7 +19,7 @@ along with OpenOCPP. If not, see . #ifndef OPENOCPP_MESSAGESVALIDATOR_H #define OPENOCPP_MESSAGESVALIDATOR_H -#include +#include "JsonValidator.h" #include #include From 55ab75da7dbd8c6d61693cd60b81d7535c3883c6 Mon Sep 17 00:00:00 2001 From: c-jimenez <18682655+c-jimenez@users.noreply.github.com> Date: Sun, 3 Mar 2024 22:34:43 +0100 Subject: [PATCH 03/10] [tools] Fix thread pool stop race condition --- src/tools/helpers/WorkerThreadPool.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tools/helpers/WorkerThreadPool.h b/src/tools/helpers/WorkerThreadPool.h index 8580623c..a2287c81 100644 --- a/src/tools/helpers/WorkerThreadPool.h +++ b/src/tools/helpers/WorkerThreadPool.h @@ -95,7 +95,10 @@ class Job : public JobBase } // Notify end of job - this->end = true; + { + std::lock_guard lock(this->end_of_job_mutex); + this->end = true; + } this->end_of_job_var.notify_all(); } @@ -132,7 +135,10 @@ class Job : public JobBase } // Notify end of job - this->end = true; + { + std::lock_guard lock(this->end_of_job_mutex); + this->end = true; + } this->end_of_job_var.notify_all(); } From f5d95cc0f56b87e23bc811854949ecdb10935414 Mon Sep 17 00:00:00 2001 From: Mahdi Dadashi Date: Wed, 6 Mar 2024 10:37:45 +0330 Subject: [PATCH 04/10] send info in StatusNotification with no error ability to send info and vendor id and error code in StatusNotification with no NoError if any of them are set --- src/messages/StatusNotification.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/messages/StatusNotification.cpp b/src/messages/StatusNotification.cpp index b0b011d8..6f426b08 100644 --- a/src/messages/StatusNotification.cpp +++ b/src/messages/StatusNotification.cpp @@ -71,8 +71,8 @@ bool StatusNotificationReqConverter::fromJson(const rapidjson::Value& json, extract(json, "info", data.info); data.status = ChargePointStatusHelper.fromString(json["status"].GetString()); ret = ret && extract(json, "timestamp", data.timestamp, error_message); - extract(json, "info", data.vendorId); - extract(json, "info", data.vendorErrorCode); + extract(json, "vendorId", data.vendorId); + extract(json, "vendorErrorCode", data.vendorErrorCode); if (!ret) { error_code = ocpp::rpc::IRpc::RPC_ERROR_TYPE_CONSTRAINT_VIOLATION; @@ -87,12 +87,15 @@ bool StatusNotificationReqConverter::toJson(const StatusNotificationReq& data, r fill(json, "errorCode", ChargePointErrorCodeHelper.toString(data.errorCode)); fill(json, "status", ChargePointStatusHelper.toString(data.status)); fill(json, "timestamp", data.timestamp); - if (data.errorCode != ChargePointErrorCode::NoError) - { - fill(json, "info", data.info); - fill(json, "vendorId", data.vendorId); - fill(json, "vendorErrorCode", data.vendorErrorCode); - } + //if (data.errorCode != ChargePointErrorCode::NoError) + //{ + if(data.info.isSet()) + fill(json, "info", data.info); + if(data.vendorId.isSet()) + fill(json, "vendorId", data.vendorId); + if(data.vendorErrorCode.isSet()) + fill(json, "vendorErrorCode", data.vendorErrorCode); + // } return true; } From 1668756341e9af1f52abffb02c20c7001f2b984b Mon Sep 17 00:00:00 2001 From: Mahdi Dadashi Date: Wed, 6 Mar 2024 10:42:38 +0330 Subject: [PATCH 05/10] limit value Accepts at most one digit fraction according to standard ,limit value is "Required. Charging rate limit during the schedule period, in the applicable chargingRateUnit, for example in Amperes or Watts. Accepts at most one digit fraction (e.g. 8.1)." --- src/messages/IMessageConverter.h | 11 ++++++++ .../types/ChargingScheduleConverter.cpp | 25 +++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/messages/IMessageConverter.h b/src/messages/IMessageConverter.h index 07363963..0636aaa1 100644 --- a/src/messages/IMessageConverter.h +++ b/src/messages/IMessageConverter.h @@ -94,6 +94,17 @@ class IMessageConverter json.AddMember(rapidjson::StringRef(name), rapidjson::Value(value), *allocator); } + /** + * @brief Helper function to fill a floating point value in a JSON object + * @param json JSON object to fill + * @param field Name of the field to fill + * @param value Floating point value to fill + */ + void fill(rapidjson::Value& json, const char* name, const double value) + { + json.AddMember(rapidjson::StringRef(name), rapidjson::Value(value), *allocator); + } + /** * @brief Helper function to fill a string value in a JSON object * @param json JSON object to fill diff --git a/src/messages/types/ChargingScheduleConverter.cpp b/src/messages/types/ChargingScheduleConverter.cpp index 8df8705f..7592d374 100644 --- a/src/messages/types/ChargingScheduleConverter.cpp +++ b/src/messages/types/ChargingScheduleConverter.cpp @@ -67,14 +67,35 @@ bool ChargingScheduleConverter::fromJson(const rapidjson::Value& json, return ret; } + +double getDecimal(float data) +{ + std::stringstream m_stringstream; + std::string m_string; + + m_stringstream.str(""); + m_stringstream.clear(); + m_stringstream << std::fixed << std::setprecision(1) << data; + m_string = m_stringstream.str(); + + double res = std::stod(m_string); + + return res; +} + /** @copydoc bool IMessageConverter::toJson(const ocpp::types::ChargingSchedule&, * rapidjson::Document&) */ bool ChargingScheduleConverter::toJson(const ocpp::types::ChargingSchedule& data, rapidjson::Document& json) { + std::stringstream m_stringstream; + std::string m_string; + + fill(json, "startSchedule", data.startSchedule); fill(json, "duration", data.duration); fill(json, "chargingRateUnit", ChargingRateUnitTypeHelper.toString(data.chargingRateUnit)); - fill(json, "minChargingRate", data.minChargingRate); + fill(json, "minChargingRate", getDecimal(data.minChargingRate)); + rapidjson::Value chargingSchedulePeriod(rapidjson::kArrayType); for (const ChargingSchedulePeriod& schedule_period : data.chargingSchedulePeriod) @@ -82,7 +103,7 @@ bool ChargingScheduleConverter::toJson(const ocpp::types::ChargingSchedule& data rapidjson::Document value; value.Parse("{}"); fill(value, "startPeriod", schedule_period.startPeriod); - fill(value, "limit", schedule_period.limit); + fill(value, "limit", getDecimal(schedule_period.limit)); fill(value, "numberPhases", schedule_period.numberPhases); chargingSchedulePeriod.PushBack(value.Move(), *allocator); } From 90409e6420e95891d50022d17800710aa2e7c834 Mon Sep 17 00:00:00 2001 From: c-jimenez <18682655+c-jimenez@users.noreply.github.com> Date: Sun, 10 Mar 2024 11:51:26 +0100 Subject: [PATCH 06/10] [messages-rpc] Use rapidjson to set decimal precision to 1 digit + code format --- .../DefaultChargePointEventsHandler.cpp | 6 ++--- examples/common/config/OcppConfig.cpp | 6 ++--- .../smartcharging/ProfileDatabase.cpp | 1 + .../smartcharging/SmartChargingManager.cpp | 2 +- src/messages/StatusNotification.cpp | 15 +++++------ .../types/ChargingScheduleConverter.cpp | 25 ++----------------- src/rpc/RpcBase.cpp | 2 ++ .../libwebsockets/LibWebsocketClientPool.cpp | 2 +- 8 files changed, 19 insertions(+), 40 deletions(-) diff --git a/examples/common/DefaultChargePointEventsHandler.cpp b/examples/common/DefaultChargePointEventsHandler.cpp index 7d8c1a1c..369039b0 100644 --- a/examples/common/DefaultChargePointEventsHandler.cpp +++ b/examples/common/DefaultChargePointEventsHandler.cpp @@ -169,15 +169,15 @@ bool DefaultChargePointEventsHandler::remoteStartTransactionRequested(unsigned i bool ret = false; cout << "Remote start transaction : " << connector_id << " - " << id_tag << endl; - if(connector_id > m_config.ocppConfig().numberOfConnectors() || connector_id == 0) + if (connector_id > m_config.ocppConfig().numberOfConnectors() || connector_id == 0) { - ret=false; + ret = false; } else { m_remote_start_pending[connector_id - 1u] = true; m_remote_start_id_tag[connector_id - 1u] = id_tag; - ret=true; + ret = true; } return ret; } diff --git a/examples/common/config/OcppConfig.cpp b/examples/common/config/OcppConfig.cpp index 86e16160..6c4db9be 100644 --- a/examples/common/config/OcppConfig.cpp +++ b/examples/common/config/OcppConfig.cpp @@ -207,8 +207,8 @@ ocpp::types::ConfigurationStatus OcppConfig::setConfiguration(const std::string& } } - if (ret != ConfigurationStatus::Rejected) - { + if (ret != ConfigurationStatus::Rejected) + { if ((it->second & PARAM_OCPP) != 0) { m_config.set(OCPP_PARAMS, key, value); @@ -225,7 +225,7 @@ ocpp::types::ConfigurationStatus OcppConfig::setConfiguration(const std::string& { ret = ConfigurationStatus::Accepted; } - } + } } else { diff --git a/src/chargepoint/smartcharging/ProfileDatabase.cpp b/src/chargepoint/smartcharging/ProfileDatabase.cpp index 6301b579..a0224357 100644 --- a/src/chargepoint/smartcharging/ProfileDatabase.cpp +++ b/src/chargepoint/smartcharging/ProfileDatabase.cpp @@ -362,6 +362,7 @@ std::string ProfileDatabase::serialize(const ocpp::types::ChargingProfile& profi rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(1); // OCPP decimals have 1 digit precision profile_json.Accept(writer); profile_str = buffer.GetString(); return profile_str; diff --git a/src/chargepoint/smartcharging/SmartChargingManager.cpp b/src/chargepoint/smartcharging/SmartChargingManager.cpp index 0ca28601..e7c71fe2 100644 --- a/src/chargepoint/smartcharging/SmartChargingManager.cpp +++ b/src/chargepoint/smartcharging/SmartChargingManager.cpp @@ -303,7 +303,7 @@ bool SmartChargingManager::handleMessage(const ocpp::messages::SetChargingProfil } else { - ret = false; + ret = false; error_message = "Recurring profiles must have a start schedule and a duration"; } } diff --git a/src/messages/StatusNotification.cpp b/src/messages/StatusNotification.cpp index 6f426b08..ca9f41bb 100644 --- a/src/messages/StatusNotification.cpp +++ b/src/messages/StatusNotification.cpp @@ -87,15 +87,12 @@ bool StatusNotificationReqConverter::toJson(const StatusNotificationReq& data, r fill(json, "errorCode", ChargePointErrorCodeHelper.toString(data.errorCode)); fill(json, "status", ChargePointStatusHelper.toString(data.status)); fill(json, "timestamp", data.timestamp); - //if (data.errorCode != ChargePointErrorCode::NoError) - //{ - if(data.info.isSet()) - fill(json, "info", data.info); - if(data.vendorId.isSet()) - fill(json, "vendorId", data.vendorId); - if(data.vendorErrorCode.isSet()) - fill(json, "vendorErrorCode", data.vendorErrorCode); - // } + if (data.info.isSet()) + fill(json, "info", data.info); + if (data.vendorId.isSet()) + fill(json, "vendorId", data.vendorId); + if (data.vendorErrorCode.isSet()) + fill(json, "vendorErrorCode", data.vendorErrorCode); return true; } diff --git a/src/messages/types/ChargingScheduleConverter.cpp b/src/messages/types/ChargingScheduleConverter.cpp index 7592d374..8df8705f 100644 --- a/src/messages/types/ChargingScheduleConverter.cpp +++ b/src/messages/types/ChargingScheduleConverter.cpp @@ -67,35 +67,14 @@ bool ChargingScheduleConverter::fromJson(const rapidjson::Value& json, return ret; } - -double getDecimal(float data) -{ - std::stringstream m_stringstream; - std::string m_string; - - m_stringstream.str(""); - m_stringstream.clear(); - m_stringstream << std::fixed << std::setprecision(1) << data; - m_string = m_stringstream.str(); - - double res = std::stod(m_string); - - return res; -} - /** @copydoc bool IMessageConverter::toJson(const ocpp::types::ChargingSchedule&, * rapidjson::Document&) */ bool ChargingScheduleConverter::toJson(const ocpp::types::ChargingSchedule& data, rapidjson::Document& json) { - std::stringstream m_stringstream; - std::string m_string; - - fill(json, "startSchedule", data.startSchedule); fill(json, "duration", data.duration); fill(json, "chargingRateUnit", ChargingRateUnitTypeHelper.toString(data.chargingRateUnit)); - fill(json, "minChargingRate", getDecimal(data.minChargingRate)); - + fill(json, "minChargingRate", data.minChargingRate); rapidjson::Value chargingSchedulePeriod(rapidjson::kArrayType); for (const ChargingSchedulePeriod& schedule_period : data.chargingSchedulePeriod) @@ -103,7 +82,7 @@ bool ChargingScheduleConverter::toJson(const ocpp::types::ChargingSchedule& data rapidjson::Document value; value.Parse("{}"); fill(value, "startPeriod", schedule_period.startPeriod); - fill(value, "limit", getDecimal(schedule_period.limit)); + fill(value, "limit", schedule_period.limit); fill(value, "numberPhases", schedule_period.numberPhases); chargingSchedulePeriod.PushBack(value.Move(), *allocator); } diff --git a/src/rpc/RpcBase.cpp b/src/rpc/RpcBase.cpp index 44569edb..71955410 100644 --- a/src/rpc/RpcBase.cpp +++ b/src/rpc/RpcBase.cpp @@ -75,6 +75,7 @@ bool RpcBase::call(const std::string& action, // Serialize message rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(1); // OCPP decimals have 1 digit precision payload.Accept(writer); std::stringstream serialized_message; @@ -186,6 +187,7 @@ void RpcBase::processIncomingRequest(std::shared_ptr& rpc_message) // Serialize message rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(1); // OCPP decimals have 1 digit precision response.Accept(writer); std::stringstream serialized_message; diff --git a/src/websockets/libwebsockets/LibWebsocketClientPool.cpp b/src/websockets/libwebsockets/LibWebsocketClientPool.cpp index f9dbd9be..a16de847 100644 --- a/src/websockets/libwebsockets/LibWebsocketClientPool.cpp +++ b/src/websockets/libwebsockets/LibWebsocketClientPool.cpp @@ -144,7 +144,7 @@ void LibWebsocketClientPool::process() // Dummy vhost to handle context related events struct lws_protocols protocols[] = {{"LibWebsocketClientPool", &LibWebsocketClientPool::eventCallback, 0, 0, 0, this, 0}, - LWS_PROTOCOL_LIST_TERM}; + LWS_PROTOCOL_LIST_TERM}; struct lws_context_creation_info vhost_info; memset(&vhost_info, 0, sizeof(vhost_info)); vhost_info.protocols = protocols; From 242d700119d1e7f94d5e52066076f4a0b8e49011 Mon Sep 17 00:00:00 2001 From: c-jimenez <18682655+c-jimenez@users.noreply.github.com> Date: Sun, 10 Mar 2024 14:15:55 +0100 Subject: [PATCH 07/10] [websockets] Add support for HTTP Basic Authentication password which may contain non ASCII/UTF-8 characters (including null char) --- .../libwebsockets/LibWebsocketClient.cpp | 29 ++++++++++++++++++- .../libwebsockets/LibWebsocketClientPool.cpp | 11 +++++-- .../libwebsockets/LibWebsocketServer.cpp | 3 +- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/websockets/libwebsockets/LibWebsocketClient.cpp b/src/websockets/libwebsockets/LibWebsocketClient.cpp index f021356d..70074138 100644 --- a/src/websockets/libwebsockets/LibWebsocketClient.cpp +++ b/src/websockets/libwebsockets/LibWebsocketClient.cpp @@ -23,6 +23,29 @@ along with OpenOCPP. If not, see . #include #include +/** @brief Generate basic authent header with bytes password (may contain \0 char) */ +int lws_http_basic_auth_gen2(const char* user, const void* pw, size_t pwd_len, char* buf, size_t len) +{ + size_t n = strlen(user), m = pwd_len; + char b[128]; + + if (len < 6 + ((4 * (n + m + 1)) / 3) + 1) + return 1; + + memcpy(buf, "Basic ", 6); + + n = (unsigned int)lws_snprintf(b, sizeof(b), "%s:", user); + if ((n + pwd_len) >= sizeof(b) - 2) + return 2; + memcpy(&b[n], pw, pwd_len); + n += pwd_len; + + lws_b64_encode_string(b, (int)n, buf + 6, (int)len - 6); + buf[len - 1] = '\0'; + + return 0; +} + namespace ocpp { namespace websockets @@ -411,7 +434,11 @@ int LibWebsocketClient::eventCallback(struct lws* wsi, enum lws_callback_reasons if (client->m_credentials.user.empty()) break; - if (lws_http_basic_auth_gen(client->m_credentials.user.c_str(), client->m_credentials.password.c_str(), b, sizeof(b))) + if (lws_http_basic_auth_gen2(client->m_credentials.user.c_str(), + client->m_credentials.password.data(), + client->m_credentials.password.size(), + b, + sizeof(b))) break; if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_AUTHORIZATION, (unsigned char*)b, (int)strlen(b), p, end)) return -1; diff --git a/src/websockets/libwebsockets/LibWebsocketClientPool.cpp b/src/websockets/libwebsockets/LibWebsocketClientPool.cpp index a16de847..5b58e60f 100644 --- a/src/websockets/libwebsockets/LibWebsocketClientPool.cpp +++ b/src/websockets/libwebsockets/LibWebsocketClientPool.cpp @@ -22,6 +22,9 @@ along with OpenOCPP. If not, see . #include #include +/** @brief Generate basic authent header with bytes password (may contain \0 char) => implemented in LibWebsocketClient.cpp*/ +extern int lws_http_basic_auth_gen2(const char* user, const void* pw, size_t pwd_len, char* buf, size_t len); + namespace ocpp { namespace websockets @@ -144,7 +147,7 @@ void LibWebsocketClientPool::process() // Dummy vhost to handle context related events struct lws_protocols protocols[] = {{"LibWebsocketClientPool", &LibWebsocketClientPool::eventCallback, 0, 0, 0, this, 0}, - LWS_PROTOCOL_LIST_TERM}; + LWS_PROTOCOL_LIST_TERM}; struct lws_context_creation_info vhost_info; memset(&vhost_info, 0, sizeof(vhost_info)); vhost_info.protocols = protocols; @@ -599,7 +602,11 @@ int LibWebsocketClientPool::Client::eventCallback( if (client->m_credentials.user.empty()) break; - if (lws_http_basic_auth_gen(client->m_credentials.user.c_str(), client->m_credentials.password.c_str(), b, sizeof(b))) + if (lws_http_basic_auth_gen2(client->m_credentials.user.c_str(), + client->m_credentials.password.data(), + client->m_credentials.password.size(), + b, + sizeof(b))) break; if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_AUTHORIZATION, (unsigned char*)b, (int)strlen(b), p, end)) return -1; diff --git a/src/websockets/libwebsockets/LibWebsocketServer.cpp b/src/websockets/libwebsockets/LibWebsocketServer.cpp index 2efa8b29..f6815c10 100644 --- a/src/websockets/libwebsockets/LibWebsocketServer.cpp +++ b/src/websockets/libwebsockets/LibWebsocketServer.cpp @@ -383,7 +383,8 @@ int LibWebsocketServer::eventCallback(struct lws* wsi, enum lws_callback_reasons { // Check credentials std::string username(plain, static_cast(pcolon - plain)); - std::string password(pcolon + 1u); + std::string password(pcolon + 1u, m - (username.size() + 1u)); + password.resize(m - (username.size() + 1u)); authorized = server->m_listener->wsCheckCredentials(uri, username, password); } } From 68cd35346e9e70384b6ee35d3c8f1c4cc893be0d Mon Sep 17 00:00:00 2001 From: c-jimenez <18682655+c-jimenez@users.noreply.github.com> Date: Sun, 10 Mar 2024 14:17:05 +0100 Subject: [PATCH 08/10] [chargepoint] Fix AuthorizationKey handling (store received hexadecimal string but use bytes representation on connect) --- .vscode/launch.json | 20 ++++++++ .../CentralSystemEventsHandler.cpp | 22 +++++---- src/chargepoint/ChargePoint.cpp | 6 ++- src/chargepoint/security/SecurityManager.cpp | 13 ++--- src/tools/helpers/StringHelpers.cpp | 48 +++++++++++++++++++ src/tools/helpers/StringHelpers.h | 28 +++++++++++ 6 files changed, 121 insertions(+), 16 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 17e1d00a..a70a919a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -38,6 +38,26 @@ "target": "${workspaceRoot}/build_gcc_native/tests/chargepoint/smartcharging/test_smartcharging_setpoint", "cwd": "${workspaceRoot}", "valuesFormatting": "parseText" + }, + { + "type": "cppdbg", + "request": "attach", + "name": "Join process", + "program": "${workspaceRoot}/bin/gcc_native/quick_start_chargepoint", + "processId": "${command:pickProcess}", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Activer l'impression en mode Pretty pour gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Définir la version désassemblage sur Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] } ] } \ No newline at end of file diff --git a/examples/security_centralsystem/CentralSystemEventsHandler.cpp b/examples/security_centralsystem/CentralSystemEventsHandler.cpp index 7d2cb8b4..ef3ac4f6 100644 --- a/examples/security_centralsystem/CentralSystemEventsHandler.cpp +++ b/examples/security_centralsystem/CentralSystemEventsHandler.cpp @@ -25,6 +25,7 @@ SOFTWARE. #include "CentralSystemEventsHandler.h" #include "ChargePointDatabase.h" +#include #include #include #include @@ -99,7 +100,8 @@ bool CentralSystemEventsHandler::checkCredentials(const std::string& chargepoint { bool ret = false; - cout << "Check credentials for [" << chargepoint_id << "] : " << password << endl; + std::string hex_encoded_password = ocpp::helpers::toHexString(password); + cout << "Check credentials for [" << chargepoint_id << "] : " << hex_encoded_password << endl; // HTTP Basic Authentication is for Charge Points configured with Security Profile 1 or 2 only @@ -111,7 +113,7 @@ bool CentralSystemEventsHandler::checkCredentials(const std::string& chargepoint { if ((security_profile == 1u) || (security_profile == 2u)) { - ret = (password == authent_key); + ret = (hex_encoded_password == authent_key); } else { @@ -257,14 +259,18 @@ ocpp::types::RegistrationStatus CentralSystemEventsHandler::ChargePointRequestHa } else { - // Generate an authent key for the charge point : minimal 16 bytes, max : 20 bytes - std::stringstream ss_authent_key; - ss_authent_key << std::hex; - for (int i = 0; i < 5; i++) + // Generate an authent key for the charge point : minimal 8 bytes, max : 20 bytes + std::mt19937 rand_gen; + std::uniform_int_distribution rand_distrib; + std::random_device rd; + rand_gen.seed(rd()); + + std::array authent_key_bytes; + for (auto& val : authent_key_bytes) { - ss_authent_key << std::setfill('0') << std::setw(4) << std::rand(); + val = static_cast(rand_distrib(rand_gen)); } - m_authent_key = ss_authent_key.str(); + m_authent_key = ocpp::helpers::toHexString(authent_key_bytes); // Add the charge point to the database m_chargepoint_db.addChargePoint(this->proxy()->identifier(), serial_number, vendor, model, 0, m_authent_key); diff --git a/src/chargepoint/ChargePoint.cpp b/src/chargepoint/ChargePoint.cpp index 96515056..05bc60f7 100644 --- a/src/chargepoint/ChargePoint.cpp +++ b/src/chargepoint/ChargePoint.cpp @@ -1164,8 +1164,10 @@ bool ChargePoint::doConnect() std::string authorization_key = m_ocpp_config.authorizationKey(); if (!authorization_key.empty() && (security_profile <= 2)) { - credentials.user = m_stack_config.chargePointIdentifier(); - credentials.password = authorization_key; + auto authentication_key = ocpp::helpers::fromHexString(authorization_key); + credentials.user = m_stack_config.chargePointIdentifier(); + credentials.password = std::string(reinterpret_cast(authentication_key.data()), authorization_key.size()); + credentials.password.resize(authentication_key.size()); } if (security_profile != 1) { diff --git a/src/chargepoint/security/SecurityManager.cpp b/src/chargepoint/security/SecurityManager.cpp index 8fbadf41..b481a539 100644 --- a/src/chargepoint/security/SecurityManager.cpp +++ b/src/chargepoint/security/SecurityManager.cpp @@ -669,15 +669,16 @@ bool SecurityManager::handleMessage(const ocpp::messages::InstallCertificateReq& ocpp::types::ConfigurationStatus SecurityManager::checkAuthorizationKeyParameter(const std::string& key, const std::string& value) { (void)key; - ConfigurationStatus ret = ConfigurationStatus::Accepted; + ConfigurationStatus ret = ConfigurationStatus::Rejected; - // Authorization key length for security profiles 1 and 2 must be between 32 and 40 bytes - unsigned int security_profile = m_ocpp_config.securityProfile(); - if ((security_profile == 1) || (security_profile == 2)) + // Authorization key length for security profiles 1 and 2 must be between 16 and 40 bytes + // and must be a valid hexadecimal representation + if ((value.size() >= 16u) && (value.size() <= 40u)) { - if ((value.size() < 32u) || (value.size() > 40u)) + auto key_bytes = ocpp::helpers::fromHexString(value); + if (key_bytes.size() == (value.size() / 2u)) { - ret = ConfigurationStatus::Rejected; + ret = ConfigurationStatus::Accepted; } } diff --git a/src/tools/helpers/StringHelpers.cpp b/src/tools/helpers/StringHelpers.cpp index fa53d420..e05fcb95 100644 --- a/src/tools/helpers/StringHelpers.cpp +++ b/src/tools/helpers/StringHelpers.cpp @@ -18,6 +18,9 @@ along with OpenOCPP. If not, see . #include "StringHelpers.h" +#include +#include + namespace ocpp { namespace helpers @@ -114,5 +117,50 @@ bool endsWith(const std::string& str, const std::string& substr) return ret; } +/** @brief Helper function to convert a buffer to an hexadecimal string representation */ +std::string toHexString(const void* buffer, size_t size) +{ + std::stringstream ss; + const uint8_t* data = reinterpret_cast(buffer); + if (data) + { + ss << std::hex; + for (size_t i = 0; i < size; i++) + { + ss << std::setw(2) << std::setfill('0') << static_cast(data[i]) << ""; + } + } + return ss.str(); +} + +/** @brief Helper function to convert an hexadecimal string representation into an array of bytes */ +std::vector fromHexString(const std::string& hex_string) +{ + std::vector ret; + if ((hex_string.size() & 1) == 0) + { + try + { + for (size_t i = 0; i < hex_string.size(); i += 2u) + { + std::string ss = hex_string.substr(i, 2u); + for (const auto& c : ss) + { + if (!((c >= '0') && (c <= '9')) && !((c >= 'a') && (c <= 'f')) && !((c >= 'A') && (c <= 'F'))) + { + throw std::out_of_range(ss); + } + } + ret.push_back(static_cast(std::stoul(ss, nullptr, 16))); + } + } + catch (...) + { + ret.clear(); + } + } + return ret; +} + } // namespace helpers } // namespace ocpp diff --git a/src/tools/helpers/StringHelpers.h b/src/tools/helpers/StringHelpers.h index 0ef88592..63b37618 100644 --- a/src/tools/helpers/StringHelpers.h +++ b/src/tools/helpers/StringHelpers.h @@ -19,8 +19,10 @@ along with OpenOCPP. If not, see . #ifndef OPENOCPP_STRING_H #define OPENOCPP_STRING_H +#include #include #include + namespace ocpp { namespace helpers @@ -87,6 +89,32 @@ bool startsWith(const std::string& str, const std::string& substr); */ bool endsWith(const std::string& str, const std::string& substr); +/** + * @brief Helper function to convert a buffer to an hexadecimal string representation + * @param buffer Buffer to convert + * @param size Size of the buffer in bytes + * @return Buffer contents as an hexadecimal string + */ +std::string toHexString(const void* buffer, size_t size); + +/** + * @brief Helper function to convert a ContiguousContainer to an hexadecimal string representation + * @param cont container to convert + * @return Container contents as an hexadecimal string + */ +template +std::string toHexString(const ContiguousContainer& cont) +{ + return toHexString(&cont[0], cont.size() * sizeof(cont[0])); +} + +/** + * @brief Helper function to convert an hexadecimal string representation into an array of bytes + * @param hex_string Hexadecimal string to convert + * @return Corresponding array of bytes, empty if the input string is invalid + */ +std::vector fromHexString(const std::string& hex_string); + } // namespace helpers } // namespace ocpp From 5027159ea0419cf7e7395259b4ce21926bfb9c25 Mon Sep 17 00:00:00 2001 From: c-jimenez <18682655+c-jimenez@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:27:30 +0100 Subject: [PATCH 09/10] =?UTF-8?q?[chargepoint]=C2=A0Handle=20configuration?= =?UTF-8?q?s=20keys=20name=20as=20case=20insensitive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chargepoint/ChargePoint.cpp | 4 ++-- src/chargepoint/config/ConfigManager.cpp | 15 +++++++++------ src/tools/helpers/StringHelpers.cpp | 9 +++++++++ src/tools/helpers/StringHelpers.h | 8 ++++++++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/chargepoint/ChargePoint.cpp b/src/chargepoint/ChargePoint.cpp index 05bc60f7..0aa05710 100644 --- a/src/chargepoint/ChargePoint.cpp +++ b/src/chargepoint/ChargePoint.cpp @@ -990,7 +990,7 @@ void ChargePoint::rcpMessageSent(const std::string& msg) void ChargePoint::configurationValueChanged(const std::string& key) { // Check configuration key - if (key == "AuthorizationKey") + if (key == "authorizationkey") { // Reconnect with new authorization key if (m_ocpp_config.securityProfile() != 3) @@ -1001,7 +1001,7 @@ void ChargePoint::configurationValueChanged(const std::string& key) m_security_manager.logSecurityEvent(SECEVT_RECONFIG_SECURITY_PARAMETER, "AuthorizationKey"); } - else if (key == "SecurityProfile") + else if (key == "securityprofile") { // Reconnect with new profile LOG_INFO << "SecurityProfile modified, reconnect with new security profile"; diff --git a/src/chargepoint/config/ConfigManager.cpp b/src/chargepoint/config/ConfigManager.cpp index 1137c9a4..d55763e2 100644 --- a/src/chargepoint/config/ConfigManager.cpp +++ b/src/chargepoint/config/ConfigManager.cpp @@ -49,13 +49,15 @@ ConfigManager::~ConfigManager() { } /** @copydoc void registerCheckFunction(const std::string&, ConfigurationValueCheckFunc) */ void ConfigManager::registerCheckFunction(const std::string& key, ConfigurationValueCheckFunc func) { - m_specific_checks[key] = func; + auto lower_case_key = ocpp::helpers::tolower(key); + m_specific_checks[lower_case_key] = func; } /** @copydoc void IConfigManager::registerConfigChangedListener(const std::string&, IConfigChangedListener&) */ void ConfigManager::registerConfigChangedListener(const std::string& key, IConfigChangedListener& listener) { - m_listeners[key] = &listener; + auto lower_case_key = ocpp::helpers::tolower(key); + m_listeners[lower_case_key] = &listener; } /** @copydoc bool GenericMessageHandler::handleMessage(const RequestType& request, @@ -93,10 +95,11 @@ bool ConfigManager::handleMessage(const ocpp::messages::ChangeConfigurationReq& response.status = ConfigurationStatus::Accepted; // Specific check - auto it = m_specific_checks.find(request.key); + auto lower_case_key = ocpp::helpers::tolower(request.key); + auto it = m_specific_checks.find(lower_case_key); if (it != m_specific_checks.end()) { - response.status = it->second(request.key, request.value); + response.status = it->second(lower_case_key, request.value); } if (response.status == ConfigurationStatus::Accepted) { @@ -105,10 +108,10 @@ bool ConfigManager::handleMessage(const ocpp::messages::ChangeConfigurationReq& if (response.status == ConfigurationStatus::Accepted) { // Notify change - auto iter = m_listeners.find(request.key); + auto iter = m_listeners.find(lower_case_key); if (iter != m_listeners.end()) { - iter->second->configurationValueChanged(request.key); + iter->second->configurationValueChanged(lower_case_key); } } } diff --git a/src/tools/helpers/StringHelpers.cpp b/src/tools/helpers/StringHelpers.cpp index e05fcb95..6b89aa34 100644 --- a/src/tools/helpers/StringHelpers.cpp +++ b/src/tools/helpers/StringHelpers.cpp @@ -18,6 +18,7 @@ along with OpenOCPP. If not, see . #include "StringHelpers.h" +#include #include #include @@ -29,6 +30,14 @@ namespace helpers /** @brief Space */ const std::string SPACE_STRING = " "; +/** @brief Helper function to convert a string to lowercase */ +std::string tolower(const std::string& str) +{ + std::string ret = str; + std::transform(ret.begin(), ret.end(), ret.begin(), [](unsigned char c) { return std::tolower(c); }); + return ret; +} + /** @brief Helper function to trim a string */ std::string& trim(std::string& str, const std::string& chars) { diff --git a/src/tools/helpers/StringHelpers.h b/src/tools/helpers/StringHelpers.h index 63b37618..225e475f 100644 --- a/src/tools/helpers/StringHelpers.h +++ b/src/tools/helpers/StringHelpers.h @@ -31,6 +31,14 @@ namespace helpers /** @brief Space */ extern const std::string SPACE_STRING; +/** + * @brief Helper function to convert a string to lowercase + * /!\ Works only on ASCII strings /!\ + * @param str String to convert + * @return Reference to the converted string + */ +std::string tolower(const std::string& str); + /** * @brief Helper function to trim a string * @param str String to trim From dcc79b163e460d896544f5402c586605a03a7e5e Mon Sep 17 00:00:00 2001 From: c-jimenez <18682655+c-jimenez@users.noreply.github.com> Date: Sun, 10 Mar 2024 15:36:03 +0100 Subject: [PATCH 10/10] =?UTF-8?q?[version]=C2=A0Update=20version=20number?= =?UTF-8?q?=20=3D>=201.5.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51ffa9ed..f922e71d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.13) project(OpenOCPP DESCRIPTION "Open Source C++ implementation of the OCPP 1.6 protocol" - VERSION 1.4.3 + VERSION 1.5.0 ) # Definitions for Version.h file