diff --git a/CMakeLists.txt b/CMakeLists.txt index 38dff936..f2863384 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -349,6 +349,7 @@ set_property( TARGET ${LIB_CXX_PROJECT_NAME} PROPERTY INCLUDE_DIRECTORIES "${LIB_CXX_INCLUDE_PATHS}" + "${LIB_LICENSE_INCLUDE_PATHS}" "${LIB_UTILITY_INCLUDE_PATHS}" "${LIB_INCLUDE_PATHS}" ) diff --git a/bin/app/user/ApplicationSettings_template.xml b/bin/app/user/ApplicationSettings_template.xml index 7ad86d6a..5c3498b7 100644 --- a/bin/app/user/ApplicationSettings_template.xml +++ b/bin/app/user/ApplicationSettings_template.xml @@ -82,7 +82,6 @@ - diff --git a/bin/test/data/GeneratorTestSuite/private-v2.pem b/bin/test/data/LicenseGeneratorTestSuite/private-v2.pem similarity index 100% rename from bin/test/data/GeneratorTestSuite/private-v2.pem rename to bin/test/data/LicenseGeneratorTestSuite/private-v2.pem diff --git a/src/app/main.cpp b/src/app/main.cpp index 3f16a1eb..e91be77f 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -15,7 +15,6 @@ #include "QtCoreApplication.h" #include "utilityQt.h" #include "QtViewFactory.h" -#include "QtEulaWindow.h" #include "ApplicationSettings.h" #include "CommandLineParser.h" #include "ConsoleLogger.h" @@ -258,7 +257,7 @@ int main(int argc, char *argv[]) // check if already agreed to EULA ApplicationSettings* appSettings = ApplicationSettings::getInstance().get(); - if (appSettings->getAcceptedEulaVersion() < QtEulaWindow::EULA_VERSION) + if (appSettings->getAcceptedEulaVersion() < Application::EULA_VERSION) { if (!commandLineParser.acceptedEULA()) { @@ -282,7 +281,7 @@ int main(int argc, char *argv[]) } std::cout << "\nSourcetrail End User License Agreement accepted.\n" << std::endl; - appSettings->setAcceptedEulaVersion(QtEulaWindow::EULA_VERSION); + appSettings->setAcceptedEulaVersion(Application::EULA_VERSION); appSettings->save(); } diff --git a/src/lib/Application.cpp b/src/lib/Application.cpp index e889e597..342f523f 100644 --- a/src/lib/Application.cpp +++ b/src/lib/Application.cpp @@ -1,36 +1,36 @@ #include "Application.h" -#include "IDECommunicationController.h" -#include "NetworkFactory.h" -#include "DialogView.h" -#include "GraphViewStyle.h" -#include "MainView.h" -#include "ViewFactory.h" -#include "StorageCache.h" -#include "LicenseChecker.h" #include "ApplicationSettings.h" -#include "ProjectSettings.h" +#include "AppPath.h" #include "ColorScheme.h" +#include "DialogView.h" #include "FileSystem.h" -#include "SharedMemoryGarbageCollector.h" +#include "GraphViewStyle.h" +#include "IDECommunicationController.h" +#include "LicenseChecker.h" #include "logging.h" #include "LogManager.h" +#include "MainView.h" #include "MessageFilterErrorCountUpdate.h" #include "MessageFilterFocusInOut.h" #include "MessageFilterSearchAutocomplete.h" #include "MessageQueue.h" -#include "MessageForceEnterLicense.h" #include "MessageQuitApplication.h" #include "MessageStatus.h" +#include "NetworkFactory.h" +#include "ProjectSettings.h" +#include "SharedMemoryGarbageCollector.h" +#include "StorageCache.h" #include "TabId.h" #include "TaskManager.h" #include "TaskScheduler.h" #include "tracing.h" +#include "UpdateChecker.h" #include "UserPaths.h" #include "utilityString.h" #include "utilityUuid.h" #include "Version.h" -#include "UpdateChecker.h" +#include "ViewFactory.h" std::shared_ptr Application::s_instance; std::string Application::s_uuid; @@ -38,7 +38,17 @@ std::string Application::s_uuid; void Application::createInstance( const Version& version, ViewFactory* viewFactory, NetworkFactory* networkFactory ){ + bool hasGui = (viewFactory != nullptr); + Version::setApplicationVersion(version); + LicenseChecker::loadPublicKey(); + LicenseChecker::setEncodeKey(AppPath::getAppPath().str()); + + if (hasGui) + { + GraphViewStyle::setImpl(viewFactory->createGraphStyleImpl()); + } + loadSettings(); SharedMemoryGarbageCollector* collector = SharedMemoryGarbageCollector::createInstance(); @@ -51,7 +61,6 @@ void Application::createInstance( TaskManager::createScheduler(TabId::background()); MessageQueue::getInstance(); - bool hasGui = (viewFactory != nullptr); s_instance = std::shared_ptr(new Application(hasGui)); s_instance->m_storageCache = std::make_shared(); @@ -60,9 +69,6 @@ void Application::createInstance( { s_instance->m_mainView = viewFactory->createMainView(s_instance->m_storageCache.get()); s_instance->m_mainView->setup(); - s_instance->updateTitle(); - - GraphViewStyle::setImpl(viewFactory->createGraphStyleImpl()); } if (networkFactory != nullptr) @@ -121,7 +127,6 @@ void Application::loadStyle(const FilePath& colorSchemePath) Application::Application(bool withGUI) : m_hasGUI(withGUI) - , m_licenseType(MessageEnteredLicense::LICENSE_NONE) , m_lastLicenseCheck(TimeStamp::now()) { } @@ -207,21 +212,6 @@ void Application::handleMessage(MessageActivateWindow* message) } } -void Application::handleMessage(MessageEnteredLicense* message) -{ - MessageStatus(L"Found valid license key, unlocked application.").dispatch(); - - m_licenseType = message->type; - - updateTitle(); - loadSettings(); - - if (m_hasGUI) - { - m_mainView->refreshViews(); - } -} - void Application::handleMessage(MessageIndexingFinished* message) { logStorageStats(); @@ -241,11 +231,7 @@ void Application::handleMessage(MessageLoadProject* message) TRACE("app load project"); FilePath projectSettingsFilePath(message->projectSettingsFilePath); - if (m_hasGUI) - { - bool showStartWindow = projectSettingsFilePath.empty(); - m_mainView->loadWindow(showStartWindow); - } + loadWindow(projectSettingsFilePath.empty()); if (projectSettingsFilePath.empty()) { @@ -332,6 +318,8 @@ void Application::handleMessage(MessageRefreshUI* message) if (m_hasGUI) { + updateTitle(); + if (message->loadStyle) { loadStyle(ApplicationSettings::getInstance()->getColorSchemePath()); @@ -366,9 +354,9 @@ void Application::handleMessage(MessageWindowFocus* message) m_lastLicenseCheck = TimeStamp::now(); LicenseChecker::LicenseState state = LicenseChecker::checkCurrentLicense(); - if (state != LicenseChecker::LICENSE_VALID && state != LicenseChecker::LICENSE_MOVED) + if (state != LicenseChecker::LicenseState::VALID) { - MessageForceEnterLicense(state).dispatch(); + m_mainView->forceEnterLicense(LicenseChecker::getLicenseErrorForState(state)); } } } @@ -417,6 +405,54 @@ void Application::startMessagingAndScheduling() queue->startMessageLoopThreaded(); } +void Application::loadWindow(bool showStartWindow) +{ + if (!m_hasGUI) + { + return; + } + + if (!m_loadedWindow) + { + ApplicationSettings* appSettings = ApplicationSettings::getInstance().get(); + LicenseChecker::LicenseState state = LicenseChecker::LicenseState::VALID; + std::string licenseError; + + if (!appSettings->getNonCommercialUse()) + { + state = LicenseChecker::setCurrentLicenseStringEncoded(appSettings->getLicenseString()); + licenseError = LicenseChecker::getLicenseErrorForState(state); + + if (state == LicenseChecker::LicenseState::VALID) + { + MessageStatus(L"Found valid license key, unlocked application.").dispatch(); + } + else if (state == LicenseChecker::LicenseState::EMPTY) + { + licenseError = ""; + } + else if (state == LicenseChecker::LicenseState::MALFORMED) + { + licenseError = "Plese re-enter your license key."; + appSettings->setLicenseString(""); + appSettings->save(); + } + } + + updateTitle(); + + bool showEula = (appSettings->getAcceptedEulaVersion() < EULA_VERSION); + bool enterLicense = (state != LicenseChecker::LicenseState::VALID); + + m_mainView->loadWindow(showStartWindow, showEula, enterLicense, licenseError); + m_loadedWindow = true; + } + else if (!showStartWindow) + { + m_mainView->hideStartScreen(); + } +} + void Application::refreshProject(RefreshMode refreshMode) { if (m_project && checkSharedMemory()) @@ -487,16 +523,15 @@ void Application::updateTitle() { std::wstring title = L"Sourcetrail"; - switch (m_licenseType) + switch (LicenseChecker::getCurrentLicenseType()) { - case MessageEnteredLicense::LICENSE_TEST: + case LicenseType::TEST: title += L" [test]"; break; - case MessageEnteredLicense::LICENSE_NONE: - case MessageEnteredLicense::LICENSE_NON_COMMERCIAL: + case LicenseType::NON_COMMERCIAL: title += L" [non-commercial]"; break; - case MessageEnteredLicense::LICENSE_COMMERCIAL: + case LicenseType::COMMERCIAL: break; } @@ -519,7 +554,8 @@ bool Application::checkSharedMemory() std::wstring error = utility::decodeFromUtf8(SharedMemory::checkSharedMemory(getUUID())); if (error.size()) { - MessageStatus(L"Error on accessing shared memory. Indexing not possible. Please restart computer or run as admin: " + error, true).dispatch(); + MessageStatus(L"Error on accessing shared memory. Indexing not possible. " + "Please restart computer or run as admin: " + error, true).dispatch(); handleDialog( L"There was an error accessing shared memory on your computer: " + error + L"\n\n" "Project indexing is not possible. Please restart your computer or try running Sourcetrail as admin. If the " diff --git a/src/lib/Application.h b/src/lib/Application.h index 9130695f..39a8a3a7 100644 --- a/src/lib/Application.h +++ b/src/lib/Application.h @@ -7,7 +7,6 @@ #include "MessageListener.h" #include "MessageIndexingFinished.h" #include "MessageActivateWindow.h" -#include "MessageEnteredLicense.h" #include "MessageLoadProject.h" #include "MessageRefresh.h" #include "MessageRefreshUI.h" @@ -27,7 +26,6 @@ class ViewFactory; class Application : public MessageListener - , public MessageListener , public MessageListener , public MessageListener , public MessageListener @@ -36,6 +34,8 @@ class Application , public MessageListener { public: + static const int EULA_VERSION = 5; + static void createInstance(const Version& version, ViewFactory* viewFactory, NetworkFactory* networkFactory); static std::shared_ptr getInstance(); static void destroyInstance(); @@ -68,7 +68,6 @@ private: Application(bool withGUI = true); void handleMessage(MessageActivateWindow* message) override; - void handleMessage(MessageEnteredLicense* message) override; void handleMessage(MessageIndexingFinished* message) override; void handleMessage(MessageLoadProject* message) override; void handleMessage(MessageRefresh* message) override; @@ -79,6 +78,8 @@ private: FilePath migrateProjectSettings(const FilePath& projectSettingsFilePath) const; void startMessagingAndScheduling(); + void loadWindow(bool showStartWindow); + void refreshProject(RefreshMode refreshMode); void updateRecentProjects(const FilePath& projectSettingsFilePath); @@ -89,6 +90,8 @@ private: bool checkSharedMemory(); const bool m_hasGUI; + bool m_loadedWindow = false; + std::shared_ptr m_project; std::shared_ptr m_storageCache; @@ -97,7 +100,6 @@ private: std::shared_ptr m_ideCommunicationController; std::shared_ptr m_updateChecker; - MessageEnteredLicense::LicenseType m_licenseType; TimeStamp m_lastLicenseCheck; }; diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 228fa371..0f9a7fe6 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -495,12 +495,10 @@ add_files( utility/messaging/type/MessageClearStatusView.h utility/messaging/type/MessageCodeReference.h utility/messaging/type/MessageDeactivateEdge.h - utility/messaging/type/MessageEnteredLicense.h utility/messaging/type/MessageFind.h utility/messaging/type/MessageFlushUpdates.h utility/messaging/type/MessageFocusIn.h utility/messaging/type/MessageFocusOut.h - utility/messaging/type/MessageForceEnterLicense.h utility/messaging/type/MessageGraphNodeBundleSplit.h utility/messaging/type/MessageGraphNodeExpand.h utility/messaging/type/MessageGraphNodeHide.h @@ -655,7 +653,5 @@ add_files( LanguagePackage.h LanguagePackageManager.cpp LanguagePackageManager.h - LicenseChecker.cpp - LicenseChecker.h UpdateChecker.h ) diff --git a/src/lib/LicenseChecker.cpp b/src/lib/LicenseChecker.cpp deleted file mode 100644 index 83bfab21..00000000 --- a/src/lib/LicenseChecker.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include "LicenseChecker.h" - -#include "ApplicationSettings.h" -#include "AppPath.h" -#include "License.h" -#include "logging.h" -#include "utilityApp.h" - -std::string LicenseChecker::getCurrentLicenseString() -{ - License license; - bool isLoaded = license.loadFromEncodedString( - ApplicationSettings::getInstance()->getLicenseString(), AppPath::getAppPath().str()); - - if (isLoaded) - { - return license.getLicenseString(); - } - - return ""; -} - -void LicenseChecker::saveCurrentLicenseString(const std::string& licenseString) -{ - License license; - bool isLoaded = license.loadFromString(licenseString); - if (!isLoaded) - { - return; - } - - utility::saveLicense(&license); -} - -bool LicenseChecker::isCurrentLicenseValid() -{ - return checkCurrentLicense() == LICENSE_VALID; -} - -LicenseChecker::LicenseState LicenseChecker::checkCurrentLicense() -{ - ApplicationSettings* appSettings = ApplicationSettings::getInstance().get(); - if (appSettings == nullptr) - { - LOG_ERROR_STREAM(<< "Unable to retrieve app settings"); - return LICENSE_EMPTY; - } - - const FilePath appPath = AppPath::getAppPath(); - - if (appPath.empty()) - { - LOG_ERROR_STREAM(<< "Failed to retrieve app path"); - return LICENSE_EMPTY; - } - - std::string licenseString = appSettings->getLicenseString(); - if (licenseString.size() == 0) - { - LOG_ERROR_STREAM(<< "No license key available."); - return LICENSE_EMPTY; - } - - if (!License::checkLocation(appPath.getAbsolute().str(), appSettings->getLicenseCheck())) - { - LOG_ERROR_STREAM(<< "Application was moved, reenter license key."); - return LICENSE_MOVED; - } - - License license; - bool isLoaded = license.loadFromEncodedString(licenseString, appPath.str()); - if (!isLoaded) - { - LOG_ERROR_STREAM(<< "License is invalid or application was moved."); - return LICENSE_MOVED; - } - - return checkLicense(license); -} - -LicenseChecker::LicenseState LicenseChecker::checkLicenseString(const std::string& licenseString) -{ - if (licenseString.size() == 0) - { - return LICENSE_EMPTY; - } - - License license; - bool isLoaded = license.loadFromString(licenseString); - if (!isLoaded) - { - return LICENSE_MALFORMED; - } - - license.print(); - - return checkLicense(license); -} - -MessageEnteredLicense::LicenseType LicenseChecker::getCurrentLicenseType() -{ - License license; - bool isLoaded = license.loadFromEncodedString( - ApplicationSettings::getInstance()->getLicenseString(), AppPath::getAppPath().str()); - - if (!isLoaded) - { - return MessageEnteredLicense::LICENSE_NONE; - } - - LicenseState state = checkLicense(license); - if (state != LICENSE_VALID) - { - return MessageEnteredLicense::LICENSE_NONE; - } - else if (license.isTestLicense()) - { - return MessageEnteredLicense::LICENSE_TEST; - } - else if (license.isNonCommercialLicenseType()) - { - return MessageEnteredLicense::LICENSE_NON_COMMERCIAL; - } - - return MessageEnteredLicense::LICENSE_COMMERCIAL; -} - -MessageEnteredLicense::LicenseType LicenseChecker::getLicenseType(const std::string& licenseString) -{ - if (licenseString.size() == 0) - { - return MessageEnteredLicense::LICENSE_NONE; - } - - License license; - bool isLoaded = license.loadFromString(licenseString); - if (!isLoaded) - { - return MessageEnteredLicense::LICENSE_NONE; - } - - LicenseState state = checkLicense(license); - if (state != LICENSE_VALID) - { - return MessageEnteredLicense::LICENSE_NONE; - } - else if (license.isTestLicense()) - { - return MessageEnteredLicense::LICENSE_TEST; - } - else if (license.isNonCommercialLicenseType()) - { - return MessageEnteredLicense::LICENSE_NON_COMMERCIAL; - } - - return MessageEnteredLicense::LICENSE_COMMERCIAL; -} - -std::string LicenseChecker::getCurrentLicenseTypeString() -{ - // WARNING: Don't change these string. The server API relies on them. - switch (getCurrentLicenseType()) - { - case MessageEnteredLicense::LICENSE_NONE: - case MessageEnteredLicense::LICENSE_NON_COMMERCIAL: - return "private"; - case MessageEnteredLicense::LICENSE_TEST: - return "test"; - case MessageEnteredLicense::LICENSE_COMMERCIAL: - return "commercial"; - } - - return "private"; -} - -LicenseChecker::LicenseState LicenseChecker::checkLicense(License& license) -{ - if (license.isExpired()) - { - return LICENSE_EXPIRED; - } - - if (license.isValid()) - { - return LICENSE_VALID; - } - - return LICENSE_INVALID; -} diff --git a/src/lib/LicenseChecker.h b/src/lib/LicenseChecker.h deleted file mode 100644 index 90b944ca..00000000 --- a/src/lib/LicenseChecker.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef LICENSE_CHECKER_H -#define LICENSE_CHECKER_H - -#include "MessageEnteredLicense.h" - -class License; - -class LicenseChecker -{ -public: - enum LicenseState - { - LICENSE_EMPTY, - LICENSE_MOVED, - LICENSE_MALFORMED, - LICENSE_INVALID, - LICENSE_EXPIRED, - LICENSE_VALID - }; - - static std::string getCurrentLicenseString(); - static void saveCurrentLicenseString(const std::string& licenseString); - - static bool isCurrentLicenseValid(); - static LicenseState checkCurrentLicense(); - static LicenseState checkLicenseString(const std::string& licenseString); - - static MessageEnteredLicense::LicenseType getCurrentLicenseType(); - static MessageEnteredLicense::LicenseType getLicenseType(const std::string& licenseString); - - static std::string getCurrentLicenseTypeString(); - -private: - static LicenseState checkLicense(License& license); -}; - -#endif // LICENSE_CHECKER_H diff --git a/src/lib/component/view/MainView.h b/src/lib/component/view/MainView.h index f7efd39f..d8ea6a96 100644 --- a/src/lib/component/view/MainView.h +++ b/src/lib/component/view/MainView.h @@ -32,11 +32,12 @@ public: virtual void refreshView() = 0; - virtual void loadWindow(bool showStartWindow) = 0; + virtual void loadWindow(bool showStartWindow, bool showEULA, bool enterLicense, std::string licenseError) = 0; virtual void hideStartScreen() = 0; virtual void setTitle(const std::wstring& title) = 0; virtual void activateWindow() = 0; + virtual void forceEnterLicense(std::string licenseError) = 0; virtual void updateRecentProjectMenu() = 0; diff --git a/src/lib/settings/ApplicationSettings.cpp b/src/lib/settings/ApplicationSettings.cpp index 5ce763c3..d9f0a67b 100644 --- a/src/lib/settings/ApplicationSettings.cpp +++ b/src/lib/settings/ApplicationSettings.cpp @@ -53,20 +53,6 @@ bool ApplicationSettings::load(const FilePath& filePath, bool readOnly) "network/coati_port", "network/sourcetrail_port" )); - migrator.addMigration(3, std::make_shared( - [](const SettingsMigration* migration, Settings* settings) - { - License license; - bool isLoaded = license.loadFromEncodedString( - migration->getValueFromSettings(settings, "user/license/license", ""), AppPath::getAppPath().str()); - if (isLoaded && license.isValid() && license.isNonCommercialLicenseType()) - { - migration->removeValuesInSettings(settings, "user/license/license"); - migration->removeValuesInSettings(settings, "user/license/check"); - migration->setValueInSettings(settings, "user/license/non_commercial_use", true); - } - } - )); migrator.addMigration(4, std::make_shared( [](const SettingsMigration* migration, Settings* settings) { @@ -680,16 +666,6 @@ void ApplicationSettings::setLicenseString(const std::string& licenseString) setValue("user/license/license", licenseString); } -std::string ApplicationSettings::getLicenseCheck() const -{ - return getValue("user/license/check", ""); -} - -void ApplicationSettings::setLicenseCheck(const std::string& hash) -{ - setValue("user/license/check", hash); -} - bool ApplicationSettings::getNonCommercialUse() const { return getValue("user/license/non_commercial_use", false); diff --git a/src/lib/settings/ApplicationSettings.h b/src/lib/settings/ApplicationSettings.h index b7283f3c..73032288 100644 --- a/src/lib/settings/ApplicationSettings.h +++ b/src/lib/settings/ApplicationSettings.h @@ -191,9 +191,6 @@ public: std::string getLicenseString() const; void setLicenseString(const std::string& licenseString); - std::string getLicenseCheck() const; - void setLicenseCheck(const std::string& hash); - bool getNonCommercialUse() const; void setNonCommercialUse(bool nonCommercialUse); diff --git a/src/lib/utility/messaging/type/MessageEnteredLicense.h b/src/lib/utility/messaging/type/MessageEnteredLicense.h deleted file mode 100644 index e2cd408f..00000000 --- a/src/lib/utility/messaging/type/MessageEnteredLicense.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MESSAGE_ENTERED_LICENSE_H -#define MESSAGE_ENTERED_LICENSE_H - -#include "Message.h" - -class MessageEnteredLicense - : public Message -{ -public: - enum LicenseType - { - LICENSE_NONE, - LICENSE_TEST, - LICENSE_NON_COMMERCIAL, - LICENSE_COMMERCIAL - }; - - MessageEnteredLicense(LicenseType type) - : type(type) - { - setSendAsTask(false); - } - - static const std::string getStaticType() - { - return "MessageEnteredLicense"; - } - - const LicenseType type; -}; - -#endif // MESSAGE_ENTERED_LICENSE_H diff --git a/src/lib/utility/messaging/type/MessageForceEnterLicense.h b/src/lib/utility/messaging/type/MessageForceEnterLicense.h deleted file mode 100644 index e21375b8..00000000 --- a/src/lib/utility/messaging/type/MessageForceEnterLicense.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef MESSAGE_FORCE_ENTER_LICENSE_H -#define MESSAGE_FORCE_ENTER_LICENSE_H - -#include "LicenseChecker.h" -#include "Message.h" - -class MessageForceEnterLicense - : public Message -{ -public: - MessageForceEnterLicense(LicenseChecker::LicenseState state) - : state(state) - { - setSendAsTask(false); - } - - static const std::string getStaticType() - { - return "MessageForceEnterLicense"; - } - - const LicenseChecker::LicenseState state; -}; - -#endif // MESSAGE_FORCE_ENTER_LICENSE_H diff --git a/src/lib_gui/qt/view/QtMainView.cpp b/src/lib_gui/qt/view/QtMainView.cpp index a6d8d7c0..11dda326 100644 --- a/src/lib_gui/qt/view/QtMainView.cpp +++ b/src/lib_gui/qt/view/QtMainView.cpp @@ -98,12 +98,12 @@ void QtMainView::saveLayout() m_window->saveLayout(); } -void QtMainView::loadWindow(bool showStartWindow) +void QtMainView::loadWindow(bool showStartWindow, bool showEULA, bool enterLicense, std::string licenseError) { m_onQtThread( [=]() { - m_window->loadWindow(showStartWindow); + m_window->loadWindow(showStartWindow, showEULA, enterLicense, licenseError); } ); } @@ -163,6 +163,16 @@ void QtMainView::activateWindow() ); } +void QtMainView::forceEnterLicense(std::string licenseError) +{ + m_onQtThread( + [=]() + { + m_window->forceEnterLicense(licenseError); + } + ); +} + void QtMainView::updateRecentProjectMenu() { m_onQtThread( @@ -208,18 +218,6 @@ void QtMainView::clearBookmarksMenu() updateBookmarksMenu({}); } -void QtMainView::handleMessage(MessageForceEnterLicense* message) -{ - LicenseChecker::LicenseState state = message->state; - - m_onQtThread( - [=]() - { - m_window->forceEnterLicense(state); - } - ); -} - void QtMainView::handleMessage(MessageProjectEdit* message) { m_onQtThread( diff --git a/src/lib_gui/qt/view/QtMainView.h b/src/lib_gui/qt/view/QtMainView.h index a42ce52b..c7a9b534 100644 --- a/src/lib_gui/qt/view/QtMainView.h +++ b/src/lib_gui/qt/view/QtMainView.h @@ -10,7 +10,6 @@ #include "QtThreadedFunctor.h" #include "MessageListener.h" -#include "MessageForceEnterLicense.h" #include "MessageProjectEdit.h" #include "MessageProjectNew.h" @@ -19,7 +18,6 @@ class View; class QtMainView : public MainView - , public MessageListener , public MessageListener , public MessageListener { @@ -50,13 +48,14 @@ public: void loadLayout() override; void saveLayout() override; - void loadWindow(bool showStartWindow) override; + void loadWindow(bool showStartWindow, bool showEULA, bool enterLicense, std::string licenseError) override; void refreshView() override; void hideStartScreen() override; void setTitle(const std::wstring& title) override; void activateWindow() override; + void forceEnterLicense(std::string licenseError) override; void updateRecentProjectMenu() override; @@ -67,7 +66,6 @@ public: void clearBookmarksMenu() override; private: - void handleMessage(MessageForceEnterLicense* message) override; void handleMessage(MessageProjectEdit* message) override; void handleMessage(MessageProjectNew* message) override; diff --git a/src/lib_gui/qt/window/QtEulaWindow.h b/src/lib_gui/qt/window/QtEulaWindow.h index 84acdab5..32d8f689 100644 --- a/src/lib_gui/qt/window/QtEulaWindow.h +++ b/src/lib_gui/qt/window/QtEulaWindow.h @@ -7,9 +7,8 @@ class QtEulaWindow : public QtWindow { Q_OBJECT -public: - static const int EULA_VERSION = 5; +public: QtEulaWindow(QWidget* parent, bool forceAccept); QSize sizeHint() const override; diff --git a/src/lib_gui/qt/window/QtLicenseWindow.cpp b/src/lib_gui/qt/window/QtLicenseWindow.cpp index a7404dc5..ef23993d 100644 --- a/src/lib_gui/qt/window/QtLicenseWindow.cpp +++ b/src/lib_gui/qt/window/QtLicenseWindow.cpp @@ -85,9 +85,11 @@ void QtLicenseWindow::populateWindow(QWidget* widget) m_licenseText->setPlaceholderText( "-----BEGIN LICENSE-----\n" "Product: Sourcetrail\n" - "Licensed to:\n" + "License holder:\n" "License type:\n" - "Valid up to version:\n" + "Licensed number of users:\n" + "Licensed product version:\n" + "Licensed usage period:\n" "-\n" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n" @@ -152,45 +154,22 @@ void QtLicenseWindow::handleNext() if (m_commercialUse->isChecked()) { std::string licenseString = m_licenseText->toPlainText().toStdString(); - LicenseChecker::LicenseState state = LicenseChecker::checkLicenseString(licenseString); - std::string errorString; + std::string errorString = LicenseChecker::getLicenseErrorForState(state); - switch (state) + if (state == LicenseChecker::LicenseState::VALID) { - case LicenseChecker::LICENSE_EMPTY: - errorString = "No license key was entered."; - break; - case LicenseChecker::LICENSE_MOVED: - case LicenseChecker::LICENSE_MALFORMED: - errorString = "The entered license key is malformed."; - break; - case LicenseChecker::LICENSE_INVALID: - errorString = "The entered license key is invalid."; - break; - case LicenseChecker::LICENSE_EXPIRED: - errorString = "The entered license key is expired."; - break; - case LicenseChecker::LICENSE_VALID: - { - if (LicenseChecker::getLicenseType(licenseString) == MessageEnteredLicense::LICENSE_NON_COMMERCIAL) - { - errorString = - "The entered license key does not permit commercial use. You no longer need a license " - "key for non-commercial use. Please choose the non-commercial option below."; - break; - } + ApplicationSettings* appSettings = ApplicationSettings::getInstance().get(); - LicenseChecker::saveCurrentLicenseString(licenseString); - m_errorLabel->setText(" "); + LicenseChecker::setCurrentLicenseString(licenseString); - ApplicationSettings::getInstance()->setNonCommercialUse(false); - ApplicationSettings::getInstance()->save(); + appSettings->setLicenseString(LicenseChecker::getCurrentLicenseStringEncoded()); + appSettings->setNonCommercialUse(false); + appSettings->save(); - emit finished(); - return; - } + emit finished(); + return; } m_errorLabel->setText(errorString.c_str()); diff --git a/src/lib_gui/qt/window/QtMainWindow.cpp b/src/lib_gui/qt/window/QtMainWindow.cpp index 0d02ada9..e6493135 100644 --- a/src/lib_gui/qt/window/QtMainWindow.cpp +++ b/src/lib_gui/qt/window/QtMainWindow.cpp @@ -29,6 +29,7 @@ #include "QtStartScreen.h" #include "ApplicationSettings.h" #include "FileSystem.h" +#include "LicenseChecker.h" #include "logging.h" #include "MessageErrorsHelpMessage.h" #include "MessageHistoryRedo.h" @@ -39,7 +40,6 @@ #include "MessageBookmarkBrowse.h" #include "MessageBookmarkCreate.h" #include "MessageCodeReference.h" -#include "MessageEnteredLicense.h" #include "MessageFind.h" #include "MessageIndexingShowDialog.h" #include "MessageLoadProject.h" @@ -116,8 +116,7 @@ bool MouseReleaseFilter::eventFilter(QObject* obj, QEvent* event) QtMainWindow::QtMainWindow() - : m_loaded(false) - , m_historyMenu(nullptr) + : m_historyMenu(nullptr) , m_bookmarksMenu(nullptr) , m_showDockWidgetTitleBars(true) , m_windowStack(this) @@ -324,46 +323,20 @@ void QtMainWindow::loadDockWidgetLayout() } } -void QtMainWindow::loadWindow(bool showStartWindow) +void QtMainWindow::loadWindow(bool showStartWindow, bool showEULA, bool enterLicense, const std::string& licenseError) { - if (m_loaded) - { - if (!showStartWindow) - { - hideStartScreen(); - } - return; - } - - m_loaded = true; - - LicenseChecker::LicenseState state = LicenseChecker::checkCurrentLicense(); - bool licenseValid = (state == LicenseChecker::LICENSE_VALID); - - if (licenseValid) - { - MessageEnteredLicense(LicenseChecker::getCurrentLicenseType()).dispatch(); - } - - ApplicationSettings* appSettings = ApplicationSettings::getInstance().get(); - - if (state == LicenseChecker::LICENSE_MOVED) - { - appSettings->setLicenseString(""); - } - if (showStartWindow) { showStartScreen(); } - if (state != LicenseChecker::LICENSE_VALID && !appSettings->getNonCommercialUse()) + if (enterLicense) { - forceEnterLicense(state); + forceEnterLicense(licenseError); } #ifndef Q_OS_WIN - if (appSettings->getAcceptedEulaVersion() < QtEulaWindow::EULA_VERSION) + if (showEULA) { showEula(true); } @@ -387,7 +360,7 @@ void QtMainWindow::saveLayout() settings.setValue("DOCK_LOCATIONS", this->saveState()); } -void QtMainWindow::forceEnterLicense(LicenseChecker::LicenseState state) +void QtMainWindow::forceEnterLicense(const std::string& licenseError) { QtLicenseWindow* window = dynamic_cast(m_windowStack.getTopWindow()); if (window) @@ -404,14 +377,9 @@ void QtMainWindow::forceEnterLicense(LicenseChecker::LicenseState state) return; } - if (state == LicenseChecker::LICENSE_EXPIRED) + if (licenseError.size()) { - window->setErrorMessage("The license key is expired."); - } - else if (state != LicenseChecker::LICENSE_VALID && state != LicenseChecker::LICENSE_EMPTY) - { - window->clear(); - window->setErrorMessage("Please re-enter your license key."); + window->setErrorMessage(QString::fromStdString(licenseError)); } window->updateCloseButton("Quit"); @@ -620,7 +588,7 @@ void QtMainWindow::showEula(bool forceAccept) void QtMainWindow::acceptedEula() { - ApplicationSettings::getInstance()->setAcceptedEulaVersion(QtEulaWindow::EULA_VERSION); + ApplicationSettings::getInstance()->setAcceptedEulaVersion(Application::EULA_VERSION); ApplicationSettings::getInstance()->save(); setEnabled(true); @@ -654,7 +622,7 @@ void QtMainWindow::enteredLicense() m_windowStack.clearWindows(); - MessageEnteredLicense(LicenseChecker::getCurrentLicenseType()).dispatch(); + MessageRefreshUI().dispatch(); setEnabled(true); diff --git a/src/lib_gui/qt/window/QtMainWindow.h b/src/lib_gui/qt/window/QtMainWindow.h index fe08dfa2..feb230ff 100644 --- a/src/lib_gui/qt/window/QtMainWindow.h +++ b/src/lib_gui/qt/window/QtMainWindow.h @@ -8,11 +8,11 @@ #include #include "SearchMatch.h" -#include "LicenseChecker.h" #include "QtWindowsTaskbarButton.h" #include "QtWindowStack.h" class Bookmark; +class MessageBase; class QDockWidget; class View; @@ -73,9 +73,9 @@ public: void saveLayout(); void loadDockWidgetLayout(); - void loadWindow(bool showStartWindow); + void loadWindow(bool showStartWindow, bool showEULA, bool enterLicense, const std::string& licenseError); - void forceEnterLicense(LicenseChecker::LicenseState state); + void forceEnterLicense(const std::string& licenseError); void updateHistoryMenu(std::shared_ptr message); void clearHistoryMenu(); @@ -190,8 +190,6 @@ private: std::vector m_dockWidgets; - bool m_loaded; - QMenu* m_viewMenu; QAction* m_viewSeparator; diff --git a/src/lib_gui/qt/window/QtStartScreen.cpp b/src/lib_gui/qt/window/QtStartScreen.cpp index 14bea92a..2f69b907 100644 --- a/src/lib_gui/qt/window/QtStartScreen.cpp +++ b/src/lib_gui/qt/window/QtStartScreen.cpp @@ -8,11 +8,9 @@ #include #include "ApplicationSettings.h" -#include "AppPath.h" -#include "License.h" +#include "LicenseChecker.h" #include "MessageLoadProject.h" #include "ProjectSettings.h" -#include "PublicKey.h" #include "QtUpdateCheckerWidget.h" #include "QtNewsWidget.h" #include "ResourcePaths.h" @@ -146,9 +144,6 @@ void QtStartScreen::updateButtons() void QtStartScreen::setupStartScreen() { - License license; - license.loadFromEncodedString(ApplicationSettings::getInstance()->getLicenseString(), AppPath::getAppPath().str()); - setStyleSheet(utility::getStyleSheet(ResourcePaths::getGuiPath().concatenate(L"startscreen/startscreen.css")).c_str()); addLogo(); @@ -170,15 +165,15 @@ void QtStartScreen::setupStartScreen() col->addSpacing(15); - if (license.isValid()) + if (LicenseChecker::checkCurrentLicense() == LicenseChecker::LicenseState::VALID) { - std::string licenseString = license.getLicenseInfo(); + std::string licenseInfo = LicenseChecker::getCurrentLicense()->getLicenseInfo(); QLabel* licenseHeader = new QLabel("Licensed to:"); licenseHeader->setObjectName("boldLabel"); col->addWidget(licenseHeader); col->addSpacing(2); - QLabel* licenseLabel = new QLabel(licenseString.c_str()); + QLabel* licenseLabel = new QLabel(licenseInfo.c_str()); licenseLabel->setObjectName("textLabel"); col->addWidget(licenseLabel); } diff --git a/src/lib_gui/utility/utilityApp.cpp b/src/lib_gui/utility/utilityApp.cpp index 8712335a..d13d5898 100644 --- a/src/lib_gui/utility/utilityApp.cpp +++ b/src/lib_gui/utility/utilityApp.cpp @@ -224,32 +224,3 @@ std::string utility::getOsTypeString() } return "unknown"; } - -bool utility::saveLicense(const License* license) -{ - if (license == nullptr) - { - return false; - } - - if (license->isValid()) - { - ApplicationSettings* appSettings = ApplicationSettings::getInstance().get(); - if (appSettings == nullptr) - { - LOG_ERROR_STREAM(<< "Unable to retrieve app settings"); - return false; - } - - const FilePath appLocation = AppPath::getAppPath(); - appSettings->setLicenseString(license->getLicenseEncodedString(appLocation.str())); - appSettings->setLicenseCheck(license->hashLocation(appLocation.getAbsolute().str())); - appSettings->save(); - return true; - } - else - { - LOG_ERROR( "The entered license key is invalid."); - return false; - } -} diff --git a/src/lib_gui/utility/utilityApp.h b/src/lib_gui/utility/utilityApp.h index 816510df..fd0b2f1f 100644 --- a/src/lib_gui/utility/utilityApp.h +++ b/src/lib_gui/utility/utilityApp.h @@ -28,8 +28,6 @@ namespace utility OsType getOsType(); std::string getOsTypeString(); - - bool saveLicense(const License* license); } #endif // UTILITY_APP_H diff --git a/src/lib_license/CMakeLists.txt b/src/lib_license/CMakeLists.txt index 1e678239..d6f989ae 100644 --- a/src/lib_license/CMakeLists.txt +++ b/src/lib_license/CMakeLists.txt @@ -6,4 +6,7 @@ add_files( License.cpp License.h + LicenseChecker.cpp + LicenseChecker.h + LicenseType.h ) diff --git a/src/lib_license/License.cpp b/src/lib_license/License.cpp index 8de2eda7..a943e238 100644 --- a/src/lib_license/License.cpp +++ b/src/lib_license/License.cpp @@ -1,28 +1,18 @@ #include "License.h" #include -#include #include #include #include #include -#include -//#include #include #include -#include -#include -#include -#include -#include -#include -#include +// #include #include -#include +#include #include "Version.h" -#include "PublicKey.h" namespace { @@ -41,96 +31,7 @@ std::string toLowerCase(const std::string& in) return out; } -} - -License::License() - : m_rng(std::make_shared()) - , m_user("") - , m_type("Private License") - , m_numberOfUsers(0) - , m_createdWithSeats(false) - , m_expire("") -{ - loadPublicKey(); -} - -License::~License() -{ -} - -void License::createHeader( - const std::string& user, - const std::string& type, - const std::string& expiration, - bool expiresAtDate, - size_t numberOfUsers -) -{ - if (user.empty() || type.empty() || expiration.empty()) - { - return; - } - m_user = user; - m_expire = expiration; - m_expiresAtDate = expiresAtDate; - m_type = type; - m_numberOfUsers = numberOfUsers; -} - -std::string License::getMessage(bool withNewlines) const -{ - const std::string separator = (withNewlines ? "\n" : ""); - std::string message = ""; - - 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_numberOfUsers == 0) - { - message += (m_createdWithSeats ? " (unlimited seats)" : " (unlimited users)"); - } - else if (m_numberOfUsers == 1) - { - message += (m_createdWithSeats ? " (1 Seat)" : " (1 user)"); - } - else if (m_numberOfUsers > 1) - { - message += " (" + std::to_string(m_numberOfUsers) + (m_createdWithSeats ? " Seats)" : " users)"); - } - - message += separator; - message += getExpireLine() + separator; - message += LicenseConstants::SEPARATOR_STRING + separator; - message += m_hashLine; - - return message; -} - -std::string License::getLine(std::istream& stream) -{ - std::string line; - if(getline(stream, line, '\n')) - { - return trim(line); - } - return ""; -} - -void License::setHashLine(const std::string &hash) -{ - if (!hash.empty()) - { - m_hashLine = hash; - } -} - -std::string License::removeCaption(const std::string& line, const std::string& caption) const +std::string removeCaption(const std::string& line, const std::string& caption) { if (line.substr(0, caption.length()) == caption) { @@ -139,50 +40,20 @@ std::string License::removeCaption(const std::string& line, const std::string& c return ""; } -bool License::extractData(const std::string& data, LICENSE_LINE line) -{ - 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); - m_expiresAtDate = false; - if (m_expire.empty()) - { - m_expire = removeCaption(data, LicenseConstants::VALID_UNTIL_STRING); - m_expiresAtDate = true; - } - return !m_expire.empty(); - case TYPE_LINE: - setTypeAndNumberOfUsers(removeCaption(data, LicenseConstants::LICENSE_TYPE_STRING)); - return !m_type.empty(); - default: - return false; - } } -bool License::isTestLicense() const +License::License() + : m_rng(std::make_unique()) { - return m_type == LicenseConstants::TEST_LICENSE_STRING; } -bool License::isNonCommercialLicenseType() const +License::~License() { - for ( const std::string& nonCommercialLicenseType : NON_COMMERCIAL_LICENSE_TYPES) - { - if (m_type == nonCommercialLicenseType) - { - return true; - } - } - return false; } -size_t License::getNumberOfUsers() const +std::string License::getUser() const { - return m_numberOfUsers; + return m_user; } std::string License::getType() const @@ -190,18 +61,9 @@ std::string License::getType() const return m_type; } -std::string License::getExpireLine() const +size_t License::getNumberOfUsers() const { - return (m_expiresAtDate ? - LicenseConstants::VALID_UNTIL_STRING : - LicenseConstants::VALID_UP_TO_STRING) + m_expire; -} - -std::string License::getExpireLineUI() const -{ - return toLowerCase(m_expiresAtDate ? - LicenseConstants::VALID_UNTIL_STRING : - LicenseConstants::VALID_UP_TO_STRING) + m_expire; + return m_numberOfUsers; } std::string License::getLicenseInfo() const @@ -211,11 +73,7 @@ std::string License::getLicenseInfo() const info += m_type + "\n"; // get info depending on license type - if (isNonCommercialLicenseType()) - { - info += "not registered for commercial development\n"; - } - else if (m_numberOfUsers == 0) + if (m_numberOfUsers == 0) { info += "unlimited users\n"; } @@ -228,21 +86,135 @@ std::string License::getLicenseInfo() const info += "1 user\n"; } - info += getExpireLineUI(); + if (m_expirationVersion != LicenseConstants::VALID_UNLIMITED) + { + info += "valid up to version " + m_expirationVersion; + } + else if (m_expirationDate != LicenseConstants::VALID_UNLIMITED) + { + info += "valid until " + m_expirationDate; + } + else + { + info += "valid perpetually"; + } return info; } -std::string License::getUser() const +std::string License::getMessage(bool withNewlines) const { - return m_user; + if (m_user.empty() || m_type.empty() || m_expirationVersion.empty() || m_expirationDate.empty() || m_hashLine.empty()) + { + return ""; + } + + const std::string separator = (withNewlines ? "\n" : ""); + std::string message = LicenseConstants::PRODUCT + separator; + + if (m_isOldFormat) + { + message += LicenseConstants::LICENSED_TO_OLD + m_user + separator; + message += LicenseConstants::LICENSE_TYPE + m_type; + + if (m_numberOfUsers == 0) + { + message += (m_createdWithSeats ? " (unlimited seats)" : " (unlimited users)"); + } + else if (m_numberOfUsers == 1) + { + message += (m_createdWithSeats ? " (1 Seat)" : " (1 user)"); + } + else if (m_numberOfUsers > 1) + { + message += " (" + std::to_string(m_numberOfUsers) + (m_createdWithSeats ? " Seats)" : " users)"); + } + message += separator; + + if (m_expirationVersion != LicenseConstants::VALID_UNLIMITED) + { + message += LicenseConstants::VALID_UP_TO_OLD + m_expirationVersion + separator; + } + else if (m_expirationDate != LicenseConstants::VALID_UNLIMITED) + { + message += LicenseConstants::VALID_UNTIL_OLD + m_expirationDate + separator; + } + } + else + { + std::string s; + message += LicenseConstants::LICENSE_HOLDER + m_user + separator; + message += LicenseConstants::LICENSE_TYPE + m_type + separator; + + if (m_numberOfUsers > 0) + { + message += LicenseConstants::LICENSED_USERS + std::to_string(m_numberOfUsers) + separator; + } + else + { + message += s + LicenseConstants::LICENSED_USERS + LicenseConstants::VALID_UNLIMITED + separator; + } + + if (m_expirationVersion != LicenseConstants::VALID_UNLIMITED) + { + message += s + LicenseConstants::LICENSED_VERSION + LicenseConstants::VALID_UP_TO + m_expirationVersion + separator; + } + else + { + message += s + LicenseConstants::LICENSED_VERSION + LicenseConstants::VALID_UNLIMITED + separator; + } + + if (m_expirationDate != LicenseConstants::VALID_UNLIMITED) + { + message += s + LicenseConstants::LICENSED_PERIOD + LicenseConstants::VALID_UNTIL + m_expirationDate + separator; + } + else + { + message += s + LicenseConstants::LICENSED_PERIOD + LicenseConstants::VALID_UNLIMITED + separator; + } + } + + message += LicenseConstants::SEPARATOR + separator; + message += m_hashLine; + + return message; +} + +void License::setMessage( + const std::string& user, + const std::string& type, + size_t numberOfUsers, + const std::string& expirationVersion, + const std::string& expirationDate +){ + if (user.empty() || type.empty() || (numberOfUsers == 0 && expirationVersion.empty() && expirationDate.empty())) + { + return; + } + + m_user = user; + m_type = type; + m_numberOfUsers = numberOfUsers; + m_expirationVersion = expirationVersion.size() ? expirationVersion : LicenseConstants::VALID_UNLIMITED; + m_expirationDate = expirationDate.size() ? expirationDate : LicenseConstants::VALID_UNLIMITED; + m_hashLine = generateHash(user + type); +} + +const std::string& License::getSignature() const +{ + return m_signature; +} + +void License::setSignature(const std::string& signature) +{ + m_signature = signature; } int License::getTimeLeft() const { const std::string dateText = "YYYY-MMM-DD"; - std::string dateString = m_expire; + std::string dateString = m_expirationDate; if (dateString.length() != dateText.length()) { return -2; @@ -269,100 +241,6 @@ bool License::loadFromString(const std::string& licenseText) return load(license); } -bool License::load(std::istream& stream) -{ - std::string line; - std::array lines; - - line = getLine(stream); - if (line == LicenseConstants::BEGIN_LICENSE_STRING) - { - line = getLine(stream); - } - - // 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 (trim(line) != LicenseConstants::SEPARATOR_STRING) - { - return false; - } - - // hash line - m_hashLine = getLine(stream); - - // signature - m_signature = ""; - while (getline(stream, line, '\n')) - { - std::string l = trim(line); - if (l == LicenseConstants::END_LICENSE_STRING) - { - break; - } - if (l.size()) - { - m_signature += l; - } - } - if (m_signature.length() != 344) - { - return false; - } - - return true; -} - -void License::setTypeAndNumberOfUsers(const std::string& line) -{ - size_t pos = line.find("("); - - if (pos != line.npos) - { - m_type = line.substr(0, pos - 1); - try - { - m_numberOfUsers = std::stoi(line.substr(pos+1)); - } - catch (std::invalid_argument e) - { - m_numberOfUsers = 0; - } - - if (toLowerCase(line).find("seat") != line.npos) - { - m_createdWithSeats = true; - } - } - else - { - m_type = line; - m_numberOfUsers = 0; - } -} - bool License::loadFromFile(const std::string& filename) { std::ifstream sigfile(filename); @@ -374,293 +252,324 @@ void License::print() std::cout << getLicenseString(); } -bool License::isValid() const +bool License::isEmpty() const { - if (!m_publicKey) + if (m_user.empty() && m_type.empty() && + m_expirationVersion.empty() && m_expirationDate.empty() && + m_hashLine.empty() && m_signature.empty()) { - std::cout << "No public key loaded" << std::endl; - return false; - } - try - { - if (m_signature.empty()) - { - std::cout << "Could not read signature" << std::endl; - return false; - } - - Botan::secure_vector signature = Botan::base64_decode(m_signature); - - if (m_publicKey == NULL) - { - std::cout << "Public key is NULL" << std::endl; - return false; - } - - if (isExpired()) - { - return false; - } - - Botan::PK_Verifier verifier(*m_publicKey.get(), "EMSA4(SHA-256)"); - - Botan::DataSource_Memory in(getMessage()); - Botan::byte buffer[4096] = {0}; - while(size_t got = in.read(buffer, sizeof(buffer))) - { - verifier.update(buffer, got); // what does 'got' stand for? - } - - const bool ok = verifier.check_signature(signature); - if (!ok) - { - std::cout << "License check failed" << std::endl; - return false; - } - return ok; - } - catch(...) // invalid character, or something else... why not check what the exception is? - { - std::cout << "Invalid character in Licensekey" << std::endl; - } - return false; -} - -bool License::isExpired() const -{ - if (m_expiresAtDate) - { - return (getTimeLeft() == -1); - } - else - { - Version version = Version::fromString(m_expire); - return Version::getApplicationVersion().toShortVersion() > version; - } -} - -std::string License::getPublicKeyFilename() const -{ - if (m_publicKeyFilename.empty()) - { - return "public-sourcetrail" + KEY_FILEENDING; - } - return m_publicKeyFilename; -} - -bool License::loadPublicKeyFromFile(const std::string& filename) -{ - if (!filename.empty()) - { - m_publicKeyFilename = filename; - } - - if (boost::filesystem::exists(getPublicKeyFilename())) - { - Botan::RSA_PublicKey* rsaPublicKey = - dynamic_cast(Botan::X509::load_key(getPublicKeyFilename())); - - if (!rsaPublicKey) - { - std::cout << "The loaded key is not a RSA key" << std::endl; - return false; - } - - m_publicKey = std::shared_ptr(rsaPublicKey); return true; } return false; } -bool License::loadPublicKey() +bool License::isComplete() const { - Botan::DataSource_Memory in(PUBLIC_KEY); - Botan::RSA_PublicKey *rsaPublicKey = dynamic_cast(Botan::X509::load_key(in)); - - if (!rsaPublicKey) + if (m_user.empty() || m_type.empty() || + m_expirationVersion.empty() || m_expirationDate.empty() || + m_hashLine.empty() || m_signature.empty()) { - std::cout << "The loaded key is not a RSA key" << std::endl; return false; } - m_publicKey = std::shared_ptr(rsaPublicKey); return true; } -bool License::loadPublicKeyFromString(const std::string& publicKey) +bool License::isExpired() const { - if (publicKey.empty()) + if (m_expirationVersion != LicenseConstants::VALID_UNLIMITED) + { + Version version = Version::fromString(m_expirationVersion); + return Version::getApplicationVersion().toShortVersion() > version; + } + else if (m_expirationDate != LicenseConstants::VALID_UNLIMITED) + { + return (getTimeLeft() == -1); + } + else if (m_expirationVersion == LicenseConstants::VALID_UNLIMITED && + m_expirationDate == LicenseConstants::VALID_UNLIMITED) { - std::cout << "Public key is empty" << std::endl; return false; } - Botan::DataSource_Memory in(publicKey); - Botan::RSA_PublicKey *rsaPublicKey = dynamic_cast(Botan::X509::load_key(in)); - - if (!rsaPublicKey) - { - std::cout << "The loaded key is not a RSA key" << std::endl; - return false; - } - - m_publicKey = std::shared_ptr(rsaPublicKey); return true; } +bool License::isTestLicense() const +{ + return m_type == LicenseConstants::TEST_LICENSE; +} + std::string License::getLicenseString() const { + if (m_user.empty() || m_type.empty() || m_expirationVersion.empty() || m_expirationDate.empty()) + { + return ""; + } + + std::string signatureLines; + std::stringstream ss; + const int lineLength = 55; + for (size_t i = 0; i < m_signature.size(); i++) + { + if (i % lineLength == 0 && i != 0) + { + signatureLines += ss.str() + "\n"; + ss.str(""); + } + ss << m_signature[i]; + } + signatureLines += ss.str(); + std::string license = ""; - license += LicenseConstants::BEGIN_LICENSE_STRING; + license += LicenseConstants::BEGIN_LICENSE; license += "\n"; license += getMessage(true) + "\n"; - license += getSignature() + "\n"; - license += LicenseConstants::END_LICENSE_STRING; + license += signatureLines + "\n"; + license += LicenseConstants::END_LICENSE; license += "\n"; return license; } -std::string License::hashLocation(const std::string& location) const +std::string License::generateHash(const std::string& str) const { - if (m_rng == NULL || location.size() <= 0) + if (m_rng == NULL || str.size() <= 0) { return ""; } - return Botan::generate_passhash9(location, *(m_rng.get())); + return Botan::generate_passhash9(str, *(m_rng.get())); } -bool License::checkLocation(const std::string& location, const std::string& hash) +bool License::checkHash(const std::string& str, const std::string& hash) { - if (location.empty() || hash.empty()) + if (str.empty() || hash.empty()) { return true; } - return Botan::check_passhash9(location, hash); + return Botan::check_passhash9(str, hash); } -std::string License::getLicenseEncodedString(const std::string& applicationLocation) const +bool License::load(std::istream& stream) { - if (applicationLocation.empty()) + std::string line; + std::vector lines; + + while (getline(stream, line, '\n')) { - std::cout << "No application location was given" << std::endl; - return ""; + line = trim(line); + + if ((!lines.size() && line == LicenseConstants::BEGIN_LICENSE) || line == LicenseConstants::END_LICENSE) + { + continue; + } + + if (line.size()) + { + lines.push_back(line); + } } - Botan::AutoSeeded_RNG rng; - std::vector fileContents; - std::stringstream input(getLicenseString()); - - //prepare the license string to work with the botan cryptobox - while(input.good()) + if (lines.size() == 15) { - Botan::byte filebuffer[4096] = { 0 }; - input.read((char*)filebuffer, sizeof(filebuffer)); - size_t got = input.gcount(); // what does got stand for? - - fileContents.insert(fileContents.end(), filebuffer, filebuffer + got); + m_isOldFormat = false; + return loadFromLines(lines); } - - if(fileContents.size() <= 0) + else if (lines.size() == 13) { - std::cout << "Failed to read license string" << std::endl; - return ""; - } - - std::string result = - Botan::CryptoBox::encrypt(&fileContents[0], fileContents.size(), getEncodeKey(applicationLocation), rng); - - //remove Botan Cryptobox begin and end - //should not be in the application settings - const int leMagicNumberA = 40; - const int leMagicNumberB = 78; - - if (result.length() < leMagicNumberA + leMagicNumberB) - { - std::cout << "Invalid result" << std::endl; - return ""; - } - - result = result.substr(leMagicNumberA, result.length() - leMagicNumberB); - - return result; -} - -bool License::loadFromEncodedString(const std::string& encodedLicense, const std::string& applicationLocation) -{ - if (encodedLicense.empty()) - { - std::cout << "No license string given" << std::endl; - return false; - } - - if (applicationLocation.empty()) - { - std::cout << "No application location given" << std::endl; - return false; - } - - try - { - //add cryptobox begin and end to loaded string - std::string crypbobxInput = "-----BEGIN BOTAN CRYPTOBOX MESSAGE-----\n"; - crypbobxInput += encodedLicense; - crypbobxInput += "-----END BOTAN CRYPTOBOX MESSAGE-----"; - - //decrypt license - return loadFromString(Botan::CryptoBox::decrypt(crypbobxInput, getEncodeKey(applicationLocation))); - } - catch(...) - { - //loaded string from application settings is invalid - return false; + m_isOldFormat = true; + return loadFromLinesOld(lines); } return false; } -std::string License::getHashedLicense() const +bool License::loadFromLines(const std::vector& lines) { - return getEncodeKey("fakeKey"); -} - -std::string License::getEncodeKey(const std::string applicationLocation) const -{ - if (applicationLocation.empty()) + if (lines.size() != 15) { - std::cout << "No application location given" << std::endl; - return ""; + return false; } - Botan::PBKDF *pbkdf = Botan::get_pbkdf("PBKDF2(SHA-256)"); - Botan::secure_vector salt = Botan::base64_decode("34zA54n60v4CxjY5n20k3J40c976n690", 32); - Botan::AutoSeeded_RNG rng; - 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++) + // sourcetrail line + if (lines[0] != LicenseConstants::PRODUCT) { - if(i % lineLength == 0 && i != 0) + return false; + } + + // user line + m_user = removeCaption(lines[1], LicenseConstants::LICENSE_HOLDER); + if (m_user.empty()) + { + return false; + } + + // type line + m_type = removeCaption(lines[2], LicenseConstants::LICENSE_TYPE); + if (m_type.empty()) + { + return false; + } + + // number of users + std::string users = removeCaption(lines[3], LicenseConstants::LICENSED_USERS); + if (users != LicenseConstants::VALID_UNLIMITED) + { + try { - sig += ss.str() + "\n"; - ss.str(""); + m_numberOfUsers = std::stoi(users); } - ss << m_signature[i]; + catch (std::invalid_argument e) + { + m_numberOfUsers = 0; + return false; + } + } + else + { + m_numberOfUsers = 0; } - sig += ss.str(); - return sig; + // expiration version + m_expirationVersion = removeCaption(lines[4], LicenseConstants::LICENSED_VERSION); + if (m_expirationVersion != LicenseConstants::VALID_UNLIMITED) + { + m_expirationVersion = removeCaption(m_expirationVersion, LicenseConstants::VALID_UP_TO); + if (!Version::fromString(m_expirationVersion).isValid()) + { + return false; + } + } + + // expiration date + m_expirationDate = removeCaption(lines[5], LicenseConstants::LICENSED_PERIOD); + if (m_expirationDate != LicenseConstants::VALID_UNLIMITED) + { + m_expirationDate = removeCaption(m_expirationDate, LicenseConstants::VALID_UNTIL); + + if (m_expirationDate.size() != 11 || m_expirationDate[4] != '-' || m_expirationDate[8] != '-') + { + return false; + } + } + + // separator line + if (lines[6] != LicenseConstants::SEPARATOR) + { + return false; + } + + // hash line + m_hashLine = lines[7]; + if (m_hashLine.length() != 55) + { + return false; + } + + // signature + m_signature = lines[8] + lines[9] + lines[10] + lines[11] + lines[12] + lines[13] + lines[14]; + if (m_signature.length() != 344) + { + return false; + } + + return true; } -void License::setSignature(const std::string& signature) +bool License::loadFromLinesOld(const std::vector& lines) { - m_signature = signature; + if (lines.size() != 13) + { + return false; + } + + // sourcetrail line + if (lines[0] != LicenseConstants::PRODUCT) + { + return false; + } + + // user line + m_user = removeCaption(lines[1], LicenseConstants::LICENSED_TO_OLD); + if (m_user.empty()) + { + return false; + } + + // type line + { + std::string line = removeCaption(lines[2], LicenseConstants::LICENSE_TYPE); + size_t pos = line.find("("); + + if (pos != line.npos) + { + m_type = line.substr(0, pos - 1); + try + { + m_numberOfUsers = std::stoi(line.substr(pos+1)); + } + catch (std::invalid_argument e) + { + m_numberOfUsers = 0; + } + + if (toLowerCase(line).find("seat") != line.npos) + { + m_createdWithSeats = true; + } + } + else + { + m_type = line; + m_numberOfUsers = 0; + } + + if (m_type.empty()) + { + return false; + } + } + + // expire line + m_expirationVersion = removeCaption(lines[3], LicenseConstants::VALID_UP_TO_OLD); + m_expirationDate = removeCaption(lines[3], LicenseConstants::VALID_UNTIL_OLD); + if (m_expirationVersion.empty() && m_expirationDate.empty()) + { + return false; + } + + if (m_expirationVersion.empty()) + { + m_expirationVersion = LicenseConstants::VALID_UNLIMITED; + } + + if (m_expirationDate.empty()) + { + m_expirationDate = LicenseConstants::VALID_UNLIMITED; + } + + // separator line + if (lines[4] != LicenseConstants::SEPARATOR) + { + return false; + } + + // hash line + m_hashLine = lines[5]; + if (m_hashLine.length() != 55) + { + return false; + } + + // signature + m_signature = lines[6] + lines[7] + lines[8] + lines[9] + lines[10] + lines[11] + lines[12]; + if (m_signature.length() != 344) + { + return false; + } + + return true; } diff --git a/src/lib_license/License.h b/src/lib_license/License.h index 78703da9..61c94eab 100644 --- a/src/lib_license/License.h +++ b/src/lib_license/License.h @@ -4,130 +4,101 @@ #include #include #include -#include namespace Botan { - class RSA_PublicKey; - class RSA_PrivateKey; class AutoSeeded_RNG; } -namespace LicenseConstants { - const char BEGIN_LICENSE_STRING[] = "-----BEGIN LICENSE-----"; - const char END_LICENSE_STRING[] = "-----END LICENSE-----"; - const char TEST_LICENSE_STRING[] = "Test License"; - const char PRODUCT_STRING[] = "Product: Sourcetrail"; - const char LICENSED_TO_STRING[] = "Licensed to: "; - const char LICENSE_TYPE_STRING[] = "License type: "; - const char VALID_UNTIL_STRING[] = "Valid until: "; - const char VALID_UP_TO_STRING[] = "Valid up to version: "; - const char SEPARATOR_STRING[] = "-"; - const int MINOR_VERSIONS_PER_YEAR = 4; +namespace LicenseConstants +{ + const char BEGIN_LICENSE[] = "-----BEGIN LICENSE-----"; + const char END_LICENSE[] = "-----END LICENSE-----"; + + const char TEST_LICENSE[] = "Test License"; + + const char PRODUCT[] = "Product: Sourcetrail"; + + const char LICENSE_HOLDER[] = "License holder: "; + const char LICENSE_TYPE[] = "License type: "; + const char LICENSED_USERS[] = "Licensed number of users: "; + const char LICENSED_VERSION[] = "Licensed product version: "; + const char LICENSED_PERIOD[] = "Licensed usage period: "; + + const char VALID_UNTIL[] = "until "; + const char VALID_UP_TO[] = "up to "; + const char VALID_UNLIMITED[] = "unlimited"; + + const char LICENSED_TO_OLD[] = "Licensed to: "; + const char VALID_UNTIL_OLD[] = "Valid for: "; + const char VALID_UP_TO_OLD[] = "Valid up to version: "; + + const char SEPARATOR[] = "-"; } class License { public: - enum LICENSE_LINE - { - BEGIN_LICENSE_LINE = 0, - SOURCETRAIL_LINE = 1, - USER_LINE = 2, - TYPE_LINE = 3, - EXPIRE_LINE = 4, - SEPARATOR_LINE=5, - HASH_LINE = 6, - FIRST_SIGNATURE_LINE = 7, - LAST_SIGNATURE_LINE = 13, - END_LICENSE_LINE = 14, - LICENSE_LINES = 15 - }; - License(); ~License(); - std::string getMessage(bool withNewlines = false) const; - std::string getSignature() const; - std::string getVersionLineWithoutPrefix() 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 setTypeAndNumberOfUsers(const std::string& line); - - std::string getLicenseInfo() const; - std::string getPublicKeyFilename() const; - std::string getVersion() const; std::string getType() const; std::string getUser() const; size_t getNumberOfUsers() const; + std::string getLicenseInfo() const; - void createHeader( + std::string getMessage(bool withNewlines) const; + void setMessage( const std::string& user, const std::string& type, - const std::string& expiration, - bool expiresAtDate, - size_t numberOfUsers = 0 + size_t numberOfUsers, + const std::string& expirationVersion, + const std::string& expirationDate ); - std::string getExpireLine() const; - std::string getExpireLineUI() const; - /// if Test License return >=0 or -1 if expired - /// for non Test License -2 - int getTimeLeft() const; + const std::string& getSignature() const; + void setSignature(const std::string& signature); std::string getLicenseString() const; - std::string getLicenseEncodedString(const std::string& applicationLocation) const; - std::string getHashedLicense() const; void writeToFile(const std::string& filename); - bool load(std::istream& stream); + bool loadFromString(const std::string& licenseText); bool loadFromFile(const std::string& filename); - bool loadFromEncodedString(const std::string& encodedLicense, const std::string& applicationLocation); - bool loadPublicKey(); - - bool loadPublicKeyFromFile(const std::string&); - bool loadPublicKeyFromString(const std::string&); - - void setSignature(const std::string&); - - bool isValid() const; + bool isEmpty() const; + bool isComplete() const; bool isExpired() const; bool isTestLicense() const; - bool isNonCommercialLicenseType() const; + + // if Test License return >=0 or -1 if expired + // for non Test License -2 + int getTimeLeft() const; void print(); - std::string hashLocation(const std::string&) const; - static bool checkLocation(const std::string&, const std::string&); + std::string generateHash(const std::string& str) const; + static bool checkHash(const std::string& str, const std::string& hash); private: - std::string getEncodeKey(const std::string applicationLocation) const; - bool extractData(const std::string& data, LICENSE_LINE line); - std::string removeCaption(const std::string& line, const std::string& caption) const; + bool load(std::istream& stream); + bool loadFromLines(const std::vector& lines); + bool loadFromLinesOld(const std::vector& lines); - std::string m_publicKeyFilename; - std::shared_ptr m_publicKey; + std::unique_ptr m_rng; - std::shared_ptr m_rng; - // message std::string m_user; std::string m_type; - size_t m_numberOfUsers; - bool m_createdWithSeats; - std::string m_expire; - bool m_expiresAtDate; - std::string m_hashLine; + size_t m_numberOfUsers = 0; + bool m_createdWithSeats = false; + std::string m_expirationVersion; + std::string m_expirationDate; + + std::string m_hashLine; std::string m_signature; - const std::string KEY_FILEENDING = ".pem"; - - const std::vector NON_COMMERCIAL_LICENSE_TYPES = { { "Private/Academic Single User License" } }; + bool m_isOldFormat = false; }; #endif // SOURCETRAIL_LICENSE_H diff --git a/src/lib_license/LicenseChecker.cpp b/src/lib_license/LicenseChecker.cpp new file mode 100644 index 00000000..e50fde1d --- /dev/null +++ b/src/lib_license/LicenseChecker.cpp @@ -0,0 +1,336 @@ +#include "LicenseChecker.h" + +#include +#include + +#include +// #include +#include +#include +#include +#include +#include +#include +#include + +#include "License.h" +#include "PublicKey.h" + +std::string LicenseChecker::s_encodeKey; +std::unique_ptr LicenseChecker::s_publicKey; +std::unique_ptr LicenseChecker::s_currentLicense; + +void LicenseChecker::setEncodeKey(const std::string& key) +{ + if (key.empty()) + { + return; + } + + Botan::PBKDF *pbkdf = Botan::get_pbkdf("PBKDF2(SHA-256)"); + Botan::secure_vector salt = Botan::base64_decode("34zA54n60v4CxjY5n20k3J40c976n690", 32); + s_encodeKey = pbkdf->derive_key(32, key, &salt[0], salt.size(), 10000).as_string(); +} + +bool LicenseChecker::loadPublicKeyFromFile(std::string fileName) +{ + if (!boost::filesystem::exists(fileName)) + { + std::cout << "Public key file does not exist: " << fileName << std::endl; + return false; + } + + return createPublicKey(dynamic_cast(Botan::X509::load_key(fileName))); +} + +bool LicenseChecker::loadPublicKeyFromString(const std::string& publicKey) +{ + if (publicKey.empty()) + { + std::cout << "Public key is empty" << std::endl; + return false; + } + + Botan::DataSource_Memory in(publicKey); + return createPublicKey(dynamic_cast(Botan::X509::load_key(in))); +} + +bool LicenseChecker::loadPublicKey() +{ + return loadPublicKeyFromString(PUBLIC_KEY); +} + +const License* LicenseChecker::getCurrentLicense() +{ + return s_currentLicense.get(); +} + +std::string LicenseChecker::getCurrentLicenseString() +{ + if (s_currentLicense) + { + return s_currentLicense->getLicenseString(); + } + return ""; +} + +std::string LicenseChecker::getCurrentLicenseStringEncoded() +{ + if (!s_currentLicense) + { + std::cout << "No current license" << std::endl; + return ""; + } + + if (s_encodeKey.empty()) + { + std::cout << "No encode key" << std::endl; + return ""; + } + + Botan::AutoSeeded_RNG rng; + std::vector fileContents; + std::stringstream input(s_currentLicense->getLicenseString()); + + // prepare the license string to work with the botan cryptobox + while (input.good()) + { + Botan::byte filebuffer[4096] = { 0 }; + input.read((char*)filebuffer, sizeof(filebuffer)); + size_t got = input.gcount(); + + fileContents.insert(fileContents.end(), filebuffer, filebuffer + got); + } + + if (fileContents.size() <= 0) + { + std::cout << "Failed to read license string" << std::endl; + return ""; + } + + std::string result = Botan::CryptoBox::encrypt(&fileContents[0], fileContents.size(), s_encodeKey, rng); + + // remove Botan Cryptobox begin and end + // should not be in the application settings + const int cryptoboxBeginLength = 40; + const int cryptoboxEndLength = 38; + + if (result.length() < cryptoboxBeginLength + cryptoboxEndLength) + { + std::cout << "Invalid result" << std::endl; + return ""; + } + + result = result.substr(cryptoboxBeginLength, result.length() - (cryptoboxBeginLength + cryptoboxEndLength)); + + return result; +} + +LicenseChecker::LicenseState LicenseChecker::setCurrentLicenseString(const std::string& licenseStr) +{ + s_currentLicense = nullptr; + + if (licenseStr.empty()) + { + return LicenseState::EMPTY; + } + + s_currentLicense = std::make_unique(); + if (!s_currentLicense->loadFromString(licenseStr)) + { + s_currentLicense = nullptr; + return LicenseState::MALFORMED; + } + + return checkCurrentLicense(); +} + +LicenseChecker::LicenseState LicenseChecker::setCurrentLicenseStringEncoded(const std::string& licenseStr) +{ + s_currentLicense = nullptr; + + if (licenseStr.empty()) + { + return LicenseState::EMPTY; + } + + if (s_encodeKey.empty()) + { + std::cout << "No key available" << std::endl; + return LicenseState::EMPTY; + } + + s_currentLicense = std::make_unique(); + + try + { + // add cryptobox begin and end to loaded string + std::string cryptoboxInput = "-----BEGIN BOTAN CRYPTOBOX MESSAGE-----\n"; + cryptoboxInput += licenseStr; + cryptoboxInput += "-----END BOTAN CRYPTOBOX MESSAGE-----"; + + // decrypt license + if (!s_currentLicense->loadFromString(Botan::CryptoBox::decrypt(cryptoboxInput, s_encodeKey))) + { + s_currentLicense = nullptr; + return LicenseState::MALFORMED; + } + } + catch (...) + { + s_currentLicense = nullptr; + return LicenseState::MALFORMED; + } + + return checkCurrentLicense(); +} + +LicenseChecker::LicenseState LicenseChecker::checkLicense(const License& license) +{ + if (license.isEmpty()) + { + return LicenseState::EMPTY; + } + + if (!license.isComplete()) + { + return LicenseState::INCOMPLETE; + } + + std::string message = license.getMessage(false); + std::string signature = license.getSignature(); + if (message.empty() || signature.empty()) + { + return LicenseState::INCOMPLETE; + } + + if (!s_publicKey) + { + std::cout << "No public key loaded" << std::endl; + return LicenseState::INCOMPLETE; + } + + try + { + Botan::PK_Verifier verifier(*s_publicKey.get(), "EMSA4(SHA-256)"); + Botan::DataSource_Memory in(message); + Botan::byte buffer[4096] = { 0 }; + + while (size_t got = in.read(buffer, sizeof(buffer))) + { + verifier.update(buffer, got); + } + + Botan::secure_vector sig = Botan::base64_decode(signature); + if (!verifier.check_signature(sig)) + { + return LicenseState::INVALID; + } + } + catch (...) + { + std::cout << "Invalid character in Licensekey" << std::endl; + return LicenseState::MALFORMED; + } + + if (license.isExpired()) + { + return LicenseState::EXPIRED; + } + + return LicenseState::VALID; +} + +LicenseChecker::LicenseState LicenseChecker::checkLicenseString(const std::string& licenseString) +{ + if (licenseString.size() == 0) + { + return LicenseState::EMPTY; + } + + License license; + bool isLoaded = license.loadFromString(licenseString); + if (!isLoaded) + { + return LicenseState::MALFORMED; + } + + return checkLicense(license); +} + +LicenseChecker::LicenseState LicenseChecker::checkCurrentLicense() +{ + if (!s_currentLicense) + { + return LicenseState::EMPTY; + } + + return checkLicense(*s_currentLicense.get()); +} + +LicenseType LicenseChecker::getCurrentLicenseType() +{ + if (!s_currentLicense) + { + return LicenseType::NON_COMMERCIAL; + } + + LicenseState state = checkLicense(*s_currentLicense.get()); + if (state != LicenseState::VALID) + { + return LicenseType::NON_COMMERCIAL; + } + else if (s_currentLicense->isTestLicense()) + { + return LicenseType::TEST; + } + + return LicenseType::COMMERCIAL; +} + +std::string LicenseChecker::getCurrentLicenseTypeString() +{ + // WARNING: Don't change these strings. The analytics API on the server relies on them. + switch (getCurrentLicenseType()) + { + case LicenseType::NON_COMMERCIAL: + return "private"; + case LicenseType::TEST: + return "test"; + case LicenseType::COMMERCIAL: + return "commercial"; + } + + return "private"; +} + +std::string LicenseChecker::getLicenseErrorForState(LicenseState state) +{ + switch (state) + { + case LicenseState::EMPTY: + return "No license key was entered."; + case LicenseState::INCOMPLETE: + return "The license key is incomplete."; + case LicenseState::MALFORMED: + return "The license key is malformed."; + case LicenseState::INVALID: + return "The license key is invalid."; + case LicenseState::EXPIRED: + return "The license key is expired."; + case LicenseState::VALID: + return ""; + } +} + +bool LicenseChecker::createPublicKey(Botan::RSA_PublicKey *rsaPublicKey) +{ + if (!rsaPublicKey) + { + std::cout << "The loaded key is not a RSA key" << std::endl; + return false; + } + + s_publicKey = std::unique_ptr(rsaPublicKey); + return true; +} diff --git a/src/lib_license/LicenseChecker.h b/src/lib_license/LicenseChecker.h new file mode 100644 index 00000000..c68fda8a --- /dev/null +++ b/src/lib_license/LicenseChecker.h @@ -0,0 +1,60 @@ +#ifndef LICENSE_CHECKER_H +#define LICENSE_CHECKER_H + +#include +#include + +#include "License.h" +#include "LicenseType.h" + +namespace Botan +{ + class RSA_PublicKey; + class AutoSeeded_RNG; +} + +class LicenseChecker +{ +public: + enum class LicenseState + { + EMPTY, + INCOMPLETE, + MALFORMED, + INVALID, + EXPIRED, + VALID + }; + + static void setEncodeKey(const std::string& key); + + static bool loadPublicKeyFromFile(std::string fileName); + static bool loadPublicKeyFromString(const std::string& publicKey); + static bool loadPublicKey(); + + static const License* getCurrentLicense(); + + static std::string getCurrentLicenseString(); + static std::string getCurrentLicenseStringEncoded(); + + static LicenseState setCurrentLicenseString(const std::string& licenseStr); + static LicenseState setCurrentLicenseStringEncoded(const std::string& licenseStr); + + static LicenseState checkLicense(const License& license); + static LicenseState checkLicenseString(const std::string& licenseString); + static LicenseState checkCurrentLicense(); + + static LicenseType getCurrentLicenseType(); + static std::string getCurrentLicenseTypeString(); + + static std::string getLicenseErrorForState(LicenseState state); + +private: + static bool createPublicKey(Botan::RSA_PublicKey *rsaPublicKey); + + static std::string s_encodeKey; + static std::unique_ptr s_publicKey; + static std::unique_ptr s_currentLicense; +}; + +#endif // LICENSE_CHECKER_H diff --git a/src/lib_license/LicenseType.h b/src/lib_license/LicenseType.h new file mode 100644 index 00000000..97dd015a --- /dev/null +++ b/src/lib_license/LicenseType.h @@ -0,0 +1,11 @@ +#ifndef LICENSE_TYPE_H +#define LICENSE_TYPE_H + +enum class LicenseType +{ + TEST, + NON_COMMERCIAL, + COMMERCIAL +}; + +#endif // LICENSE_TYPE_H diff --git a/src/license_generator/CMakeLists.txt b/src/license_generator/CMakeLists.txt index 4164af63..16ee6b06 100644 --- a/src/license_generator/CMakeLists.txt +++ b/src/license_generator/CMakeLists.txt @@ -1,8 +1,8 @@ add_files( LICENSE_GENERATOR - Generator.cpp - Generator.h + LicenseGenerator.cpp + LicenseGenerator.h ) # split for tests diff --git a/src/license_generator/Generator.cpp b/src/license_generator/Generator.cpp deleted file mode 100644 index 1c61b893..00000000 --- a/src/license_generator/Generator.cpp +++ /dev/null @@ -1,291 +0,0 @@ -#include "Generator.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "License.h" -#include "Version.h" -#include "PrivateKey.h" -#include "PublicKey.h" - -const char PRIVATE_KEY_PASSWORD[] = "BA#jk5vbklAiKL9K3k$"; -const char PRIVATE_KEY_FILE[] = "private-sourcetrail.pem"; -const char PUBLIC_KEY_FILE[] = "public-sourcetrail.pem"; - -Generator::Generator() -{ - loadPrivateKeyFromString(PRIVATE_KEY); -} - -Generator::~Generator() -{ -} - -void Generator::generateKeys() -{ - m_privateKey = std::make_unique(m_rng, 2048); -} - -std::string Generator::getPrivateKeyFilename() -{ - if(m_privateKeyFile.empty()) - { - return PRIVATE_KEY_FILE; - } - return m_privateKeyFile; -} - -std::string Generator::getPublicKeyFilename() -{ - if(m_publicKeyFile.empty()) - { - return PUBLIC_KEY_FILE; - } - return m_publicKeyFile; -} - -std::string Generator::encodeLicenseByVersion( - const std::string& user, - const std::string& licenseType, - size_t numberOfUsers, - const std::string& version -){ - m_license = nullptr; - - if (user.size() <= 0) - { - std::cout << "No user given" << std::endl; - return ""; - } - - if (licenseType.size() <= 0) - { - std::cout << "No license type given" << std::endl; - return ""; - } - - if (version.empty()) - { - std::cout << "No version given" << std::endl; - return ""; - } - - Version tempVersion = Version::fromString(version); - if (tempVersion.isValid()) - { - createLicense(user, licenseType, tempVersion.toShortString(), false, numberOfUsers); - } - - return m_license->getLicenseString(); -} - -std::string Generator::encodeLicenseByQuarters( - const std::string& user, - const std::string& licenseType, - size_t numberOfUsers, - size_t quarters -){ - return encodeLicenseByVersion(user, licenseType, numberOfUsers, getExpireVersion(quarters)); -} - -std::string Generator::encodeLicenseByDays( - const std::string& user, - const std::string& licenseType, - size_t numberOfUsers, - size_t days -){ - boost::gregorian::date today = boost::gregorian::day_clock::local_day(); - boost::gregorian::days daysToTry(days); - boost::gregorian::date expireDate = today + daysToTry; - - createLicense(user, licenseType, boost::gregorian::to_simple_string(expireDate), true, numberOfUsers); - - return m_license->getLicenseString(); -} - -void Generator::printLicenseAndWriteItToFile() -{ - if (m_license) - { - m_license->print(); - m_license->writeToFile("license.txt"); - } - else - { - std::cout << "nothing to print" << std::endl; - } -} - -bool Generator::verifyLicense(const std::string& filename) -{ - License license; - license.loadFromFile(filename); -// license.loadPublicKeyFromString(PUBLIC_KEY); - return license.isValid(); -} - -void Generator::setCustomPrivateKeyFile(const std::string& file) -{ - if(!file.empty()) - { - m_privateKeyFile = file; - } -} - -void Generator::setCustomPublicKeyFile(const std::string& file) -{ - if(!file.empty()) - { - m_publicKeyFile = file; - } -} - -std::string Generator::getPublicKeyPEMFileAsString() -{ - return Botan::X509::PEM_encode(*m_privateKey); -} - -std::string Generator::getPrivateKeyPEMFileAsString() -{ - if (m_privateKey == NULL) - { - std::cout << "m_privateKey is NULL" << std::endl; - return ""; - } - - return Botan::PKCS8::PEM_encode(*m_privateKey, m_rng, PRIVATE_KEY_PASSWORD); -} - -void Generator::writeKeysToFiles() -{ - std::string publicKeyFilename = getPublicKeyFilename(); - if (publicKeyFilename.size() <= 0) - { - std::cout << "Failed to retrieve file name for public key" << std::endl; - return; - } - - std::string privateKeyFilename = getPrivateKeyFilename(); - if (privateKeyFilename.size() <= 0) - { - std::cout << "Failed to retrieve file name for private key" << std::endl; - return; - } - - std::cout << "public key filename: " << publicKeyFilename << std::endl; - std::ofstream pub(publicKeyFilename); - pub << getPublicKeyPEMFileAsString(); - std::cout << "public key created" << std::endl; - - std::cout << "private key filename: " << privateKeyFilename << std::endl; - std::ofstream priv(privateKeyFilename); - priv << getPrivateKeyPEMFileAsString(); - std::cout << "private key created" << std::endl; -} - -bool Generator::loadPrivateKeyFromFile() -{ - if (boost::filesystem::exists(getPrivateKeyFilename()) == false) - { - return false; - } - - Botan::Private_Key* privateKey = Botan::PKCS8::load_key(getPrivateKeyFilename(), m_rng, PRIVATE_KEY_PASSWORD); - Botan::RSA_PrivateKey *rsaKey = dynamic_cast(privateKey); - - if (!rsaKey) - { - std::cout << "The key is not a RSA key" << std::endl; - return false; - } - - m_privateKey = std::unique_ptr(rsaKey); - - return (m_privateKey != NULL); -} - -bool Generator::loadPrivateKeyFromString(const std::string& key) -{ - if (key.size() <= 0) - { - std::cout << "No key string given" << std::endl; - return false; - } - - Botan::DataSource_Memory in(key); - Botan::Private_Key* privateKey= Botan::PKCS8::load_key(in, m_rng, PRIVATE_KEY_PASSWORD); - Botan::RSA_PrivateKey *rsaKey = dynamic_cast(privateKey); - - if (!rsaKey) - { - std::cout << "The key is not a RSA key" << std::endl; - return false; - } - - m_privateKey = std::unique_ptr(rsaKey); - - return (m_privateKey != NULL); -} - -Botan::RSA_PrivateKey *Generator::getPrivateKey() const -{ - return m_privateKey.get(); -} - -void Generator::createLicense( - const std::string& user, - const std::string& type, - const std::string& expiration, - bool expiresAtDate, - size_t numberOfUsers -){ - m_license = std::make_unique(); - - m_license->createHeader( - user, type.size() ? type : LicenseConstants::TEST_LICENSE_STRING, expiration, expiresAtDate, numberOfUsers); - - Botan::AutoSeeded_RNG rng; - std::string pass9 = Botan::generate_passhash9(m_license->getExpireLine(), m_rng); - m_license->setHashLine(pass9); - - //encode message - const std::string emsa = "EMSA4(SHA-256)"; - Botan::PK_Signer signer(*(m_privateKey.get()), rng, emsa); - Botan::DataSource_Memory in(m_license->getMessage()); - Botan::byte buffer[4096] = {0}; - - while (size_t got = in.read(buffer, sizeof(buffer))) - { - signer.update(buffer, got); - } - - const std::string signature = Botan::base64_encode(signer.signature(rng)); - m_license->setSignature(signature); -} - -int Generator::mapMonthToVersion(int month) -{ - return (month-1)/3+1; -} - -std::string Generator::getExpireVersion(int quarters) -{ - boost::gregorian::date today = boost::gregorian::day_clock::local_day(); - int monthNumber = today.month().as_number(); - Version version(today.year(), mapMonthToVersion(monthNumber)); - version += quarters; - return version.toShortString(); -} diff --git a/src/license_generator/Generator.h b/src/license_generator/Generator.h deleted file mode 100644 index 906fce35..00000000 --- a/src/license_generator/Generator.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef KEYGEN_H -#define KEYGEN_H - -#include -#include - -#include - -namespace Botan -{ - class RSA_PrivateKey; -} -class License; - -class Generator -{ -public: - Generator(); - ~Generator(); - - std::string encodeLicenseByVersion( - const std::string& user, - const std::string& licenseType, - size_t numberOfUsers, - const std::string& version - ); - std::string encodeLicenseByQuarters( - const std::string& user, - const std::string& licenseType, - size_t numberOfUsers, - size_t quarters - ); - std::string encodeLicenseByDays( - const std::string& user, - const std::string& licenseType, - size_t numberOfUsers, - size_t days - ); - - void printLicenseAndWriteItToFile(); - bool verifyLicense(const std::string& filename = "license.txt"); - void generateKeys(); - void writeKeysToFiles(); - void setCustomPrivateKeyFile(const std::string& file); - void setCustomPublicKeyFile(const std::string& file); - - std::string getPublicKeyPEMFileAsString(); - std::string getPrivateKeyPEMFileAsString(); - bool loadPrivateKeyFromFile(); - bool loadPrivateKeyFromString(const std::string& key); - - Botan::RSA_PrivateKey* getPrivateKey() const; - void createLicense( - const std::string& user, - const std::string& type, - const std::string& expiration, - bool expiresAtDate, - size_t numberOfUsers - ); - - std::string getExpireVersion(int quarters); - void setVersionLine(int year, int minorVersion); -private: - int mapMonthToVersion(int month); - std::string getPrivateKeyFilename(); - std::string getPublicKeyFilename(); - - //Botan - Botan::AutoSeeded_RNG m_rng; - - std::string m_privateKeyFile; - std::string m_publicKeyFile; - std::unique_ptr m_license; - std::unique_ptr m_privateKey; - -}; - -#endif // KEYGEN_H diff --git a/src/license_generator/LicenseGenerator.cpp b/src/license_generator/LicenseGenerator.cpp new file mode 100644 index 00000000..6cf72433 --- /dev/null +++ b/src/license_generator/LicenseGenerator.cpp @@ -0,0 +1,243 @@ +#include "LicenseGenerator.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Version.h" + +const char PRIVATE_KEY_PASSWORD[] = "BA#jk5vbklAiKL9K3k$"; + +LicenseGenerator::LicenseGenerator() +{ +} + +LicenseGenerator::~LicenseGenerator() +{ +} + +void LicenseGenerator::generatePrivateKey() +{ + m_privateKey = std::make_unique(m_rng, 2048); +} + +std::unique_ptr LicenseGenerator::createLicenseByVersion( + const std::string& user, + const std::string& licenseType, + size_t numberOfUsers, + const std::string& version +){ + if (version.empty()) + { + std::cout << "No version given" << std::endl; + return nullptr; + } + + Version tempVersion = Version::fromString(version); + if (tempVersion.isValid()) + { + return createLicense(user, licenseType, numberOfUsers, tempVersion.toShortString(), LicenseConstants::VALID_UNLIMITED); + } + + return nullptr; +} + +std::unique_ptr LicenseGenerator::createLicenseByQuarters( + const std::string& user, + const std::string& type, + size_t numberOfUsers, + size_t quarters +){ + boost::gregorian::date today = boost::gregorian::day_clock::local_day(); + int quarter = (today.month().as_number() - 1) / 3 + 1; + Version version(today.year(), quarter); + version += quarters; + + return createLicenseByVersion(user, type, numberOfUsers, version.toShortString()); +} + +std::unique_ptr LicenseGenerator::createLicenseByDays( + const std::string& user, + const std::string& type, + size_t numberOfUsers, + size_t days +){ + boost::gregorian::date today = boost::gregorian::day_clock::local_day(); + boost::gregorian::days daysToTry(days); + boost::gregorian::date expireDate = today + daysToTry; + + return createLicense( + user, + (type.size() ? type : LicenseConstants::TEST_LICENSE), numberOfUsers, + LicenseConstants::VALID_UNLIMITED, + boost::gregorian::to_simple_string(expireDate) + ); +} + +std::unique_ptr LicenseGenerator::createLicenseLifelong( + const std::string& user, + const std::string& type, + size_t numberOfUsers +){ + if (numberOfUsers == 0) + { + std::cout << "No user count given" << std::endl; + return nullptr; + } + + return createLicense(user, type, numberOfUsers, LicenseConstants::VALID_UNLIMITED, LicenseConstants::VALID_UNLIMITED); +} + +std::string LicenseGenerator::getPublicKeyPEMFileAsString() +{ + if (!m_privateKey) + { + std::cout << "No private key loaded" << std::endl; + return ""; + } + + return Botan::X509::PEM_encode(*m_privateKey); +} + +std::string LicenseGenerator::getPrivateKeyPEMFileAsString() +{ + if (!m_privateKey) + { + std::cout << "No private key loaded" << std::endl; + return ""; + } + + return Botan::PKCS8::PEM_encode(*m_privateKey, m_rng, PRIVATE_KEY_PASSWORD); +} + +void LicenseGenerator::writeKeysToFiles(const std::string& publicKeyFilename, const std::string& privateKeyFilename) +{ + if (publicKeyFilename.size() <= 0) + { + std::cout << "Public key file path is empty" << std::endl; + return; + } + + if (privateKeyFilename.size() <= 0) + { + std::cout << "Private key file path is empty" << std::endl; + return; + } + + std::cout << "public key filename: " << publicKeyFilename << std::endl; + std::ofstream pub(publicKeyFilename); + pub << getPublicKeyPEMFileAsString(); + pub.close(); + std::cout << "public key created" << std::endl; + + std::cout << "private key filename: " << privateKeyFilename << std::endl; + std::ofstream priv(privateKeyFilename); + priv << getPrivateKeyPEMFileAsString(); + priv.close(); + std::cout << "private key created" << std::endl; +} + +bool LicenseGenerator::loadPrivateKeyFromFile(const std::string& file) +{ + if (!boost::filesystem::exists(file)) + { + std::cout << "Private key not found: " << file << std::endl; + return false; + } + + return createPrivateKey(dynamic_cast(Botan::PKCS8::load_key(file, m_rng, PRIVATE_KEY_PASSWORD))); +} + +bool LicenseGenerator::loadPrivateKeyFromString(const std::string& key) +{ + if (key.empty()) + { + std::cout << "No key string given" << std::endl; + return false; + } + + Botan::DataSource_Memory in(key); + return createPrivateKey(dynamic_cast(Botan::PKCS8::load_key(in, m_rng, PRIVATE_KEY_PASSWORD))); +} + +Botan::RSA_PrivateKey* LicenseGenerator::getPrivateKey() const +{ + return m_privateKey.get(); +} + +bool LicenseGenerator::createPrivateKey(Botan::RSA_PrivateKey* rsaPrivateKey) +{ + if (!rsaPrivateKey) + { + std::cout << "The key is not a RSA key" << std::endl; + return false; + } + + m_privateKey = std::unique_ptr(rsaPrivateKey); + return true; +} + +std::unique_ptr LicenseGenerator::createLicense( + const std::string& user, + const std::string& type, + size_t numberOfUsers, + const std::string& expirationVersion, + const std::string& expirationDate +){ + if (user.empty()) + { + std::cout << "No user given" << std::endl; + return nullptr; + } + + if (type.empty()) + { + std::cout << "No license type given" << std::endl; + return nullptr; + } + + if (expirationVersion.empty()) + { + std::cout << "No expiration version given" << std::endl; + return nullptr; + } + + if (expirationDate.empty()) + { + std::cout << "No expiration date given" << std::endl; + return nullptr; + } + + std::unique_ptr license = std::make_unique(); + license->setMessage(user, type, numberOfUsers, expirationVersion, expirationDate); + + Botan::AutoSeeded_RNG rng; + + // encode message + const std::string emsa = "EMSA4(SHA-256)"; + Botan::PK_Signer signer(*(m_privateKey.get()), rng, emsa); + Botan::DataSource_Memory in(license->getMessage(false)); + + Botan::byte buffer[4096] = {0}; + while (size_t got = in.read(buffer, sizeof(buffer))) + { + signer.update(buffer, got); + } + + const std::string signature = Botan::base64_encode(signer.signature(rng)); + license->setSignature(signature); + + return license; +} diff --git a/src/license_generator/LicenseGenerator.h b/src/license_generator/LicenseGenerator.h new file mode 100644 index 00000000..eded7f48 --- /dev/null +++ b/src/license_generator/LicenseGenerator.h @@ -0,0 +1,78 @@ +#ifndef LICENSE_GENERATOR_H +#define LICENSE_GENERATOR_H + +#include +#include + +#include + +#include "License.h" + +namespace Botan +{ + class RSA_PrivateKey; +} + +class License; + +class LicenseGenerator +{ +public: + LicenseGenerator(); + ~LicenseGenerator(); + + void generatePrivateKey(); + bool loadPrivateKeyFromFile(const std::string& file); + bool loadPrivateKeyFromString(const std::string& key); + + Botan::RSA_PrivateKey* getPrivateKey() const; + + std::string getPublicKeyPEMFileAsString(); + std::string getPrivateKeyPEMFileAsString(); + + void writeKeysToFiles(const std::string& publicKeyFilename, const std::string& privateKeyFilename); + + std::unique_ptr createLicenseByVersion( + const std::string& user, + const std::string& type, + size_t numberOfUsers, + const std::string& version + ); + + std::unique_ptr createLicenseByQuarters( + const std::string& user, + const std::string& type, + size_t numberOfUsers, + size_t quarters + ); + + std::unique_ptr createLicenseByDays( + const std::string& user, + const std::string& type, + size_t numberOfUsers, + size_t days + ); + + std::unique_ptr createLicenseLifelong( + const std::string& user, + const std::string& type, + size_t numberOfUsers + ); + +private: + bool createPrivateKey(Botan::RSA_PrivateKey* rsaPrivateKey); + + std::unique_ptr createLicense( + const std::string& user, + const std::string& type, + size_t numberOfUsers, + const std::string& expirationVersion, + const std::string& expirationDate + ); + + //Botan + Botan::AutoSeeded_RNG m_rng; + std::unique_ptr m_privateKey; +}; + +#endif // KEYGEN_H diff --git a/src/license_generator/main.cpp b/src/license_generator/main.cpp index af6b1cb1..1a30b629 100644 --- a/src/license_generator/main.cpp +++ b/src/license_generator/main.cpp @@ -5,7 +5,8 @@ #include -#include "Generator.h" +#include "LicenseChecker.h" +#include "LicenseGenerator.h" #include "PrivateKey.h" #include "PublicKey.h" @@ -17,9 +18,9 @@ bool process_command_line(int argc, char** argv) { std::string user; std::string version; - std::string privateKeyFile; - std::string publicKeyFile; - std::string licenseFile = ""; + std::string privateKeyFile = "private-sourcetrail.pem"; + std::string publicKeyFile = "public-sourcetrail.pem"; + std::string licenseFile = "license.txt"; std::string type = ""; int quarters = 4; int days = 0; @@ -31,32 +32,28 @@ bool process_command_line(int argc, char** argv) ("generate,g", po::value(&user), "Generate a License, USERNAME as value") ("check,c", "Validate a License"); - po::options_description keygen_description("Options Keygeneration"); + po::options_description keygen_description("Options License Generation"); keygen_description.add_options() ("version,v", po::value(&version), "Versionnumber (in format 20xx.x) until Sourcetrail valid") ("quarters,q", po::value(&quarters), "Number of quarters Sourcetrail is valid from now") ("users,u", po::value(&numberOfUsers), "Number of users") ("licenseType,t", po::value(&type), "License Type of ") - ("expiration,e", po::value(&days), "Valid for days"); + ("expiration,e", po::value(&days), "Valid for days") + ("lifelong,l", "Valid perpetually"); - po::options_description hidden_description("Hidden Options"); - hidden_description.add_options() + po::options_description advanced_description("Advanced Options"); + advanced_description.add_options() ("public-file", po::value(&publicKeyFile), "Custom public key file") ("private-file", po::value(&privateKeyFile), "Custom private key file") - ("license-file", po::value(&licenseFile), "Custom license") - ("hidden", "Print this help message"); + ("license-file", po::value(&licenseFile), "Custom license (default: license.txt)"); + po::options_description desc("Sourcetrail Generator"); desc.add_options() ("help,h", "Print this help message"); - desc.add(modes_description).add(keygen_description); - - po::options_description allDescriptions("Sourcetrail Generator"); - allDescriptions.add_options(); - allDescriptions.add(modes_description).add(keygen_description).add(hidden_description); + desc.add(modes_description).add(keygen_description).add(advanced_description); po::variables_map vm; - po::store(po::parse_command_line(argc,argv,desc), vm); po::notify(vm); @@ -64,7 +61,7 @@ bool process_command_line(int argc, char** argv) if (vm.count("help")) { std::cout << desc << std::endl; - return 1; + return true; } // no mode chosen -> display help @@ -73,42 +70,21 @@ bool process_command_line(int argc, char** argv) std::cout << "*****************************\nNo mode chosen, display help: " << "\n*****************************\n\n" << desc << std::endl; - return 1; + return false; } - - if (vm.count("hidden")) - { - std::cout << allDescriptions << std::endl; - return 1; - } - - Generator keygen; - - // make sure there are no negative amount of users - if (vm.count("users")) - { - if (numberOfUsers < 0) - { - std::cout << "Invalid amount of users. (Must be > 0)" << std::endl; - return false; - } - } + LicenseGenerator keygen; if (vm.count("key")) { - keygen.generateKeys(); - keygen.writeKeysToFiles(); - } - - if (vm.count("public-file")) - { - keygen.setCustomPublicKeyFile(publicKeyFile); + keygen.generatePrivateKey(); + keygen.writeKeysToFiles(publicKeyFile, privateKeyFile); + return true; } if (vm.count("private-file")) { - keygen.setCustomPrivateKeyFile(privateKeyFile); + keygen.loadPrivateKeyFromFile(privateKeyFile); } else { @@ -117,30 +93,71 @@ bool process_command_line(int argc, char** argv) if (vm.count("generate")) { - if (days > 0) + // make sure there is no negative amount of users + if (vm.count("users")) { - keygen.encodeLicenseByDays(user, type, numberOfUsers, days); + if (numberOfUsers < 0) + { + std::cout << "Invalid amount of users. (Must be > 0)" << std::endl; + return false; + } + } + + std::unique_ptr license; + + if (vm.count("lifelong")) + { + license = keygen.createLicenseLifelong(user, type, numberOfUsers); + } + else if (vm.count("expiration")) + { + license = keygen.createLicenseByDays(user, type, numberOfUsers, days); } else if (!version.empty()) { - keygen.encodeLicenseByVersion(user, type, numberOfUsers, version); + license = keygen.createLicenseByVersion(user, type, numberOfUsers, version); } else { - keygen.encodeLicenseByQuarters(user, type, numberOfUsers, quarters); + license = keygen.createLicenseByQuarters(user, type, numberOfUsers, quarters); + } + + if (license) + { + license->print(); + license->writeToFile(licenseFile); } - keygen.printLicenseAndWriteItToFile(); } if (vm.count("check")) { - if (keygen.verifyLicense()) + if (vm.count("public-file")) + { + LicenseChecker::loadPublicKeyFromFile(publicKeyFile); + } + else + { + LicenseChecker::loadPublicKey(); + } + + License license; + LicenseChecker::LicenseState state = LicenseChecker::LicenseState::EMPTY; + if (license.loadFromFile(licenseFile)) + { + state = LicenseChecker::checkLicense(license); + } + else + { + state = LicenseChecker::LicenseState::MALFORMED; + } + + if (state == LicenseChecker::LicenseState::VALID) { std::cout << "License valid" << std::endl; } else { - std::cout << "License not valid" << std::endl; + std::cout << LicenseChecker::getLicenseErrorForState(state) << std::endl; } } } @@ -153,7 +170,8 @@ bool process_command_line(int argc, char** argv) return true; } -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ process_command_line(argc, argv); return 0; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 70edba8c..518f3b7c 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -18,8 +18,9 @@ add_files( FilePathFilterTestSuite.h FilePathTestSuite.h FileSystemTestSuite.h - GeneratorTestSuite.h GraphTestSuite.h + LicenseCheckerTestSuite.h + LicenseGeneratorTestSuite.h LogManagerTestSuite.h LowMemoryStringMapTestSuite.h MatrixBaseTestSuite.h diff --git a/src/test/LicenseCheckerTestSuite.h b/src/test/LicenseCheckerTestSuite.h new file mode 100644 index 00000000..efdb5fa8 --- /dev/null +++ b/src/test/LicenseCheckerTestSuite.h @@ -0,0 +1,634 @@ +#include + +#include "License.h" +#include "LicenseChecker.h" +#include "Version.h" + +class LicenseCheckerTestSuite : public CxxTest::TestSuite +{ +public: + + void test_check_empty_license() + { + LicenseChecker::loadPublicKey(); + LicenseChecker::LicenseState state = LicenseChecker::checkLicenseString(""); + TS_ASSERT_EQUALS(state, LicenseChecker::LicenseState::EMPTY); + } + + void test_check_malformed_license() + { + LicenseChecker::loadPublicKey(); + LicenseChecker::LicenseState state = LicenseChecker::checkLicenseString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: 3 +Licensed usage period: until 2021-Dec-10 +- +$9$AQAKpvMMWlY7aWaMQY/AScwUo/nhqFdj+skS46v+7QHoj7X6qS+W +IErtt71WhVoqM8eki8IS+BDuO+TyLCAKhpNYQhvgykgfAx16T3ypD4A +4/DKGZbm/ZzZ3J5KD30Ko7pZfsgCnBMZu+VHSkoxtntDCzmhNySF4yo +fiDVhR9NYeWjgd0jtj1+dpBfjmQeXLgf4DqtYvgW446pUfEIIur174y +ffYziDA9VhIpgx2ZEYfFFXWNY+0esAkN389UxZ5UgQcs0LHTdEBphYO +oZJKdZdPbF+zSPwUeRej9SkCOpionrJ9lvVMo3p2pro0boeM4/bAs/p +MyEDaYFJdoazS3T8CXB0oPdEoyvYR6kOBZCw9YeW1Ud0azN8BeMx68c +gwitGhNQeI9A== +-----END LICENSE-----)" + ); + + TS_ASSERT_EQUALS(state, LicenseChecker::LicenseState::MALFORMED); + } + + void test_check_invalid_license() + { + LicenseChecker::loadPublicKey(); + LicenseChecker::LicenseState state = LicenseChecker::checkLicenseString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: 3 +Licensed product version: unlimited +Licensed usage period: until 2021-Dec-10 +- +$9$AQAKpvMMWlY7aWaMQY/AScwUo/nhqFdj+skS46v+7QHoj7X6qS+W +IErtt71WhVoqM8eki8IS+BDuO+TyLCAKhpNYQhvgykgfAx16T3ypD4A +4/DKGZbm/ZzZ3J5KD30Ko7pZfsgCnBMZu+VHSkoxtntDCzmhNySF4yo +fiDVhR9NYeWjgd0jtj1+dpBfjmQeXLgf4DqtYvgW446pUfEIIur174y +ffYziDA9VhIpgx2ZEYfFFXWNY+0esAkN389UxZ5UgQcs0LHTdEBphYO +oZJKdZdPbF+zSPwUeRej9SkCOpionrJ9lvVMo3p2pro0boeM4/bAs/p +MyEDaYFJdoazS3T8CXB0oPdEoyvYR6kOBZCw9YeW1Ud0azN8BeMx68C +gwitGhNQeI9A== +-----END LICENSE-----)" + ); + + TS_ASSERT_EQUALS(state, LicenseChecker::LicenseState::INVALID); + } + + void test_check_license_without_start_end() + { + LicenseChecker::loadPublicKey(); + LicenseChecker::LicenseState state = LicenseChecker::checkLicenseString( +R"( +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: 3 +Licensed product version: unlimited +Licensed usage period: until 2021-Dec-10 +- +$9$AQAKpvMMWlY7aWaMQY/AScwUo/nhqFdj+skS46v+7QHoj7X6qS+W +IErtt71WhVoqM8eki8IS+BDuO+TyLCAKhpNYQhvgykgfAx16T3ypD4A +4/DKGZbm/ZzZ3J5KD30Ko7pZfsgCnBMZu+VHSkoxtntDCzmhNySF4yo +fiDVhR9NYeWjgd0jtj1+dpBfjmQeXLgf4DqtYvgW446pUfEIIur174y +ffYziDA9VhIpgx2ZEYfFFXWNY+0esAkN389UxZ5UgQcs0LHTdEBphYO +oZJKdZdPbF+zSPwUeRej9SkCOpionrJ9lvVMo3p2pro0boeM4/bAs/p +MyEDaYFJdoazS3T8CXB0oPdEoyvYR6kOBZCw9YeW1Ud0azN8BeMx68c +gwitGhNQeI9A== +)" + ); + + TS_ASSERT_EQUALS(state, LicenseChecker::LicenseState::VALID); + } + + void test_license_encoding() + { + LicenseChecker::loadPublicKey(); + LicenseChecker::setCurrentLicenseString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: 3 +Licensed product version: unlimited +Licensed usage period: until 2021-Dec-10 +- +$9$AQAKpvMMWlY7aWaMQY/AScwUo/nhqFdj+skS46v+7QHoj7X6qS+W +IErtt71WhVoqM8eki8IS+BDuO+TyLCAKhpNYQhvgykgfAx16T3ypD4A +4/DKGZbm/ZzZ3J5KD30Ko7pZfsgCnBMZu+VHSkoxtntDCzmhNySF4yo +fiDVhR9NYeWjgd0jtj1+dpBfjmQeXLgf4DqtYvgW446pUfEIIur174y +ffYziDA9VhIpgx2ZEYfFFXWNY+0esAkN389UxZ5UgQcs0LHTdEBphYO +oZJKdZdPbF+zSPwUeRej9SkCOpionrJ9lvVMo3p2pro0boeM4/bAs/p +MyEDaYFJdoazS3T8CXB0oPdEoyvYR6kOBZCw9YeW1Ud0azN8BeMx68c +gwitGhNQeI9A== +-----END LICENSE-----)" + ); + + LicenseChecker::LicenseState state = LicenseChecker::checkCurrentLicense(); + TS_ASSERT_EQUALS(state, LicenseChecker::LicenseState::VALID); + + LicenseChecker::setEncodeKey("test_key"); + std::string encodedLicenseString = LicenseChecker::getCurrentLicenseStringEncoded(); + + LicenseChecker::setEncodeKey("test_key_new"); + state = LicenseChecker::setCurrentLicenseStringEncoded(encodedLicenseString); + + TS_ASSERT_EQUALS(state, LicenseChecker::LicenseState::MALFORMED); + + LicenseChecker::setEncodeKey("test_key"); + state = LicenseChecker::setCurrentLicenseStringEncoded(encodedLicenseString); + + TS_ASSERT_EQUALS(state, LicenseChecker::LicenseState::VALID); + } + + void test_check_version_license() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: 1 +Licensed product version: up to 2020.2 +Licensed usage period: unlimited +- +$9$AQAKrGHpbkd9I+FVXZ8YtKK7rJePuPqJcWwaLoNYze14fRNiIBQV +QcelksnNcsztdUVCSp1MPkEbuWHkhR74m5qJ3wECnMekfVmP3GhbvwL +cqAEyrpR4PrZCRM2oxEgcRVk6QrPM9pHYPaPHe8sV74odOGQtHUrEW6 +N8ZNL0NMLzSxurD/hXdDLdgxya9UptYIJ6c2Bbu33xJ87Ofs07pptc0 +4VzBXJYG5iKIbmQAnrU6KOBL5iIZ11/sM8VbkTw+nSySI4yr3/f8BWe +7PhDAmhvDTX9aMMfpaXXeL3PyC/023FwyAqimB1vzoo28Pm1p1tN9mF +l7Ci+UazTnVY34DLVoXYUaDVlt8t8VYq05Z7f6sfcGtqpthuevGatM0 +e6G8hqqTc+CQ== +-----END LICENSE-----)" + ); + + Version version = Version::getApplicationVersion(); + Version::setApplicationVersion(Version(2020, 2, 11, "asdfasdf")); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 1); + TS_ASSERT_EQUALS(license.getTimeLeft(), -2); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(!license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::VALID); + + Version::setApplicationVersion(version); + } + + void test_check_version_license_expired() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: 1 +Licensed product version: up to 2018.2 +Licensed usage period: unlimited +- +$9$AQAKshj0tavIV6xR/dL9nXxaxib80g8Kxho0CGpvdypb4xujP5nw +mIQi3TiVrBTCszJLONCdnhyzotj1R370JPHWa/FFbGqABhexI5PGdIb +3Pa1EqQP9otCJZvDhQTxkm2Ev3zFEgDsA8njBQjr09Blcbt6F9F5zpJ +Ld75PRPLgvVmrxJfwOOg0tYQ/WrKzmm68gcF7XUHS1N7QDSrxjkqihu +xvHd6PoQSmX8pbNpYbkNF6N1T7rR5B8o7BnCc1PEw13J6k8CsomyQ4Q +qg2qAz0IOXk+r7ZagNvR9XedqcytIu6e0u48ujpEUv+1TSj7VBrOq1a +5La45aDoFn2XgdX/mlJh4RPeTEtg6u06PHf4pHFWiOXyKcvvAWuy7GT +YeDldWiy3iwA== +-----END LICENSE-----)" + ); + + Version version = Version::getApplicationVersion(); + Version::setApplicationVersion(Version(2018, 3, 11, "asdfasdf")); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 1); + TS_ASSERT_EQUALS(license.getTimeLeft(), -2); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::EXPIRED); + + Version::setApplicationVersion(version); + } + + void test_check_period_license() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: 3 +Licensed product version: unlimited +Licensed usage period: until 2021-Dec-10 +- +$9$AQAKpvMMWlY7aWaMQY/AScwUo/nhqFdj+skS46v+7QHoj7X6qS+W +IErtt71WhVoqM8eki8IS+BDuO+TyLCAKhpNYQhvgykgfAx16T3ypD4A +4/DKGZbm/ZzZ3J5KD30Ko7pZfsgCnBMZu+VHSkoxtntDCzmhNySF4yo +fiDVhR9NYeWjgd0jtj1+dpBfjmQeXLgf4DqtYvgW446pUfEIIur174y +ffYziDA9VhIpgx2ZEYfFFXWNY+0esAkN389UxZ5UgQcs0LHTdEBphYO +oZJKdZdPbF+zSPwUeRej9SkCOpionrJ9lvVMo3p2pro0boeM4/bAs/p +MyEDaYFJdoazS3T8CXB0oPdEoyvYR6kOBZCw9YeW1Ud0azN8BeMx68c +gwitGhNQeI9A== +-----END LICENSE-----)" + ); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 3); + TS_ASSERT(license.getTimeLeft() >= 0); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(!license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::VALID); + } + + void test_check_period_license_expired() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: 3 +Licensed product version: unlimited +Licensed usage period: until 2018-Dec-06 +- +$9$AQAKkgTBy1koMv8CusfbUVWyMQtbTc67dSMTv+96bnVWSQdc/Bti +O8300TO2SWEVuyMo/BQkmXTnEFWLtwnAPqtAJXLR4oDTqlHUNs8Ur5q +iL6dMVsiyp41EHrzuof1/u+pgX03kCyIgD6TAn42En3+2pgo1zhDxnx +vaBxQTUcUW8+kJyNdSlJ6E2DgwGX2uXmf8MF8nsJtwHhOe/3qyV6fm0 +PtXmWwjesNX7qGA8L+pcZKsB6AdC4dEJ71iFr8u/O1i8ab0v5xorJM3 +zJ40jE0kcucpag5sW43ch0eX6PntXSwXuFfaQ7gTUH6qYTvxqIFUZGW +r0TrPuhiyxHS80ki5QApv9AShXlt3LRM41SJSCuv71Z30kGIVV6Itoo ++rtjllrh9TBA== +-----END LICENSE-----)" + ); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 3); + TS_ASSERT_EQUALS(license.getTimeLeft(), -1); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::EXPIRED); + } + + void test_check_site_license() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: unlimited +Licensed product version: unlimited +Licensed usage period: until 2021-Dec-10 +- +$9$AQAKM0lWahp0xKiqVObgsjtpg9LNh+knuPSDbbv/6LuJqbZYA2F2 +ibdICkHLfDTIiUjqXdmLbNWbavWwcrtOnzw7zxExIyCcJPXrQxUjmRU +kvVe+mPlN+4Re1jFBmCY18X13fb9JSShOjWGEnpCR/o6Fq1EKoOoYg1 +NyWoVDy+Jv/Tr0Zr1/xFyhQJMw3dr0LiCrmYG0+J9P4lndI7MI4QG3z +0e625imrkJVDJsRUk3V9YXu8XxJTpSfc8tZjolzxCpaAKNzzCQeghdh +hwAEZEqUd3+ULN6lY36laCzRSMOts6506wMMhv4wzT65h8RRrrgJJjw +lnOALOwf+YcHvMpHWBBL18utvvxn1dWUdywBXPfmJnncglP7Cg+/B6q +gPxec/V8S5mA== +-----END LICENSE-----)" + ); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 0); + TS_ASSERT(license.getTimeLeft() >= 0); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(!license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::VALID); + } + + void test_check_site_license_expired() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: unlimited +Licensed product version: unlimited +Licensed usage period: until 2018-Dec-08 +- +$9$AQAKi2K2OESpqYwzOE6EPg5ltiYYNV3FYzGT9O/mf8fp8+ovGL5T +aJjHkXuWYsZ71yG4SH7DKrtSKc3nSHT6eBKpxkGeQ/50KMIqbFHUNWm +qsv7mzFeKeoA7e9Hf2X/RfWqBmHTV8eIhaE4qqMKF5DY+fG9Y1qlHEs +oeKJCM6BaxYiPZjG4sMKG2K/6AkT4TbNaHjrBktlE1l4ad8mW1xBXn5 +p6ph0waObobOioNZINvLuRrIModAnfjCFVdKtL+HzM8Kg/2Tx7ZPCQV +4Tvj6Jm61OiXAdgZcIoL1DYsZWqIb3HFIJQw7Q+IGsKFgh64oNwh/WP +LZi4XroeDeOJnhX5+sfr0WZoV2BKCsNCsvKNHTtEG9ZGtKSgp8ktgBt +XCElsg55RjkQ== +-----END LICENSE-----)" + ); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 0); + TS_ASSERT_EQUALS(license.getTimeLeft(), -1); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::EXPIRED); + } + + void test_check_test_license() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Test License +Licensed number of users: unlimited +Licensed product version: unlimited +Licensed usage period: until 2021-Dec-10 +- +$9$AQAKrvzvSMvN038BfcgVNT8rN6/X4pFbYKpU0gMSeH62+Vsd55MC +E6rIwc+vMlHlLzHZsn6+YpSsPX2Hs31JUpkRTAb2X2ln0SZU2mZUL/V +SD4trO/jCUoTaC7FbaXD6F1A8DWuJos706g/MaNWyXc0tUnd3N6HSnZ +UEX0HHQXaKatZh5imexO5NncQtdhObU9PFo0UhXTxQ9wFxrKFzgyIIl +EQqQq4ExHzMz41wDD/476lf43W8O6tfbSRaO2aSlALy/3IqnrZBjllm +girrswPR5tAq8JCL8hxenkajWK7plnlg4tUuvpvnj/fif/2Km5v7Xls ++MDZZ7EC7hRX2UUHmIWAnCaiv25k/NHfu1GBQ19mS8mWYnRmxXOccAO +qCavX9fMG6pQ== +-----END LICENSE-----)" + ); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Test License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 0); + TS_ASSERT(license.getTimeLeft() >= 0); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(!license.isExpired()); + TS_ASSERT(license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::VALID); + } + + void test_check_test_license_expired() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Test License +Licensed number of users: unlimited +Licensed product version: unlimited +Licensed usage period: until 2018-Dec-06 +- +$9$AQAKML9uOsAwGbhR/GdF+idZBjBwcvYuNvym5yLvTweDEOs70IHy +NQBPgvq3TOUPtmKc3DE1lHLB7Fe2/8VNwcg1hkncle9fKh44WGuNIRX +dFPJ40Hl+be8ekrjyKwz1x9fb85mkzheiDQUEC8QOHUUdJJoETgp3Il +dcLDe/UL7Sq3/GpPDH+SaThMM5y+3tluf7yO/4qqRuzbV68YW/DTHEL +47/LvlW1iAypllpeJD7igpCzUEiRYa2m2pSmpOiS0FoXaZ0jhr0LNXm +C6/zLqHZ4xRnUmhDlmTf1mfybi86xOiPeZFfvSVnv5HPtf5Nsk2wGkW +pwim1/hpoCxGJ4hiN9a3cbgnjtqG7yTbfMIiBAxM4JcHt2Dzhs3+r6d +gabQRg1nODDw== +-----END LICENSE-----)" + ); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Test License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 0); + TS_ASSERT_EQUALS(license.getTimeLeft(), -1); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(license.isExpired()); + TS_ASSERT(license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::EXPIRED); + } + + void test_check_lifelong_license() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Jane Doe +License type: Commercial License +Licensed number of users: 2 +Licensed product version: unlimited +Licensed usage period: unlimited +- +$9$AQAKIZaofFkJU73SLfokexujrBlm792b2tF+oQksC40P+Z8vUtgT +hesYyYrRxFXK2OALm7zLkdeJYNwC60+oJx0wA19D6PLQo09d5Tf3hHx +0pkdV0J+2NwidLoJX6ZHWB2KQxg1Vg0yvczZs4uzNLLd6JRdBubN3XF +0hO9m/sYLMRzeh5sn7WJjV2M/BR1bGmgmY0itW0UXw+hYGZaViiDo1f +7erXFcZq5TJwhYM447TjoWa+KDjPd/hCohO8SKKtJ33qWWuTfJNPL5q +7iqyQemOnBOtW+nXHmg/89F+YXoyGeICxinnFs1kmqMey94s/08OUcB +GSlQ649gX2BuuACmHR/JW/0SY4ik/DgkesVHuRWXD8VTq8cOBMCKjOe +YHCqvUCFHVMA== +-----END LICENSE-----)" + ); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 2); + TS_ASSERT_EQUALS(license.getTimeLeft(), -2); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(!license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::VALID); + } + + void test_check_long_name() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +License holder: Janestanasolinaria Doestopolstiminikovasotanibanastovic +License type: Commercial License +Licensed number of users: 2 +Licensed product version: up to 2023.4 +Licensed usage period: unlimited +- +$9$AQAKD2gin+JhXldRL5AkVpoTgNHibszdfWpKSgm+p5zC5wCYiGWd +Vtd8nzkj4Gys3liNW5K1Yu/bfgcoOwjBOh6ZpNXCs9MQaNtfHTNeNXA +6vKwBCyrKruO1Ez1y+63pYN+erVtrxIpy6bTxuSIuD115j++V7sDE6f +oMCTYVAtMoPBxkIDrMB/dRDVu+ZPffAtPl9qA/1Okl697UpFLIC5kOP +YoJmSgwXC0k0lOvF7OyzR95oKUjQifdWgnEH5kpPJ3DqcKsAvSh0bQj +Rmk10RSZo1OmjgYEssKlD04BZMWsgKEeNHVAkovY6mxR4160wOuELxP +Y8odKqEzRcdV/ZWysqZEGPpdeBIqeBd4ZVgN6kEDZqpGktGZL0LThEB +Muf9wa0CwXuw== +-----END LICENSE-----)" + ); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Janestanasolinaria Doestopolstiminikovasotanibanastovic"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 2); + TS_ASSERT_EQUALS(license.getTimeLeft(), -2); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(!license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::VALID); + } + + void test_check_old_format_license() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +Licensed to: Jane Doe +License type: Commercial License (5 users) +Valid up to version: 2020.2 +- +$9$AQAKDowaE3jU5qows5JQrqt6iEfM+BKO0uCgN8lYCIAy/qUzfDsI +S8pZMKD9fBNP0QHK1RNAaiwD8qszcnksDHcURUelnCBk2fpAd4YdR2a +IU6CBwkPWAT92K7KE5OfWV2h2odlSTirPb99WqWYnEngibVeBA4cGZD +k+fSpsPz3Dwg66dNUKajf+snii5wE9qa/2XYhV3gLm3Utls2jhpom31 +9Ri0uP/5b8Zx8ZWtlT5BDoS8LvVBsOtBe788DSmmF+7hA8xLel+VRvt +mNzs/MWbKg+m0JrIdwaqhDlx+Fpc+rlhb0uWTJxKO2ruRkLcXkZ4ZbS +fnOHl2FZZgsRjnT0AbI9hHNf3clXuJsu85ymGa9ZESBvKRmYWvjpdcs +0yeCOCcxABmw== +-----END LICENSE-----)" + ); + + Version version = Version::getApplicationVersion(); + Version::setApplicationVersion(Version(2019, 1, 11, "asdfasdf")); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 5); + TS_ASSERT_EQUALS(license.getTimeLeft(), -2); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(!license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::VALID); + + Version::setApplicationVersion(version); + } + + void test_check_old_format_license_expired() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +Licensed to: Jane Doe +License type: Commercial License (3 users) +Valid up to version: 2018.1 +- +$9$AQAK2j5OkBlxfT/3y0XTbMUu/tRchYJxfD1dHgSFXJMXeFPN9h1N +RVewaFTRWub12WkRSq4rHaEt6MKZCtO3ntj8bGJxuvH4ggridXBoJjP +nkHGPMlUvrdbhs/O7uaWvVXf+n7YrZPxeBBdHiXEUI2AaNbig5FzCeS +Gt31yDkLjSkNwdR4zJGoAB2zWfhxFLIzBXv6z+rToBy71hzMbRHnUX2 +B8zkBAt12QVCZszbV1XJ8LqOhxkGKdo6RsieBc5s2HJB16IFf0gcxkL +2FRzN6wMlTqs4K4pk8+kxwzB+Ywm+griLgzZo1jbg0JUm4Ou9nppaht +m4mgTH3FBGtob6Spi7H8hGNGgrT1gRmnHLyomwPfKXKuO/snvK3CZCB +yBAHSwjidKqg== +-----END LICENSE-----)" + ); + + Version version = Version::getApplicationVersion(); + Version::setApplicationVersion(Version(2019, 1, 11, "asdfasdf")); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 3); + TS_ASSERT_EQUALS(license.getTimeLeft(), -2); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::EXPIRED); + + Version::setApplicationVersion(version); + } + + void test_check_one_hundred_year_testimonial_license() + { + License license; + bool ok = license.loadFromString( +R"(-----BEGIN LICENSE----- +Product: Sourcetrail +Licensed to: Jane Doe +License type: Commercial License (1 Seat) +Valid up to version: 2117.2 +- +$9$AQAKhOnW2QeCuuNbHLpUN7CT7DE/FRuOvEOEcHDNOn7yGs0CUIMS +Ox9zCpz0xH/XsNmL6NTyS3P4DOm9snvaJZM90ttr3nxy4Dv3IRWVXvg +OsvwbkYYC+N2WUKJ+ahk8zU7Uz179tdX2FLbce6/xcOi8Utv3thWWWs +EbV62Ja3DS7Qt9Oq7ui+9PrpvPmsc81yK5g993vOsZkPuOuoE8KNX1m +Dhl9UxaBZfmfQVtiAWx1vP8zCJ4nnANgfAAKPBXfYyuvf8/+GDC7CLo +9e5e0swhcMt0z0DfKdwR59sQPZnXSZyouW5GzTUi2E8nw5IStxi3/GX +z3TR0g8ERzP07KrzdYklfYv+cxtlev/H869p0USn5kPBwW8+ZFX+5Id +8CVzujNAz//w== +-----END LICENSE-----)" + ); + + Version version = Version::getApplicationVersion(); + Version::setApplicationVersion(Version(2019, 1, 11, "asdfasdf")); + + TS_ASSERT(ok); + TS_ASSERT_EQUALS(license.getUser(), "Jane Doe"); + TS_ASSERT_EQUALS(license.getType(), "Commercial License"); + TS_ASSERT_EQUALS(license.getNumberOfUsers(), 1); + TS_ASSERT_EQUALS(license.getTimeLeft(), -2); + TS_ASSERT(license.isComplete()); + TS_ASSERT(!license.isEmpty()); + TS_ASSERT(!license.isExpired()); + TS_ASSERT(!license.isTestLicense()); + + LicenseChecker::loadPublicKey(); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(license), LicenseChecker::LicenseState::VALID); + + Version::setApplicationVersion(version); + } +}; diff --git a/src/test/GeneratorTestSuite.h b/src/test/LicenseGeneratorTestSuite.h similarity index 51% rename from src/test/GeneratorTestSuite.h rename to src/test/LicenseGeneratorTestSuite.h index dbb6f9e0..c5a98c74 100644 --- a/src/test/GeneratorTestSuite.h +++ b/src/test/LicenseGeneratorTestSuite.h @@ -1,16 +1,17 @@ #include #include "License.h" -#include "Generator.h" +#include "LicenseChecker.h" +#include "LicenseGenerator.h" -class GeneratorTestSuite : public CxxTest::TestSuite +class LicenseGeneratorTestSuite : public CxxTest::TestSuite { public: void test_create_Keys_with_Version_and_check() { - Generator generator; - generator.generateKeys(); + LicenseGenerator generator; + generator.generatePrivateKey(); std::string privateKey = generator.getPrivateKeyPEMFileAsString(); std::string publicKey = generator.getPublicKeyPEMFileAsString(); @@ -24,16 +25,15 @@ public: void test_load_private_Key_from_file() { - Generator generator; - generator.setCustomPrivateKeyFile("./data/GeneratorTestSuite/private-v2.pem"); - bool ok = generator.loadPrivateKeyFromFile(); + LicenseGenerator generator; + bool ok = generator.loadPrivateKeyFromFile("./data/LicenseGeneratorTestSuite/private-v2.pem"); TS_ASSERT(ok); } void test_load_private_key_from_string() { - Generator generator; + LicenseGenerator generator; bool ok = generator.loadPrivateKeyFromString(m_privateKey); TS_ASSERT(ok); @@ -41,71 +41,83 @@ public: void test_create_volume_license_and_validate() { - Generator generator; - generator.generateKeys(); - generator.loadPrivateKeyFromString(generator.getPrivateKeyPEMFileAsString()); + LicenseGenerator generator; + generator.generatePrivateKey(); - License license; - license.loadFromString(generator.encodeLicenseByQuarters("TestUser", "VolumeLicense", 20, 4)); + std::unique_ptr license = generator.createLicenseByQuarters("TestUser", "VolumeLicense", 20, 4); - TS_ASSERT_EQUALS(license.getNumberOfUsers(), 20); + TS_ASSERT(license.get()); + TS_ASSERT_EQUALS(license->getUser(), "TestUser"); + TS_ASSERT_EQUALS(license->getType(), "VolumeLicense"); + TS_ASSERT_EQUALS(license->getNumberOfUsers(), 20); + TS_ASSERT_EQUALS(license->isEmpty(), false); + TS_ASSERT_EQUALS(license->isComplete(), true); + TS_ASSERT_EQUALS(license->isExpired(), false); + TS_ASSERT_EQUALS(license->isTestLicense(), false); + TS_ASSERT_EQUALS(license->getTimeLeft(), -2); + + TS_ASSERT(LicenseChecker::loadPublicKeyFromString(generator.getPublicKeyPEMFileAsString())); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(*license.get()), LicenseChecker::LicenseState::VALID); } void test_create_test_license_and_validate() { - Generator generator; - generator.generateKeys(); - generator.loadPrivateKeyFromString(generator.getPrivateKeyPEMFileAsString()); + LicenseGenerator generator; + generator.generatePrivateKey(); - License license; - license.loadPublicKeyFromString(generator.getPublicKeyPEMFileAsString()); - license.loadFromString(generator.encodeLicenseByDays("User", "", 0, 10)); + std::unique_ptr license = generator.createLicenseByDays("User", "", 0, 10); - TS_ASSERT(license.isValid()); - TS_ASSERT_EQUALS(license.getTimeLeft(), 10); + TS_ASSERT(license.get()); + TS_ASSERT_EQUALS(license->getUser(), "User"); + TS_ASSERT_EQUALS(license->getType(), "Test License"); + TS_ASSERT_EQUALS(license->getNumberOfUsers(), 0); + TS_ASSERT_EQUALS(license->isEmpty(), false); + TS_ASSERT_EQUALS(license->isComplete(), true); + TS_ASSERT_EQUALS(license->isExpired(), false); + TS_ASSERT_EQUALS(license->isTestLicense(), true); + TS_ASSERT_EQUALS(license->getTimeLeft(), 10); - license.loadPublicKeyFromString(generator.getPublicKeyPEMFileAsString()); - license.loadFromString(generator.encodeLicenseByDays("User", "", 0, -10)); + TS_ASSERT(LicenseChecker::loadPublicKeyFromString(generator.getPublicKeyPEMFileAsString())); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(*license.get()), LicenseChecker::LicenseState::VALID); - TS_ASSERT(!license.isValid()); - // -1 and not -10 since it means it is expired - TS_ASSERT_EQUALS(license.getTimeLeft(), -1); + license = generator.createLicenseByDays("User", "", 0, -10); + + TS_ASSERT(license.get()); + TS_ASSERT_EQUALS(license->getUser(), "User"); + TS_ASSERT_EQUALS(license->getType(), "Test License"); + TS_ASSERT_EQUALS(license->getNumberOfUsers(), 0); + TS_ASSERT_EQUALS(license->isEmpty(), false); + TS_ASSERT_EQUALS(license->isComplete(), true); + TS_ASSERT_EQUALS(license->isExpired(), true); + TS_ASSERT_EQUALS(license->isTestLicense(), true); + TS_ASSERT_EQUALS(license->getTimeLeft(), -1); + TS_ASSERT_EQUALS(LicenseChecker::checkLicense(*license.get()), LicenseChecker::LicenseState::EXPIRED); } void test_create_licenses_and_check_the_license_info() { - Generator generator; - generator.generateKeys(); + LicenseGenerator generator; + generator.generatePrivateKey(); - License license; + std::unique_ptr license = generator.createLicenseByVersion("TestUser", "Private License", 1, "2017.4"); + std::string testInfo = "TestUser\nPrivate License\n1 user\nvalid up to version 2017.4"; + TS_ASSERT(license.get()); + TS_ASSERT_EQUALS(license->getLicenseInfo(), testInfo); - license.loadFromString(generator.encodeLicenseByVersion("TestUser", "Private License", 1, "2017.4")); - std::string testInfo = "TestUser\nPrivate License\n1 user\nvalid up to version: 2017.4"; - TS_ASSERT_EQUALS(license.getLicenseInfo(), testInfo); + license = generator.createLicenseByVersion("TestUser", "Volume License", 20, "2017.3"); + testInfo = "TestUser\nVolume License\n20 users\nvalid up to version 2017.3"; + TS_ASSERT(license.get()); + TS_ASSERT_EQUALS(license->getLicenseInfo(), testInfo); - license.loadFromString(generator.encodeLicenseByVersion("TestUser", "Volume License", 20, "2017.3")); - testInfo = "TestUser\nVolume License\n20 users\nvalid up to version: 2017.3"; - TS_ASSERT_EQUALS(license.getLicenseInfo(), testInfo); + license = generator.createLicenseByVersion("TestUser", "Volume License", 0, "2018.3"); + testInfo = "TestUser\nVolume License\nunlimited users\nvalid up to version 2018.3"; + TS_ASSERT(license.get()); + TS_ASSERT_EQUALS(license->getLicenseInfo(), testInfo); - license.loadFromString(generator.encodeLicenseByVersion("TestUser", "Private/Academic Single User License", 0, "2018.1")); - testInfo = "TestUser\nPrivate/Academic Single User License\nnot registered for commercial development\nvalid up to version: 2018.1"; - TS_ASSERT_EQUALS(license.getLicenseInfo(), testInfo); - } - - void test_create_license_and_validate() - { - Generator generator; - generator.generateKeys(); - - generator.loadPrivateKeyFromString(generator.getPrivateKeyPEMFileAsString()); - License license; - license.loadFromString(generator.encodeLicenseByVersion("TestUser", "Private License", 20, "2017.3")); - license.loadPublicKeyFromString(generator.getPublicKeyPEMFileAsString()); - - TS_ASSERT_EQUALS(license.getUser(), "TestUser"); - TS_ASSERT_EQUALS(license.getType(), "Private License"); - TS_ASSERT_EQUALS(license.getNumberOfUsers(), 20); - TS_ASSERT(license.isValid()); + license = generator.createLicenseLifelong("TestUser", "Volume License", 1); + testInfo = "TestUser\nVolume License\n1 user\nvalid perpetually"; + TS_ASSERT(license.get()); + TS_ASSERT_EQUALS(license->getLicenseInfo(), testInfo); } private: