diff --git a/include/ocpp/common/cistring.hpp b/include/ocpp/common/cistring.hpp index 00b47cd2d..cdd70a091 100644 --- a/include/ocpp/common/cistring.hpp +++ b/include/ocpp/common/cistring.hpp @@ -17,10 +17,10 @@ template class CiString : public String { public: /// \brief Creates a string from the given \p data - CiString(const std::string& data) : String(data) { + CiString(const std::string& data, StringTooLarge to_large = StringTooLarge::Throw) : String(data, to_large) { } - CiString(const char* data) : String(data) { + CiString(const char* data, StringTooLarge to_large = StringTooLarge::Throw) : String(data, to_large) { } CiString(const CiString& data) : String(data.get()) { @@ -35,8 +35,8 @@ template class CiString : public String { CiString& operator=(CiString&&) = default; /// \brief CaseInsensitive string implementation only allows printable ASCII characters - bool is_valid(const std::string& data) { - for (const char& character : data) { + bool is_valid(std::string_view data) { + for (char character : data) { // printable ASCII starts at code 0x20 (space) and ends with code 0x7e (tilde) and 0xa (\n) if ((character < 0x20 || character > 0x7e) && character != 0xa) { throw std::runtime_error("CiString can only contain printable ASCII characters"); diff --git a/include/ocpp/common/string.hpp b/include/ocpp/common/string.hpp index f726faa9b..eaa71e2ea 100644 --- a/include/ocpp/common/string.hpp +++ b/include/ocpp/common/string.hpp @@ -11,25 +11,33 @@ namespace ocpp { +class StringConversionException : public std::runtime_error { + using std::runtime_error::runtime_error; +}; + +enum class StringTooLarge { + Throw, + Truncate +}; + /// \brief Contains a String impementation with a maximum length template class String { private: std::string data; - size_t length; + static constexpr size_t length = L; public: /// \brief Creates a string from the given \p data - String(const std::string& data) : length(L) { - this->set(data); + explicit String(const std::string& data, StringTooLarge to_large = StringTooLarge::Throw) { + this->set(data, to_large); } - String(const char* data) : length(L) { - this->set(data); + explicit String(const char* data, StringTooLarge to_large = StringTooLarge::Throw) { + this->set(data, to_large); } /// \brief Creates a string - String() : length(L) { - } + String() = default; /// \brief Provides a std::string representation of the string /// \returns a std::string @@ -38,21 +46,26 @@ template class String { } /// \brief Sets the content of the string to the given \p data - void set(const std::string& data) { - if (data.length() <= this->length) { - if (this->is_valid(data)) { - this->data = data; - } else { - throw std::runtime_error("String has invalid format"); + void set(const std::string& data, StringTooLarge to_large = StringTooLarge::Throw) { + std::string_view view = data; + if (view.length() > this->length) { + if (to_large == StringTooLarge::Throw) { + throw StringConversionException("String length (" + std::to_string(view.length()) + + ") exceeds permitted length (" + std::to_string(this->length) + ")"); } + // Truncate + view = view.substr(0, length); + } + + if (this->is_valid(view)) { + this->data = view; } else { - throw std::runtime_error("String length (" + std::to_string(data.length()) + - ") exceeds permitted length (" + std::to_string(this->length) + ")"); + throw StringConversionException("String has invalid format"); } } /// \brief Override this to check for a specific format - bool is_valid(const std::string& data) { + bool is_valid(std::string_view data) { (void)data; // not needed here return true; } diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 58d1996dc..5301f869f 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -832,7 +832,18 @@ void ChargePoint::message_callback(const std::string& message) { this->message_dispatcher->dispatch_call_error( CallError(MessageId("-1"), "RpcFrameworkError", e.what(), json({}))); const auto& security_event = ocpp::security_events::INVALIDMESSAGES; - this->security->security_event_notification_req(CiString<50>(security_event), CiString<255>(message), true, + this->security->security_event_notification_req(CiString<50>(security_event, StringTooLarge::Truncate), + CiString<255>(message, StringTooLarge::Truncate), true, + utils::is_critical(security_event)); + return; + } catch (const StringConversionException& e) { + this->logging->central_system("Unknown", message); + EVLOG_error << "JSON exception during reception of message: " << e.what(); + this->message_dispatcher->dispatch_call_error( + CallError(MessageId("-1"), "RpcFrameworkError", e.what(), json({}))); + const auto& security_event = ocpp::security_events::INVALIDMESSAGES; + this->security->security_event_notification_req(CiString<50>(security_event, StringTooLarge::Truncate), + CiString<255>(message, StringTooLarge::Truncate), true, utils::is_critical(security_event)); return; } catch (const EnumConversionException& e) { @@ -840,7 +851,8 @@ void ChargePoint::message_callback(const std::string& message) { auto call_error = CallError(MessageId("-1"), "FormationViolation", e.what(), json({})); this->message_dispatcher->dispatch_call_error(call_error); const auto& security_event = ocpp::security_events::INVALIDMESSAGES; - this->security->security_event_notification_req(CiString<50>(security_event), CiString<255>(message), true, + this->security->security_event_notification_req(CiString<50>(security_event, StringTooLarge::Truncate), + CiString<255>(message, StringTooLarge::Truncate), true, utils::is_critical(security_event)); return; }