diff --git a/src/lib/ebus/data.cpp b/src/lib/ebus/data.cpp index 29bd77a9..df57a4c1 100644 --- a/src/lib/ebus/data.cpp +++ b/src/lib/ebus/data.cpp @@ -100,6 +100,31 @@ static const char* dayNames[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" #define NULL_VALUE "-" #define MAX_POS 16 +/** + * @brief Parse an unsigned int value. + * @param str the string to parse. + * @param base the numerical base. + * @param minValue the minimum resulting value. + * @param maxValue the maximum resulting value. + * @param result the variable in which to store an error code when parsing failed or the value is out of bounds. + */ +unsigned int parseInt(const char* str, int base, const unsigned int minValue, const unsigned int maxValue, result_t& result) { + char* strEnd = NULL; + + unsigned int ret = strtoul(str, &strEnd, base); + + if (strEnd == NULL || *strEnd != 0) { + result = RESULT_ERR_INVALID_ARG; // invalid value + return 0; + } + + if (ret < minValue || ret > maxValue) { + result = RESULT_ERR_INVALID_ARG; // invalid value + return 0; + } + return ret; +} + result_t DataField::create(std::vector::iterator& it, const std::vector::iterator end, const std::map< std::string, DataField*> templates, @@ -168,18 +193,9 @@ result_t DataField::create(std::vector::iterator& it, if (++offsetCnt > 2) return RESULT_ERR_INVALID_ARG; //invalid pos definition - const char* start = token.c_str(); - char* end = NULL; - unsigned int pos = strtoul(start, &end, 10) - 1; // 1-based - if (end != start + strlen(start)) { - result = RESULT_ERR_INVALID_ARG; // invalid pos definition + unsigned int pos = parseInt(token.c_str(), 10, 1, MAX_POS + 1, result) - 1; // input is 1-based + if (result != RESULT_OK) break; - } - - if (pos > MAX_POS) { - result = RESULT_ERR_INVALID_ARG; // invalid pos definition - break; - } if (offsetCnt == 1) offset = pos; @@ -204,26 +220,22 @@ result_t DataField::create(std::vector::iterator& it, std::string divisorStr = *it++; if (divisorStr.empty() == false) { if (divisorStr.find_first_not_of("0123456789") == std::string::npos) { - const char* start = divisorStr.c_str(); - char* end = NULL; - divisor = strtoul(start, &end, 10); - if (end != start + strlen(start)) { - result = RESULT_ERR_INVALID_ARG; + divisor = parseInt(divisorStr.c_str(), 10, 1, 10000, result); + if (result != RESULT_OK) break; - } } else { std::istringstream stream(divisorStr); while (std::getline(stream, token, VALUE_SEPARATOR) != 0) { - const char* start = token.c_str(); - char* end = NULL; - unsigned int id = strtoul(start, &end, 10); - if (end == NULL || end == start || *end != '=') { + const char* str = token.c_str(); + char* strEnd = NULL; + unsigned int id = strtoul(str, &strEnd, 10); + if (strEnd == NULL || strEnd == str || *strEnd != '=') { result = RESULT_ERR_INVALID_ARG; break; } - values[id] = std::string(end + 1); + values[id] = std::string(strEnd + 1); } if (result != RESULT_OK) break; @@ -524,6 +536,7 @@ result_t StringDataField::writeSymbols(std::istringstream& input, incr = -1; } + result_t result; size_t i = 0; for (size_t offset = start; offset != end; offset += incr, i++) { switch (m_dataType.type) @@ -542,11 +555,10 @@ result_t StringDataField::writeSymbols(std::istringstream& input, if (input.eof() == true) return RESULT_ERR_INVALID_ARG; // invalid hex value - str = token.c_str(); - strEnd = NULL; - value = strtoul(str, &strEnd, 16); - if (strEnd != str + strlen(str)) - return RESULT_ERR_INVALID_ARG; // invalid hex value + result_t result; + value = parseInt(token.c_str(), 16, 0, 0xff, result); + if (result != RESULT_OK) + return result; // invalid hex value } break; case bt_dat: @@ -554,11 +566,9 @@ result_t StringDataField::writeSymbols(std::istringstream& input, continue; // skip weekday in between if (std::getline(input, token, '.') == 0) return RESULT_ERR_INVALID_ARG; // incomplete - str = token.c_str(); - strEnd = NULL; - value = strtoul(str, &strEnd, 10); - if (strEnd != str + strlen(str)) - return RESULT_ERR_INVALID_ARG; // invalid date part + value = parseInt(token.c_str(), 10, 0, 9999, result); + if (result != RESULT_OK) + return result; // invalid date part if (i + 1 == m_length && value >= 2000) value -= 2000; else if (value < 1 || (i == 0 && value > 31) || (i == 1 && value > 12)) @@ -567,12 +577,10 @@ result_t StringDataField::writeSymbols(std::istringstream& input, case bt_tim: if (std::getline(input, token, ':') == 0) return RESULT_ERR_INVALID_ARG; // incomplete - str = token.c_str(); - strEnd = NULL; - value = strtoul(str, &strEnd, 10); - if (strEnd != str + strlen(str)) - return RESULT_ERR_INVALID_ARG; // invalid time part - if ((i == 0 && value > 24) || (i > 0 && (value > 59 || ( last == 24 && value > 0) ))) + value = parseInt(token.c_str(), 10, 0, 59, result); + if (result != RESULT_OK) + return result; // invalid time part + if ((i == 0 && value > 24) || (i > 0 && ( last == 24 && value > 0) )) return RESULT_ERR_INVALID_ARG; // invalid time part if (m_length == 1) { // truncated time if (i == 0) { @@ -775,11 +783,10 @@ result_t NumberDataField::writeSymbols(std::istringstream& input, unsigned int value; const char* str = input.str().c_str(); - size_t len = strlen(str); if (strcasecmp(str, NULL_VALUE) == 0) // replacement value value = m_dataType.replacement; - else if (len == 0) + else if (str == NULL || *str == 0) return RESULT_ERR_INVALID_ARG; // input too short else { char* strEnd = NULL; @@ -793,13 +800,13 @@ result_t NumberDataField::writeSymbols(std::istringstream& input, } else value = strtoul(str, &strEnd, 10); - if (strEnd != str + len) + if (strEnd == NULL || *strEnd != 0) return RESULT_ERR_INVALID_ARG; // invalid value } else { char* strEnd = NULL; double dvalue = strtod(str, &strEnd); - if (strEnd != str + len) + if (strEnd == NULL || strEnd != 0) return RESULT_ERR_INVALID_ARG; // invalid value dvalue = round(dvalue * m_divisor); if ((m_dataType.flags & SIG) != 0) { diff --git a/src/lib/ebus/data.h b/src/lib/ebus/data.h index f16d7917..c47585c7 100644 --- a/src/lib/ebus/data.h +++ b/src/lib/ebus/data.h @@ -88,11 +88,11 @@ public: * @brief Factory method for creating new instances. * @param it the iterator to traverse for the definition parts. * @param end the iterator pointing to the end of the definition parts. - * @param templates a map of DataField templates to be referenced by name. + * @param templates a map of @a DataField templates to be referenced by name. * @param returnField the variable in which to store the created instance. * @param isSetMessage whether the field is part of a set message (default false). * @param dstAddress the destination bus address (default @a SYN for creating a template @a DataField). - * @return @a RESULT_OK on success, @a RESULT_ERR_EOF if the iterator is empty, or an error code. + * @return @a RESULT_OK on success, or an error code. * Note: the caller needs to free the created instance. */ static result_t create(std::vector::iterator& it, const std::vector::iterator end,