diff --git a/setup/license.txt b/setup/license.txt index cdfbc7cc..fc3220fa 100644 --- a/setup/license.txt +++ b/setup/license.txt @@ -1,15 +1,16 @@ -----BEGIN LICENSE----- -builder -Sourcetrail -BuildingLicense -Valid until 2020.1 +Product: Sourcetrail +Licensed to: builder +License type: buildlicense +Valid up to version: 2020.1 - -$9$AQAKttB/yiIxt1mfkCY8m/k2g8SgiiC23v2BnavARxJf6g1py6lJ -oYqmp8TK/6ThpE1eNESel+MxBZ5mExT1zp3265FSpUlhI2fsxbxjWvz -KJsVSGXgv95A/q2K/dQRjtfGuInsujs+FdqyWh18YAF5NRm/PsKQtvj -J7d6sKNi23LWEBm4WRhYVYweu0T+GAzyMhOQdx9RvEZnjEYKTUZoy2E -fm+El5WO4kx0Q2SHt+yVz9+SBJCWymGu9eb5R6YIZV/aIdhTd+jqhOR -N94H0BKZ7L+4kmhPlarR98caQWrrCla9z9tv5B80LyhpgFiTEfHTsag -3CLI0S9Ez0iqi/d9uGQ0y7UbVZr2CFvnLJ4W8FLRuIqyF7esfgSx7IZ -GFsQa/6uNHVA== +$9$AQAKHf3pw5lZp/tlQHb7HlYpXvtz77zOWfLC3PKPSaV9gsZtgb9Q +lLkQVn8+OSMma5EN/aK5Dzb30t+EKSBBRyJ43LZRUOsgfVSXO0MRNdu +w9WfVuBA0spZ2TtXLF1r4lR9F9XYu2uZ//xcQ3rqFnH8udn2Jakeu55 +CJz/slxam41ZRDVVJ6AIkfeUVkQdtTDLh+1bmuOZVPMBhkD3V5BHrgU +TgKYDiwl1HDMdhJIh7V0u/clGToZUL8aesGFr35HeaUKWWD9GeZDs2p +4PizzREzLwPvbwXMcHAxpNaKOPrk2uQzBlfCe0b+FzTNQvZEioPeHI1 +oS1uX3ZtWa99l/iVe7P24tJOmalR0ripNhqu8i6GXaFyBD5ES3c7cqQ +OHRKWqZA4ypw== -----END LICENSE----- + diff --git a/src/app/main.cpp b/src/app/main.cpp index 2e1c84dc..844a7456 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -133,11 +133,8 @@ int main(int argc, char *argv[]) } Version version( - // TODO: replace hard coded version -// VERSION_YEAR, -// VERSION_MINOR, - 2017, - 2, + VERSION_YEAR, + VERSION_MINOR, VERSION_COMMIT, GIT_COMMIT_HASH ); diff --git a/src/lib_license/License.cpp b/src/lib_license/License.cpp index 2e19ec88..7d2a7d76 100644 --- a/src/lib_license/License.cpp +++ b/src/lib_license/License.cpp @@ -33,110 +33,160 @@ namespace } } - License::License() : m_rng(std::make_shared()) + , m_user("") + , m_type("Private License") + , m_seats(0) + , m_expire("") { - m_lines[BEGIN_LICENSE_LINE] = BEGIN_LICENSE; - m_lines[SOURCETRAIL_LINE] = "Sourcetrail"; - m_lines[END_LICENSE_LINE] = END_LICENSE; } License::~License() { } -std::string License::getMessage() const +void License::createHeader( + const std::string& user, + const std::string& type, + const std::string& expiration, + uint seats +) { + if (user.empty() || type.empty() || expiration.empty()) + { + return; + } + m_user = user; + m_expire = expiration; + m_type = type; + m_seats = seats; +} + +std::string License::getMessage(bool withNewlines) const +{ + const std::string separator = (withNewlines ? "\n" : ""); std::string message = ""; - for (int i = OWNER_LINE; i < FIRST_SIGNATURE_LINE; ++i) - { - message += m_lines[i]; - if (m_lines[i].empty()) - { - return ""; - } - } + if ( m_user.empty() || m_type.empty() || m_expire.empty() || m_hashLine.empty()) + { + return ""; + } + + message += LicenseConstants::PRODUCT_STRING + separator; + message += LicenseConstants::LICENSED_TO_STRING + m_user + separator; + message += LicenseConstants::LICENSE_TYPE_STRING + m_type; + if (m_seats > 1) + { + message += " (" + std::to_string(m_seats) + " Seats)"; + } + else if (m_seats == 1) + { + message += " (1 Seat)"; + } + else if (m_type == LicenseConstants::TEST_LICENSE_STRING) + { + message += " (unlimited seats)"; + } + message += separator; + message += getExpireLine() + separator; + message += LicenseConstants::SEPARATOR_STRING + separator; + message += m_hashLine; return message; } -std::string License::getSignature() const +std::string License::getLine(std::istream& stream) { - std::string signatue = ""; - - if (!m_lines[LAST_SIGNATURE_LINE].empty()) + std::string line; + if(getline(stream, line, '\n')) { - for (int i = FIRST_SIGNATURE_LINE; i <= LAST_SIGNATURE_LINE; ++i) - { - signatue += m_lines[i]; - } + return trimWhiteSpaces(line); } - else + return ""; +} + +void License::setHashLine(const std::string &hash) +{ + if (!hash.empty()) { - signatue = ""; + m_hashLine = hash; } - - return signatue; } -std::string License::getLine(LICENSE_LINE line) const +std::string License::removeCaption(const std::string& line, const std::string& caption) const { - return m_lines[line]; + if (line.substr(0, caption.length()) == caption) + { + return line.substr(caption.length()); + } + return ""; } -void License::setLine(const LICENSE_LINE line, const std::string& value) +bool License::extractData(const std::string& data, LICENSE_LINE line) { - // only allow to set message lines - if (line < FIRST_SIGNATURE_LINE) - { - m_lines[line] = value; - } + switch ( line ) + { + case USER_LINE: + m_user = removeCaption(data, LicenseConstants::LICENSED_TO_STRING); + return !m_user.empty(); + case EXPIRE_LINE: + m_expire = removeCaption(data, LicenseConstants::VALID_UP_TO_STRING); + if (m_expire.empty()) + { + m_expire = removeCaption(data, LicenseConstants::VALID_UNTIL_STRING); + } + return !m_expire.empty(); + case TYPE_LINE: + setTypeAndSeats(removeCaption(data, LicenseConstants::LICENSE_TYPE_STRING)); + return !m_type.empty(); + default: + return false; + } } -std::string License::getVersionLineWithoutPrefix() const +bool License::isTestLicense() const { - return getLine(VERSION_LINE).substr(LicenseConstants::UNTIL_PREFIX.length()); + return m_type == LicenseConstants::TEST_LICENSE_STRING; } unsigned int License::getSeats() const { - std::string line = getLine(SEATS_LINE); - if (!line.empty()) - { - try - { - return std::stoi(line); - } - catch (std::invalid_argument e) - { - return 0; - } - } - return 0; + return m_seats; +} + +std::string License::getType() const +{ + return m_type; +} + +std::string License::getExpireLine() const +{ + return (m_type == LicenseConstants::TEST_LICENSE_STRING ? + LicenseConstants::VALID_UNTIL_STRING : + LicenseConstants::VALID_UP_TO_STRING) + + m_expire; } std::string License::getLicenseInfo() const { - std::string info = getLine(OWNER_LINE) + "\n"; + std::string info = m_user + "\n"; - const std::string typeString = getLine(TYPE_LINE); - info += typeString + "\n"; - info += getLine(VERSION_LINE) + "\n"; + info += m_type + "\n"; + info += getExpireLine() + "\n"; // get info depending on license type - if (isNonCommercialLicenseType(typeString)) + if (isNonCommercialLicenseType()) { info += "not registered for commercial development"; } - else if (typeString == LicenseConstants::TEST_LICENSE_STRING) + else if (m_type == LicenseConstants::TEST_LICENSE_STRING) { info += "unlimited Seats"; } - else if (getSeats() > 1) + else if (m_seats > 1) { - info += std::to_string(getSeats()) + " Seats"; + info += std::to_string(m_seats) + " Seats"; } else { @@ -146,11 +196,11 @@ std::string License::getLicenseInfo() const return info; } -bool License::isNonCommercialLicenseType(const std::string type) const +bool License::isNonCommercialLicenseType() const { for ( const std::string nonCommercialLicenseType : NON_COMMERCIAL_LICENSE_TYPES) { - if (type == nonCommercialLicenseType) + if (m_type == nonCommercialLicenseType) { return true; } @@ -158,11 +208,16 @@ bool License::isNonCommercialLicenseType(const std::string type) const return false; } +std::string License::getUser() const +{ + return m_user; +} + int License::getTimeLeft() const { const std::string dateText = "YYYY-MMM-DD"; - std::string dateString = getVersionLineWithoutPrefix(); + std::string dateString = m_expire; if (dateString.length() != dateText.length()) { return -2; @@ -180,10 +235,7 @@ int License::getTimeLeft() const void License::writeToFile(const std::string& filename) { std::ofstream licenseFile(filename); - for(std::string line : m_lines) - { - licenseFile << line << std::endl; - } + licenseFile << getLicenseString() << std::endl; } bool License::loadFromString(const std::string& licenseText) @@ -194,32 +246,93 @@ bool License::loadFromString(const std::string& licenseText) bool License::load(std::istream& stream) { - int currentLine = 1; std::string line; + std::array lines; - while (getline(stream, line, '\n') && currentLine < END_LICENSE_LINE) + line = getLine(stream); + if (line == LicenseConstants::BEGIN_LICENSE_STRING) { - std::string l = trimWhiteSpaces(line); - if (l == BEGIN_LICENSE) - { - continue; - } - if (l.size()) - { - m_lines[currentLine] = l; - } - currentLine++; + line = getLine(stream); } - if (m_lines.size() <= 0) + // sourcetrail line + if (line != LicenseConstants::PRODUCT_STRING) + { + return false; + } + + // user line + if (!extractData(getLine(stream), USER_LINE)) + { + return false; + } + + if (!extractData(getLine(stream), TYPE_LINE)) + { + return false; + } + + // expire line + if (!extractData(getLine(stream), EXPIRE_LINE)) + { + return false; + } + + // separator line + getline(stream, line, '\n'); + if (trimWhiteSpaces(line) != LicenseConstants::SEPARATOR_STRING) + { + return false; + } + + // hash line + m_hashLine = getLine(stream); + + // signature + m_signature = ""; + while (getline(stream, line, '\n')) + { + std::string l = trimWhiteSpaces(line); + if (l == LicenseConstants::END_LICENSE_STRING) + { + break; + } + if (l.size()) + { + m_signature += l; + } + } + if (m_signature.length() != 344) { - std::cout << "Could not read licence, possible empty string or only white spaces" << std::endl; return false; } return true; } +void License::setTypeAndSeats(const std::string& line) +{ + size_t pos = line.find("("); + + if ( pos != line.npos) + { + m_type = line.substr(0, pos - 1); + try + { + m_seats = std::stoi(line.substr(pos+1)); + } + catch (std::invalid_argument e) + { + m_seats = 0; + } + } + else + { + m_type = line; + m_seats = 0; + } +} + bool License::loadFromFile(const std::string& filename) { std::ifstream sigfile(filename); @@ -231,7 +344,6 @@ void License::print() std::cout << getLicenseString(); } - bool License::isValid() const { if(!m_publicKey) @@ -241,15 +353,13 @@ bool License::isValid() const } try { - - std::string signatureString = getSignature(); - if (signatureString.size() <= 0) + if (m_signature.size() <= 0) { std::cout << "Could not read signature" << std::endl; return false; } - Botan::secure_vector signature = Botan::base64_decode(signatureString); + Botan::secure_vector signature = Botan::base64_decode(m_signature); if (m_publicKey == NULL) { @@ -282,15 +392,14 @@ bool License::isValid() const bool License::isExpired() const { - std::string expire = getVersionLineWithoutPrefix(); - if ( getLine(TYPE_LINE) == LicenseConstants::TEST_LICENSE_STRING) + if ( getType() == LicenseConstants::TEST_LICENSE_STRING) { return (getTimeLeft()==-1); } else { - Version version = Version::fromShortString(expire); - return Version::getApplicationVersion() > version; + Version version = Version::fromShortString(m_expire); + return Version::getApplicationVersion() > version; } } @@ -348,22 +457,15 @@ bool License::loadPublicKeyFromString(const std::string& publicKey) return true; } -void License::setVersion(const std::string& version) -{ - if(!version.empty()) - { - m_version = version; - } -} - std::string License::getLicenseString() const { - std::stringstream license; - for(std::string line : m_lines) - { - license << line << std::endl; - } - return license.str(); + std::string license = ""; + license += LicenseConstants::BEGIN_LICENSE_STRING + "\n"; + license += getMessage(true) + "\n"; + license += getSignature() + "\n"; + license += LicenseConstants::END_LICENSE_STRING + "\n"; + + return license; } std::string License::hashLocation(const std::string& location) @@ -484,20 +586,26 @@ std::string License::getEncodeKey(const std::string applicationLocation) const return pbkdf->derive_key(32, applicationLocation, &salt[0], salt.size(), 10000).as_string(); } +std::string License::getSignature() const +{ + std::string sig; + std::stringstream ss; + const int lineLength = 55; + for (size_t i = 0; i < m_signature.size(); i++) + { + if(i % lineLength == 0 && i != 0) + { + sig += ss.str() + "\n"; + ss.str(""); + } + ss << m_signature[i]; + } + + sig += ss.str(); + return sig; +} + void License::setSignature(const std::string& signature) { - std::stringstream ss; - int line = FIRST_SIGNATURE_LINE; - const int lineLength = 55; - for (size_t i = 0; i < signature.size(); i++) - { - if(i % lineLength == 0 && i != 0) - { - m_lines[line++] = ss.str(); - ss.str(""); - } - ss << signature[i]; - } - - m_lines[line] = ss.str(); + m_signature = signature; } diff --git a/src/lib_license/License.h b/src/lib_license/License.h index 032c9744..38d88756 100644 --- a/src/lib_license/License.h +++ b/src/lib_license/License.h @@ -14,8 +14,15 @@ namespace Botan } namespace LicenseConstants { - const std::string UNTIL_PREFIX = "Valid until "; + const std::string BEGIN_LICENSE_STRING = "-----BEGIN LICENSE-----"; + const std::string END_LICENSE_STRING = "-----END LICENSE-----"; const std::string TEST_LICENSE_STRING = "Test License"; + const std::string PRODUCT_STRING = "Product: Sourcetrail"; + const std::string LICENSED_TO_STRING = "Licensed to: "; + const std::string LICENSE_TYPE_STRING = "License type: "; + const std::string VALID_UNTIL_STRING = "Valid until: "; + const std::string VALID_UP_TO_STRING = "Valid up to version: "; + const std::string SEPARATOR_STRING = "-"; const int MINOR_VERSIONS_PER_YEAR = 4; } @@ -25,11 +32,11 @@ public: enum LICENSE_LINE { BEGIN_LICENSE_LINE = 0, - OWNER_LINE = 1, - SOURCETRAIL_LINE = 2, + SOURCETRAIL_LINE = 1, + USER_LINE = 2, TYPE_LINE = 3, - VERSION_LINE = 4, - SEATS_LINE=5, + EXPIRE_LINE = 4, + SEPARATOR_LINE=5, HASH_LINE = 6, FIRST_SIGNATURE_LINE = 7, LAST_SIGNATURE_LINE = 13, @@ -40,18 +47,31 @@ public: License(); ~License(); - std::string getMessage() const; + std::string getMessage(bool withNewlines = false) const; std::string getSignature() const; std::string getVersionLineWithoutPrefix() const; - std::string getLine(LICENSE_LINE line) const; + std::string getLine(std::istream& stream); + std::string getLineWithoutDescription(LICENSE_LINE line) const; void setLine(const LICENSE_LINE line, const std::string& value); + void setHashLine(const std::string& hash); + void setTypeAndSeats(const std::string& line); std::string getLicenseInfo() const; std::string getPublicKeyFilename() const; std::string getVersion() const; + std::string getType() const; + std::string getUser() const; unsigned int getSeats() const; + void createHeader( + const std::string& user, + const std::string& type, + const std::string& expiration, + uint seats = 0 + ); + std::string getExpireLine() const; + /// if Test License return >=0 or -1 if expired /// for non Test License -2 int getTimeLeft() const; @@ -69,11 +89,11 @@ public: bool loadPublicKeyFromFile(const std::string&); bool loadPublicKeyFromString(const std::string&); - void setVersion(const std::string&); void setSignature(const std::string&); bool isValid() const; bool isExpired() const; + bool isTestLicense() const; void print(); @@ -81,23 +101,26 @@ public: static bool checkLocation(const std::string&, const std::string&); private: - void createMessage(const std::string& user, const std::string& version, const std::string& type = 0); - void pushTodaysVersion(); - void pushVersionLine(int year, int minorVersion); std::string getEncodeKey(const std::string applicationLocation) const; + bool extractData(const std::string& string, LICENSE_LINE line); + std::string removeCaption(const std::string& line, const std::string& caption) const; - bool isNonCommercialLicenseType(const std::string type) const; + bool isNonCommercialLicenseType() const; - std::string m_version; std::string m_publicKeyFilename; std::shared_ptr m_publicKey; -// std::vector m_lines; - std::array m_lines; - std::shared_ptr m_rng; + + std::shared_ptr m_rng; + // message + std::string m_user; + std::string m_type; + uint m_seats; + std::string m_expire; + std::string m_hashLine; + + std::string m_signature; const std::string KEY_FILEENDING = ".pem"; - const std::string BEGIN_LICENSE = "-----BEGIN LICENSE-----"; - const std::string END_LICENSE = "-----END LICENSE-----"; const std::vector NON_COMMERCIAL_LICENSE_TYPES = { { "Private/Academic Single User License" } }; }; diff --git a/src/license_generator/Generator.cpp b/src/license_generator/Generator.cpp index 37256d7e..ed2603f2 100644 --- a/src/license_generator/Generator.cpp +++ b/src/license_generator/Generator.cpp @@ -238,31 +238,18 @@ Botan::RSA_PrivateKey *Generator::getPrivateKey() const void Generator::createLicense( const std::string& user, - const std::string& type, + const std::string& type, const std::string& expiration, const unsigned int seats ) { m_license = std::unique_ptr(new License()); - m_license->setLine(License::OWNER_LINE, user); - m_license->setLine(License::TYPE_LINE, type); - m_license->setLine(License::VERSION_LINE, LicenseConstants::UNTIL_PREFIX + expiration); - if (seats > 1) - { - m_license->setLine(License::SEATS_LINE, std::to_string(seats) + " Seats"); - } - else if (seats == 1) - { - m_license->setLine(License::SEATS_LINE, "1 Seat"); - } - else - { - m_license->setLine(License::SEATS_LINE, "-"); - } + m_license->createHeader(user, type, expiration, seats); + Botan::AutoSeeded_RNG rng; - std::string pass9 = Botan::generate_passhash9(m_license->getLine(License::VERSION_LINE), m_rng); - m_license->setLine(License::HASH_LINE, pass9); + std::string pass9 = Botan::generate_passhash9(m_license->getExpireLine(), m_rng); + m_license->setHashLine(pass9); //encode message const std::string emsa = "EMSA4(SHA-256)"; diff --git a/src/license_generator/Generator.h b/src/license_generator/Generator.h index 01e3cfc9..8fe44a26 100644 --- a/src/license_generator/Generator.h +++ b/src/license_generator/Generator.h @@ -43,7 +43,7 @@ public: Botan::RSA_PrivateKey* getPrivateKey() const; void createLicense( const std::string& user, - const std::string& type, + const std::string& type, const std::string& expiration, const unsigned int seates ); diff --git a/src/test/GeneratorTestSuite.h b/src/test/GeneratorTestSuite.h index 2117fbdf..d228cb68 100644 --- a/src/test/GeneratorTestSuite.h +++ b/src/test/GeneratorTestSuite.h @@ -82,17 +82,17 @@ public: license.loadFromString(generator.encodeLicense("TestUser", "Private License", 1, "2017.4")); // license.create(generator.getPrivateKey(), "TestUser", "v2"); - std::string testInfo = "TestUser\nPrivate License\nValid until 2017.4\n1 Seat"; + std::string testInfo = "TestUser\nPrivate License\nValid up to version: 2017.4\n1 Seat"; TS_ASSERT_EQUALS(license.getLicenseInfo(), testInfo); // license.create(generator.getPrivateKey(), "TestUser", "v2", "Volume License", 20); license.loadFromString(generator.encodeLicense("TestUser", "Volume License", 20, "2017.3")); - testInfo = "TestUser\nVolume License\nValid until 2017.3\n20 Seats"; + testInfo = "TestUser\nVolume License\nValid up to version: 2017.3\n20 Seats"; TS_ASSERT_EQUALS(license.getLicenseInfo(), testInfo); // license.create(generator.getPrivateKey(), "TestUser", "v2", "Private/Academic Single User License", 20); license.loadFromString(generator.encodeLicense("TestUser", "Private/Academic Single User License", 0, "2018.1")); - testInfo = "TestUser\nPrivate/Academic Single User License\nValid until 2018.1\nnot registered for commercial development"; + testInfo = "TestUser\nPrivate/Academic Single User License\nValid up to version: 2018.1\nnot registered for commercial development"; TS_ASSERT_EQUALS(license.getLicenseInfo(), testInfo); } @@ -106,8 +106,8 @@ public: license.loadFromString(generator.encodeLicense("TestUser", "Private License", 20, "2017.3")); license.loadPublicKeyFromString(generator.getPublicKeyPEMFileAsString()); - TS_ASSERT_EQUALS(license.getLine(License::OWNER_LINE), "TestUser"); - TS_ASSERT_EQUALS(license.getLine(License::TYPE_LINE), "Private License"); + TS_ASSERT_EQUALS(license.getUser(), "TestUser"); + TS_ASSERT_EQUALS(license.getType(), "Private License"); TS_ASSERT(license.isValid()); }