diff --git a/ChangeLog b/ChangeLog index 256c62815..c1a39a9f2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ v1.14.5-snapshot =========== +Enhancements: +- #7183 Refactoring for v3 licenses + Bug fixes: - #7182 Remove unused TLS options from the GUI on Synergy Client =========== diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 0e00b03c2..837a41ac9 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -17,39 +17,26 @@ #include "SerialKey.h" -#include -#include -#include #include -#include -#include -#include #include +#include #include "SerialKeyEdition.h" - -using namespace std; -static std::string hexEncode (std::string const& str); +#include "SerialKeyParser.h" SerialKey::SerialKey(Edition edition): - m_userLimit(1), - m_warnTime(ULLONG_MAX), - m_expireTime(ULLONG_MAX), - m_edition(edition) + m_data(edition) { } -SerialKey::SerialKey(std::string serial) : - m_userLimit(1), - m_warnTime(0), - m_expireTime(0), - m_edition(kBasic) +SerialKey::SerialKey(const std::string& serial) : + m_data(serial) { - string plainText = decode(serial); - bool valid = false; - if (!plainText.empty()) { - valid = parse(plainText); + SerialKeyParser parser; + + if (parser.parse(serial)) { + m_data = parser.getData(); } - if (!valid) { + else { throw std::runtime_error ("Invalid serial key"); } } @@ -61,7 +48,7 @@ SerialKey::isExpiring(time_t currentTime) const if (isTemporary()) { unsigned long long currentTimeAsLL = static_cast(currentTime); - if ((m_warnTime <= currentTimeAsLL) && (currentTimeAsLL < m_expireTime)) { + if ((m_data.warnTime <= currentTimeAsLL) && (currentTimeAsLL < m_data.expireTime)) { result = true; } } @@ -76,7 +63,7 @@ SerialKey::isExpired(time_t currentTime) const if (isTemporary()) { unsigned long long currentTimeAsLL = static_cast(currentTime); - if (m_expireTime <= currentTimeAsLL) { + if (m_data.expireTime <= currentTimeAsLL) { result = true; } } @@ -87,13 +74,13 @@ SerialKey::isExpired(time_t currentTime) const bool SerialKey::isTrial() const { - return m_KeyType.isTrial(); + return m_data.keyType.isTrial(); } bool SerialKey::isTemporary() const { - return m_KeyType.isTemporary(); + return m_data.keyType.isTemporary(); } bool @@ -101,7 +88,7 @@ SerialKey::isValid() const { bool Valid = true; - if (!m_edition.isValid() || isExpired(::time(0))) + if (!m_data.edition.isValid() || isExpired(::time(nullptr))) { Valid = false; } @@ -112,46 +99,13 @@ SerialKey::isValid() const Edition SerialKey::edition() const { - return m_edition.getType(); + return m_data.edition.getType(); } -static std::string -hexEncode (std::string const& str) { - std::ostringstream oss; - for (size_t i = 0; i < str.size(); ++i) { - unsigned c = str[i]; - c %= 256; - oss << std::setfill('0') << std::hex << std::setw(2) - << std::uppercase; - oss << c; - } - return oss.str(); -} - -std::string +const std::string& SerialKey::toString() const { - std::ostringstream oss; - oss << "{"; - if (isTemporary()) { - if (isTrial()){ - oss << "v2;" << SerialKeyType::TRIAL << ";"; - } - else{ - oss << "v2;" << SerialKeyType::SUBSCRIPTION << ";"; - } - } else { - oss << "v1;"; - } - oss << m_edition.getName() << ";"; - oss << m_name << ";"; - oss << m_userLimit << ";"; - oss << m_email << ";"; - oss << m_company << ";"; - oss << (isTemporary() ? m_warnTime : 0) << ";"; - oss << (isTemporary() ? m_expireTime : 0); - oss << "}"; - return hexEncode(oss.str()); + return m_data.key; } time_t @@ -161,8 +115,8 @@ SerialKey::daysLeft(time_t currentTime) const unsigned long long const day = 60 * 60 * 24; unsigned long long currentTimeAsLL = static_cast(currentTime); - if (currentTimeAsLL < m_expireTime) { - timeLeft = m_expireTime - currentTimeAsLL; + if (currentTimeAsLL < m_data.expireTime) { + timeLeft = m_data.expireTime - currentTimeAsLL; } unsigned long long daysLeft = 0; @@ -177,7 +131,7 @@ SerialKey::getSpanLeft(time_t time) const int result{-1}; if (isTemporary() && !isExpired(time)){ - auto timeLeft = (m_expireTime - time) * 1000; + auto timeLeft = (m_data.expireTime - time) * 1000; if (timeLeft < INT_MAX){ result = static_cast(timeLeft); @@ -189,95 +143,3 @@ SerialKey::getSpanLeft(time_t time) const return result; } - -std::string -SerialKey::email() const -{ - return m_email; -} - -std::string -SerialKey::decode(const std::string& serial) -{ - static const char* const lut = "0123456789ABCDEF"; - string output; - size_t len = serial.length(); - if (len & 1) { - return output; - } - - output.reserve(len / 2); - for (size_t i = 0; i < len; i += 2) { - - char a = serial[i]; - char b = serial[i + 1]; - - const char* p = std::lower_bound(lut, lut + 16, a); - const char* q = std::lower_bound(lut, lut + 16, b); - - if (*q != b || *p != a) { - return output; - } - - output.push_back(static_cast(((p - lut) << 4) | (q - lut))); - } - - return output; -} - -bool -SerialKey::parse(std::string plainSerial) -{ - string parityStart = plainSerial.substr(0, 1); - string parityEnd = plainSerial.substr(plainSerial.length() - 1, 1); - - bool valid = false; - - // check for parity chars { and }, record parity result, then remove them. - if (parityStart == "{" && parityEnd == "}") { - plainSerial = plainSerial.substr(1, plainSerial.length() - 2); - - // tokenize serialised subscription. - vector parts; - std::string::size_type pos = 0; - bool look = true; - while (look) { - std::string::size_type start = pos; - pos = plainSerial.find(";", pos); - if (pos == string::npos) { - pos = plainSerial.length(); - look = false; - } - parts.push_back(plainSerial.substr(start, pos - start)); - pos += 1; - } - - if ((parts.size() == 8) - && (parts.at(0).find("v1") != string::npos)) { - // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} - m_edition.setType(parts.at(1)); - m_name = parts.at(2); - sscanf(parts.at(3).c_str(), "%d", &m_userLimit); - m_email = parts.at(4); - m_company = parts.at(5); - sscanf(parts.at(6).c_str(), "%lld", &m_warnTime); - sscanf(parts.at(7).c_str(), "%lld", &m_expireTime); - valid = true; - } - else if ((parts.size() == 9) - && (parts.at(0).find("v2") != string::npos)) { - // e.g.: {v2;trial;basic;Bob;1;email;company name;1398297600;1398384000} - m_KeyType.setKeyType(parts.at(1)); - m_edition.setType(parts.at(2)); - m_name = parts.at(3); - sscanf(parts.at(4).c_str(), "%d", &m_userLimit); - m_email = parts.at(5); - m_company = parts.at(6); - sscanf(parts.at(7).c_str(), "%lld", &m_warnTime); - sscanf(parts.at(8).c_str(), "%lld", &m_expireTime); - valid = true; - } - } - - return valid; -} diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index c4330d441..6fd23abdc 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -19,19 +19,14 @@ #include #include -#include "EditionType.h" -#include "SerialKeyType.h" +#include "SerialKeyData.h" #include "SerialKeyEdition.h" -#ifdef TEST_ENV -#include -#endif - class SerialKey { friend bool operator== (SerialKey const&, SerialKey const&); public: explicit SerialKey(Edition edition = kUnregistered); - explicit SerialKey(std::string serial); + explicit SerialKey(const std::string& serial); bool isExpiring(time_t currentTime) const; bool isExpired(time_t currentTime) const; @@ -40,45 +35,18 @@ public: bool isValid() const; time_t daysLeft(time_t currentTime) const; int getSpanLeft(time_t time = ::time(0)) const; - std::string email() const; Edition edition() const; - std::string toString() const; - - static std::string decode(const std::string& serial); + const std::string& toString() const; private: - bool parse(std::string plainSerial); + SerialKeyData m_data; -#ifdef TEST_ENV -private: - FRIEND_TEST(SerialKeyTests, parse_noParty_invalid); - FRIEND_TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid); - FRIEND_TEST(SerialKeyTests, parse_validV1Serial_valid); - FRIEND_TEST(SerialKeyTests, parse_validV2Serial_valid); -#endif - -private: - std::string m_name; - std::string m_email; - std::string m_company; - unsigned m_userLimit; - unsigned long long m_warnTime; - unsigned long long m_expireTime; - SerialKeyEdition m_edition; - SerialKeyType m_KeyType; }; inline bool operator== (SerialKey const& lhs, SerialKey const& rhs) { - return (lhs.m_name == rhs.m_name) && - (lhs.m_email == rhs.m_email) && - (lhs.m_company == rhs.m_company) && - (lhs.m_userLimit == rhs.m_userLimit) && - (lhs.m_warnTime == rhs.m_warnTime) && - (lhs.m_expireTime == rhs.m_expireTime) && - (lhs.m_edition == rhs.m_edition) && - (lhs.m_KeyType == rhs.m_KeyType); + return (lhs.m_data == rhs.m_data); } inline bool diff --git a/src/lib/shared/SerialKeyData.h b/src/lib/shared/SerialKeyData.h new file mode 100644 index 000000000..0c9cd7b24 --- /dev/null +++ b/src/lib/shared/SerialKeyData.h @@ -0,0 +1,60 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2022 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include "SerialKeyType.h" +#include "SerialKeyEdition.h" + +/** + * @brief The SerialKeyData struct + * This is DTO which stores key data + */ +struct SerialKeyData { + std::string key; // Encoded serial key as a string + SerialKeyEdition edition; // Serial key edition + SerialKeyType keyType; // Serial key type + unsigned long long warnTime = 0; // Warning time + unsigned long long expireTime = 0; // Expiration time + + /** + * @brief SerialKeyData constructor + * @param key encoded key + */ + explicit SerialKeyData(const std::string& key) : + key(key) + { + } + + /** + * @brief SerialKeyData default constructor + * @param key edition. Unregistered by default + */ + explicit SerialKeyData(Edition edition = kUnregistered) : + edition(edition) + { + } +}; + +inline bool +operator== (SerialKeyData const& lhs, SerialKeyData const& rhs) { + return (lhs.key == rhs.key) && + (lhs.warnTime == rhs.warnTime) && + (lhs.expireTime == rhs.expireTime) && + (lhs.edition == rhs.edition) && + (lhs.keyType == rhs.keyType); +} diff --git a/src/lib/shared/SerialKeyParser.cpp b/src/lib/shared/SerialKeyParser.cpp new file mode 100644 index 000000000..ca3af10f6 --- /dev/null +++ b/src/lib/shared/SerialKeyParser.cpp @@ -0,0 +1,155 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2022 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "SerialKeyParser.h" + +void +SerialKeyParser::setKey(const std::string& key) +{ + m_data.key = key; +} + +void +SerialKeyParser::setType(const std::string& type) +{ + m_data.keyType.setKeyType(type); +} + +void +SerialKeyParser::setEdition(const std::string& edition) +{ + m_data.edition.setType(edition); +} + +void +SerialKeyParser::setWarningTime(const std::string& warnTime) +{ + sscanf(warnTime.c_str(), "%lld", &m_data.warnTime); +} + +void +SerialKeyParser::setExpirationTime(const std::string& expTime) +{ + sscanf(expTime.c_str(), "%lld", &m_data.expireTime); +} + +std::string +SerialKeyParser::decode(const std::string& serial) const +{ + static const char* const lut = "0123456789ABCDEF"; + std::string output; + size_t len = serial.length(); + if (len & 1) { + return output; + } + + output.reserve(len / 2); + for (size_t i = 0; i < len; i += 2) { + + char a = serial[i]; + char b = serial[i + 1]; + + const auto p = std::lower_bound(lut, lut + 16, a); + const auto q = std::lower_bound(lut, lut + 16, b); + + if (*q != b || *p != a) { + return output; + } + + output.push_back(static_cast(((p - lut) << 4) | (q - lut))); + } + + return output; +} + +bool +SerialKeyParser::parse(const std::string& plainSerial) +{ + bool valid = false; + auto key = decode(plainSerial); + const auto parts = splitToParts(key); + + if ((parts.size() == 8) && (parts.at(0).find("v1") != std::string::npos)) { + setKey(plainSerial); + parseV1(parts); + valid = true; + } + else if ((parts.size() == 9) && (parts.at(0).find("v2") != std::string::npos)) { + setKey(plainSerial); + parseV2(parts); + valid = true; + } + + return valid; +} + +const SerialKeyData& +SerialKeyParser::getData() const +{ + return m_data; +} + +void +SerialKeyParser::parseV1(const std::vector& parts) +{ + // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} + setEdition(parts.at(1)); + setWarningTime(parts.at(6)); + setExpirationTime(parts.at(7)); +} + +void +SerialKeyParser::parseV2(const std::vector& parts) +{ + // e.g.: {v2;trial;basic;Bob;1;email;company name;1398297600;1398384000} + setType(parts.at(1)); + setEdition(parts.at(2)); + setWarningTime(parts.at(7)); + setExpirationTime(parts.at(8)); +} + +std::vector +SerialKeyParser::splitToParts(const std::string& plainSerial) const +{ + // tokenize serialised subscription. + std::vector parts; + + if (!plainSerial.empty()) { + std::string parityStart = plainSerial.substr(0, 1); + std::string parityEnd = plainSerial.substr(plainSerial.length() - 1, 1); + + // check for parity chars { and }, record parity result, then remove them. + if (parityStart == "{" && parityEnd == "}") { + const auto serialData = plainSerial.substr(1, plainSerial.length() - 2); + + std::string::size_type pos = 0; + bool look = true; + while (look) { + std::string::size_type start = pos; + pos = serialData.find(";", pos); + if (pos == std::string::npos) { + pos = serialData.length(); + look = false; + } + parts.push_back(serialData.substr(start, pos - start)); + pos += 1; + } + } + } + + return parts; +} diff --git a/src/lib/shared/SerialKeyParser.h b/src/lib/shared/SerialKeyParser.h new file mode 100644 index 000000000..f4e1e449c --- /dev/null +++ b/src/lib/shared/SerialKeyParser.h @@ -0,0 +1,101 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2022 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include "SerialKeyData.h" + +class SerialKeyParser +{ +public: + /** + * @brief ~SerialKeyParser destructor + */ + virtual ~SerialKeyParser(){} + /** + * @brief parse serial key + * @param plainSerial encoded serial key + * @return true if parsed + */ + virtual bool parse(const std::string& plainSerial); + + /** + * @brief getData getter for serial key data + * @return serial key data as DTO + */ + const SerialKeyData& getData() const; + + /** + * @brief setKey setter to set encoded key + * @param key encoded serial key + */ + void setKey(const std::string& key); + + /** + * @brief setType setter for serial type + * @param type serial key type + */ + void setType(const std::string& type); + + /** + * @brief setEdition setter for serial edition + * @param edition serial key edition + */ + void setEdition(const std::string& edition); + + /** + * @brief setWarningTime setter for warning time + * @param warnTime warning time for key as a string + */ + void setWarningTime(const std::string& warnTime); + + /** + * @brief setExpirationTime setter for expiration date + * @param expTime expiration data as a string + */ + void setExpirationTime(const std::string& expTime); + +private: + /** + * @brief decode serial key from encoded string + * @param encoded serial key as a string + * @return decoded serial key as a string + */ + std::string decode(const std::string& serial) const; + + /** + * @brief parseV1 parse serial key version 1 + * @param parts of serial key version 1 + */ + void parseV1(const std::vector& parts); + + /** + * @brief parseV2 parse serial key version 2 + * @param parts parts of serial key version 2 + */ + void parseV2(const std::vector& parts); + + /** + * @brief splitToParts splits decoded serial key to parts + * @param plainSerial decoded serial key as a string + * @return splitted serial key + */ + std::vector splitToParts(const std::string& plainSerial) const; + + SerialKeyData m_data{}; //serial key data +}; diff --git a/src/lib/shared/SerialKeyParserV3.cpp b/src/lib/shared/SerialKeyParserV3.cpp new file mode 100644 index 000000000..f7acb49b0 --- /dev/null +++ b/src/lib/shared/SerialKeyParserV3.cpp @@ -0,0 +1,8 @@ +#include "SerialKeyParserV3.h" + +bool +SerialKeyParserV3::parse(const std::string& plainSerial) +{ + setKey(plainSerial); + return true; +} diff --git a/src/lib/shared/SerialKeyParserV3.h b/src/lib/shared/SerialKeyParserV3.h new file mode 100644 index 000000000..3938a902c --- /dev/null +++ b/src/lib/shared/SerialKeyParserV3.h @@ -0,0 +1,30 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2022 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "SerialKeyParser.h" + +class SerialKeyParserV3 : public SerialKeyParser +{ +public: + /** + * @brief parse serial key + * @param plainSerial encoded serial key + * @return true if parsed + */ + bool parse(const std::string& plainSerial) override; +}; diff --git a/src/test/unittests/shared/SerialKeyTests.cpp b/src/test/unittests/shared/SerialKeyTests.cpp index b09629ee4..4cde4e01f 100644 --- a/src/test/unittests/shared/SerialKeyTests.cpp +++ b/src/test/unittests/shared/SerialKeyTests.cpp @@ -22,62 +22,6 @@ #include "test/global/gtest.h" -TEST(SerialKeyTests, decode_empty_returnEmptyString) -{ - std::string plainText = SerialKey::decode(""); - EXPECT_EQ(0, plainText.size()); -} - -TEST(SerialKeyTests, decode_invalidDigit_returnEmptyString) -{ - std::string plainText = SerialKey::decode("MOCKZ"); - EXPECT_EQ(0, plainText.size()); -} - -TEST(SerialKeyTests, decode_validSerial_returnPlainText) -{ - std::string plainText = SerialKey::decode("53796E6572677920726F636B7321"); - EXPECT_EQ("Synergy rocks!", plainText); -} - -TEST(SerialKeyTests, parse_noParty_invalid) -{ - SerialKey serial; - bool r = serial.parse("MOCK"); - EXPECT_FALSE(r); -} - -TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid) -{ - SerialKey serial; - bool r = serial.parse("{Synergy;Rocks}"); - EXPECT_FALSE(r); -} - -TEST(SerialKeyTests, parse_validV1Serial_valid) -{ - SerialKey serial; - bool r = serial.parse("{v1;basic;Bob;1;email;company name;0;86400}"); - EXPECT_EQ(true, r); - EXPECT_EQ(kBasic, serial.edition()); - EXPECT_FALSE(serial.isExpired(0)); - EXPECT_EQ(true, serial.daysLeft(0)); - EXPECT_FALSE(serial.isExpiring(1)); -} - -TEST(SerialKeyTests, parse_validV2Serial_valid) -{ - SerialKey serial; - bool r = serial.parse("{v2;trial;pro;Bob;1;email;company name;0;86400}"); - EXPECT_EQ(true, r); - EXPECT_EQ(kPro, serial.edition()); - EXPECT_FALSE(serial.isExpired(0)); - EXPECT_EQ(true, serial.daysLeft(0)); - EXPECT_EQ(true, serial.isExpiring(1)); - EXPECT_EQ(true, serial.isTrial()); - EXPECT_EQ(true, serial.isTemporary()); -} - TEST(SerialKeyTests, isExpiring_validV2TrialBasicSerial_returnFalse) { // {v2;trial;basic;Bob;1;email;company name;1;86400}