diff --git a/src/lib/synergy/SubscriptionKey.h b/src/lib/synergy/SubscriptionKey.h
new file mode 100644
index 00000000..0d65a17c
--- /dev/null
+++ b/src/lib/synergy/SubscriptionKey.h
@@ -0,0 +1,28 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2015 Synergy Seamless Inc.
+ *
+ * This package is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * found in the file LICENSE that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "base/String.h"
+
+struct SubscriptionKey {
+ String m_name;
+ String m_type;
+ int m_userLimit;
+ int m_warnTime;
+ int m_expireTime;
+};
diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp
new file mode 100644
index 00000000..e13cf4c7
--- /dev/null
+++ b/src/lib/synergy/SubscriptionManager.cpp
@@ -0,0 +1,210 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2015 Synergy Seamless Inc.
+ *
+ * This package is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * found in the file LICENSE that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "synergy/SubscriptionManager.h"
+
+#include "synergy/XSynergy.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+#include "base/String.h"
+#include "common/Version.h"
+
+#include
+#include
+#include
+#include
+//#include
+
+#if SYSAPI_WIN32
+const char* kFile = "Synergy.subkey";
+#else
+const char* kFile = ".synergy.subkey";
+#endif
+
+//
+// SubscriptionManager
+//
+
+SubscriptionManager::SubscriptionManager() :
+ m_key()
+{
+}
+
+void
+SubscriptionManager::checkFile(const String& filename_)
+{
+ String filename = filename_;
+ if (filename.empty()) {
+ filename = getFilename();
+ }
+
+ std::ifstream stream(filename.c_str());
+ if (!stream.is_open()) {
+ throw XSubscription(synergy::string::sprintf(
+ "Could not open, path=%s", filename.c_str()));
+ }
+
+ String serial;
+ stream >> serial;
+
+ String plainText = decode(serial);
+ parsePlainSerial(plainText, m_key);
+
+ LOG((CLOG_DEBUG "subscription is valid"));
+}
+
+void
+SubscriptionManager::activate(const String& serial)
+{
+ String plainText = decode(serial);
+ parsePlainSerial(plainText, m_key);
+
+ String filename = getFilename();
+ std::ofstream stream(filename.c_str());
+ if (!stream.is_open()) {
+ throw XSubscription(synergy::string::sprintf(
+ "Could not open, file=%s", filename.c_str()));
+ }
+
+ stream << serial << std::endl;
+ LOG((CLOG_DEBUG "subscription file created, path=%s", filename.c_str()));
+}
+
+String
+SubscriptionManager::decode(const String& input)
+{
+ static const char* const lut = "0123456789ABCDEF";
+ size_t len = input.length();
+ if (len & 1) {
+ throw XSubscription("Invalid serial, wrong length.");
+ }
+
+ String output;
+ output.reserve(len / 2);
+ for (size_t i = 0; i < len; i += 2) {
+
+ char a = input[i];
+ char b = input[i + 1];
+
+ const char* p = std::lower_bound(lut, lut + 16, a);
+ const char* q = std::lower_bound(lut, lut + 16, b);
+
+ if (*q != b || *p != a) {
+ throw XSubscription("Invalid serial, unrecognized digit.");
+ }
+
+ output.push_back(static_cast(((p - lut) << 4) | (q - lut)));
+ }
+
+ return output;
+}
+
+void
+SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& key)
+{
+ String serial;
+ bool parity = false;
+ String parityStart = plainText.substr(0, 1);
+ String parityEnd = plainText.substr(plainText.length() - 1, 1);
+
+ // check for parity chars { and }, record parity result, then remove them.
+ if (parityStart == "{" && parityEnd == "}") {
+ parity = true;
+ serial = plainText.substr(1, plainText.length() - 2);
+ }
+ else {
+ serial = plainText;
+ }
+
+ // tokenize serialised subscription.
+ std::vector parts;
+ std::string::size_type pos = 0;
+ bool look = true;
+ while (look) {
+ std::string::size_type start = pos;
+ pos = serial.find(";", pos);
+ if (pos == String::npos) {
+ pos = plainText.length();
+ look = false;
+ }
+ parts.push_back(serial.substr(start, pos - start));
+ pos += 1;
+ }
+
+ // e.g.: {v1;trial;Bob;1;1398297600;1398384000}
+ if (parity
+ && (parts.size() == 6)
+ && (parts.at(0).find("v1") != String::npos)) {
+ key.m_type = parts.at(1);
+ key.m_name = parts.at(2);
+ sscanf(parts.at(3).c_str(), "%d", &key.m_userLimit);
+ sscanf(parts.at(4).c_str(), "%d", &key.m_warnTime);
+ sscanf(parts.at(5).c_str(), "%d", &key.m_expireTime);
+
+ // TODO: use Arch time
+ if (time(0) > key.m_expireTime) {
+ throw XSubscription(synergy::string::sprintf(
+ "%s subscription has expired",
+ key.m_type.c_str()));
+ }
+ else if (time(0) > key.m_warnTime) {
+ LOG((CLOG_WARN "%s subscription will expire soon",
+ key.m_type.c_str()));
+ }
+
+ const char* userText = (key.m_userLimit == 1) ? "user" : "users";
+ LOG((CLOG_INFO "%s subscription valid is for %d %s, registered to %s",
+ key.m_type.c_str(),
+ key.m_userLimit,
+ userText,
+ key.m_name.c_str()));
+
+ return;
+ }
+ else if ((parts.size() == 2) && (parts.at(1) == kApplication)) {
+ key.m_name = parts.at(0);
+ LOG((CLOG_INFO "subscription is valid, registered to %s",
+ key.m_name.c_str()));
+ return;
+ }
+ else if ((parts.size() == 2) && (parts.at(0) == kApplication)) {
+ key.m_name = parts.at(1);
+ LOG((CLOG_INFO "subscription is valid, registered to %s",
+ key.m_name.c_str()));
+ return;
+ }
+
+ throw XSubscription(synergy::string::sprintf("Serial is invalid."));
+}
+
+String
+SubscriptionManager::getFilename()
+{
+ String path = ARCH->getUserDirectory();
+ path = ARCH->concatPath(path, kFile);
+ if (path.empty()) {
+ throw XSubscription("Could not get filename.");
+ }
+
+ return path;
+}
+
+void
+SubscriptionManager::printFilename()
+{
+ std::cout << getFilename() << std::endl;
+}
diff --git a/src/lib/synergy/SubscriptionManager.h b/src/lib/synergy/SubscriptionManager.h
new file mode 100644
index 00000000..bf86f7c1
--- /dev/null
+++ b/src/lib/synergy/SubscriptionManager.h
@@ -0,0 +1,42 @@
+/*
+ * synergy -- mouse and keyboard sharing utility
+ * Copyright (C) 2015 Synergy Seamless Inc.
+ *
+ * This package is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * found in the file LICENSE that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "SubscriptionKey.h"
+#include "common/common.h"
+
+class SubscriptionManager {
+public:
+ SubscriptionManager();
+
+ //! Check the subscription activation file
+ void checkFile(const String& filename);
+
+ //! Create a subscription activation file based on a serial
+ void activate(const String& serial);
+
+ //! Use standard output to return subscription filename to gui
+ void printFilename();
+
+private:
+ String decode(const String& input);
+ void parsePlainSerial(const String& plainText, SubscriptionKey& key);
+ String getFilename();
+
+ SubscriptionKey m_key;
+};
diff --git a/src/lib/synergy/XSynergy.h b/src/lib/synergy/XSynergy.h
index a6361d90..78d4d86f 100644
--- a/src/lib/synergy/XSynergy.h
+++ b/src/lib/synergy/XSynergy.h
@@ -23,6 +23,12 @@
//! Generic synergy exception
XBASE_SUBCLASS(XSynergy, XBase);
+//! Subscription error
+/*!
+Thrown when there is a problem with the subscription.
+*/
+XBASE_SUBCLASS(XSubscription, XSynergy);
+
//! Client error exception
/*!
Thrown when the client fails to follow the protocol.