mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-08 04:20:09 +00:00
Added expiry time support to the juce_tracktion_marketplace module
This commit is contained in:
parent
05835ecfa1
commit
49989308b7
4 changed files with 124 additions and 28 deletions
|
|
@ -51,6 +51,22 @@ public:
|
|||
const String& machineNumbers,
|
||||
const RSAKey& privateKey);
|
||||
|
||||
/** Similar to the above key file generation method but with an expiry time.
|
||||
You must supply a Time after which this key file should no longer be considered as active.
|
||||
|
||||
N.B. when an app is unlocked with an expiring key file, OnlineUnlockStatus::isUnlocked will
|
||||
still return false. You must then check OnlineUnlockStatus::getExpiryTime to see if this
|
||||
expiring key file is still in date and act accordingly.
|
||||
|
||||
@see OnlineUnlockStatus
|
||||
*/
|
||||
static String JUCE_CALLTYPE generateExpiringKeyFile (const String& appName,
|
||||
const String& userEmail,
|
||||
const String& userName,
|
||||
const String& machineNumbers,
|
||||
const Time expiryTime,
|
||||
const RSAKey& privateKey);
|
||||
|
||||
//==============================================================================
|
||||
/** This is a simple implementation of a key-generator that you could easily wrap in
|
||||
a command-line main() function for use on your server.
|
||||
|
|
|
|||
|
|
@ -207,12 +207,12 @@ void OnlineUnlockForm::resized()
|
|||
passwordBox.setInputRestrictions (64);
|
||||
passwordBox.setFont (font);
|
||||
|
||||
r.removeFromBottom (30);
|
||||
r.removeFromBottom (20);
|
||||
emailBox.setBounds (r.removeFromBottom (boxHeight));
|
||||
emailBox.setInputRestrictions (512);
|
||||
emailBox.setFont (font);
|
||||
|
||||
r.removeFromBottom (30);
|
||||
r.removeFromBottom (20);
|
||||
|
||||
message.setBounds (r);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,42 @@
|
|||
|
||||
struct KeyFileUtils
|
||||
{
|
||||
static XmlElement createKeyFileContent (const String& appName,
|
||||
const String& userEmail,
|
||||
const String& userName,
|
||||
const String& machineNumbers,
|
||||
const String& machineNumbersAttributeName)
|
||||
{
|
||||
XmlElement xml ("key");
|
||||
|
||||
xml.setAttribute ("user", userName);
|
||||
xml.setAttribute ("email", userEmail);
|
||||
xml.setAttribute (machineNumbersAttributeName, machineNumbers);
|
||||
xml.setAttribute ("app", appName);
|
||||
xml.setAttribute ("date", String::toHexString (Time::getCurrentTime().toMilliseconds()));
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
static String createKeyFileComment (const String& appName,
|
||||
const String& userEmail,
|
||||
const String& userName,
|
||||
const String& machineNumbers)
|
||||
{
|
||||
String comment;
|
||||
comment << "Keyfile for " << appName << newLine;
|
||||
|
||||
if (userName.isNotEmpty())
|
||||
comment << "User: " << userName << newLine;
|
||||
|
||||
comment << "Email: " << userEmail << newLine
|
||||
<< "Machine numbers: " << machineNumbers << newLine
|
||||
<< "Created: " << Time::getCurrentTime().toString (true, true);
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static String encryptXML (const XmlElement& xml, RSAKey privateKey)
|
||||
{
|
||||
MemoryOutputStream text;
|
||||
|
|
@ -93,10 +129,10 @@ struct KeyFileUtils
|
|||
return decryptXML (keyFileText.fromLastOccurrenceOf ("#", false, false).trim(), rsaPublicKey);
|
||||
}
|
||||
|
||||
static StringArray getMachineNumbers (XmlElement xml)
|
||||
static StringArray getMachineNumbers (XmlElement xml, StringRef attributeName)
|
||||
{
|
||||
StringArray numbers;
|
||||
numbers.addTokens (xml.getStringAttribute ("mach"), ",; ", StringRef());
|
||||
numbers.addTokens (xml.getStringAttribute (attributeName), ",; ", StringRef());
|
||||
numbers.trim();
|
||||
numbers.removeEmptyStrings();
|
||||
return numbers;
|
||||
|
|
@ -110,6 +146,9 @@ struct KeyFileUtils
|
|||
{
|
||||
String licensee, email, appID;
|
||||
StringArray machineNumbers;
|
||||
|
||||
bool keyFileExpires;
|
||||
Time expiryTime;
|
||||
};
|
||||
|
||||
static KeyFileData getDataFromKeyFile (XmlElement xml)
|
||||
|
|
@ -119,7 +158,18 @@ struct KeyFileUtils
|
|||
data.licensee = getLicensee (xml);
|
||||
data.email = getEmail (xml);
|
||||
data.appID = getAppID (xml);
|
||||
data.machineNumbers.addArray (getMachineNumbers (xml));
|
||||
|
||||
if (xml.hasAttribute ("expiryTime") && xml.hasAttribute ("expiring_mach"))
|
||||
{
|
||||
data.keyFileExpires = true;
|
||||
data.machineNumbers.addArray (getMachineNumbers (xml, "expiring_mach"));
|
||||
data.expiryTime = Time (xml.getStringAttribute ("expiryTime").getHexValue64());
|
||||
}
|
||||
else
|
||||
{
|
||||
data.keyFileExpires = false;
|
||||
data.machineNumbers.addArray (getMachineNumbers (xml, "mach"));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
|
@ -127,6 +177,7 @@ struct KeyFileUtils
|
|||
|
||||
//==============================================================================
|
||||
const char* OnlineUnlockStatus::unlockedProp = "u";
|
||||
const char* OnlineUnlockStatus::expiryTimeProp = "t";
|
||||
static const char* stateTagName = "REG";
|
||||
static const char* userNameProp = "user";
|
||||
static const char* keyfileDataProp = "key";
|
||||
|
|
@ -183,11 +234,22 @@ void OnlineUnlockStatus::load()
|
|||
KeyFileUtils::KeyFileData data;
|
||||
data = KeyFileUtils::getDataFromKeyFile (KeyFileUtils::getXmlFromKeyFile (status[keyfileDataProp], getPublicKey()));
|
||||
|
||||
if (! doesProductIDMatch (data.appID))
|
||||
status.removeProperty (unlockedProp, nullptr);
|
||||
if (data.keyFileExpires)
|
||||
{
|
||||
if (! doesProductIDMatch (data.appID))
|
||||
status.removeProperty (expiryTimeProp, nullptr);
|
||||
|
||||
if (! machineNumberAllowed (data.machineNumbers, localMachineNums))
|
||||
status.removeProperty (unlockedProp, nullptr);
|
||||
if (! machineNumberAllowed (data.machineNumbers, localMachineNums))
|
||||
status.removeProperty (expiryTimeProp, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! doesProductIDMatch (data.appID))
|
||||
status.removeProperty (unlockedProp, nullptr);
|
||||
|
||||
if (! machineNumberAllowed (data.machineNumbers, localMachineNums))
|
||||
status.removeProperty (unlockedProp, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void OnlineUnlockStatus::save()
|
||||
|
|
@ -289,7 +351,7 @@ bool OnlineUnlockStatus::applyKeyFile (String keyFileContent)
|
|||
{
|
||||
setUserEmail (data.email);
|
||||
status.setProperty (keyfileDataProp, keyFileContent, nullptr);
|
||||
status.removeProperty (unlockedProp, nullptr);
|
||||
status.removeProperty (data.keyFileExpires ? expiryTimeProp : unlockedProp, nullptr);
|
||||
|
||||
var actualResult (0), dummyResult (1.0);
|
||||
var v (machineNumberAllowed (data.machineNumbers, getLocalMachineIDs()));
|
||||
|
|
@ -298,6 +360,14 @@ bool OnlineUnlockStatus::applyKeyFile (String keyFileContent)
|
|||
dummyResult.swapWith (v);
|
||||
jassert (! dummyResult);
|
||||
|
||||
if (data.keyFileExpires)
|
||||
{
|
||||
if ((! dummyResult) && actualResult)
|
||||
status.setProperty (expiryTimeProp, data.expiryTime.toMilliseconds(), nullptr);
|
||||
|
||||
return getExpiryTime().toMilliseconds() > 0;
|
||||
}
|
||||
|
||||
if ((! dummyResult) && actualResult)
|
||||
status.setProperty (unlockedProp, actualResult, nullptr);
|
||||
|
||||
|
|
@ -395,23 +465,24 @@ String KeyGeneration::generateKeyFile (const String& appName,
|
|||
const String& machineNumbers,
|
||||
const RSAKey& privateKey)
|
||||
{
|
||||
XmlElement xml ("key");
|
||||
|
||||
xml.setAttribute ("user", userName);
|
||||
xml.setAttribute ("email", userEmail);
|
||||
xml.setAttribute ("mach", machineNumbers);
|
||||
xml.setAttribute ("app", appName);
|
||||
xml.setAttribute ("date", String::toHexString (Time::getCurrentTime().toMilliseconds()));
|
||||
|
||||
String comment;
|
||||
comment << "Keyfile for " << appName << newLine;
|
||||
|
||||
if (userName.isNotEmpty())
|
||||
comment << "User: " << userName << newLine;
|
||||
|
||||
comment << "Email: " << userEmail << newLine
|
||||
<< "Machine numbers: " << machineNumbers << newLine
|
||||
<< "Created: " << Time::getCurrentTime().toString (true, true);
|
||||
XmlElement xml (KeyFileUtils::createKeyFileContent (appName, userEmail, userName, machineNumbers, "mach"));
|
||||
const String comment (KeyFileUtils::createKeyFileComment (appName, userEmail, userName, machineNumbers));
|
||||
|
||||
return KeyFileUtils::createKeyFile (comment, xml, privateKey);
|
||||
}
|
||||
|
||||
String KeyGeneration::generateExpiringKeyFile (const String& appName,
|
||||
const String& userEmail,
|
||||
const String& userName,
|
||||
const String& machineNumbers,
|
||||
const Time expiryTime,
|
||||
const RSAKey& privateKey)
|
||||
{
|
||||
XmlElement xml (KeyFileUtils::createKeyFileContent (appName, userEmail, userName, machineNumbers, "expiring_mach"));
|
||||
xml.setAttribute ("expiryTime", String::toHexString (expiryTime.toMilliseconds()));
|
||||
|
||||
String comment (KeyFileUtils::createKeyFileComment (appName, userEmail, userName, machineNumbers));
|
||||
comment << "Expires: " << expiryTime.toString (true, true);
|
||||
|
||||
return KeyFileUtils::createKeyFile (comment, xml, privateKey);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,15 @@ public:
|
|||
changed by a cracker in order to unlock your app, so the more places you call this
|
||||
method, the more hassle it will be for them to find and crack them all.
|
||||
*/
|
||||
inline var isUnlocked() const { return status[unlockedProp]; }
|
||||
inline var isUnlocked() const { return status[unlockedProp]; }
|
||||
|
||||
/** Returns the Time when the keyfile expires.
|
||||
|
||||
If a the key file obtained has an expiry time, isUnlocked will return false and this
|
||||
will return a non-zero time. The interpretation of this is up to your app but could
|
||||
be used for subscription based models or trial periods.
|
||||
*/
|
||||
inline Time getExpiryTime() const { return Time (static_cast<int64> (status[expiryTimeProp])); }
|
||||
|
||||
/** Optionally allows the app to provide the user's email address if
|
||||
it is known.
|
||||
|
|
@ -244,6 +252,7 @@ private:
|
|||
UnlockResult handleFailedConnection();
|
||||
|
||||
static const char* unlockedProp;
|
||||
static const char* expiryTimeProp;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OnlineUnlockStatus)
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue