PLUSNative: Background Checking and Refreshing Licenses

Checking the status of an activated license by calling SOLO Server web services is an effective and efficient way to check if activated licenses remain authorized. These checks can prevent copies of your application from running in scenarios like the following.

In each case, the background check will verify the system on which the application was activate is still authorized by verifying the status of the license and the individual system's activation are still valid.  If the background check indicates the license is no longer authorized for the activated system, then your application can take steps needed to disable itself as needed. This is especially important to securely enforce deactivations.

Important

Be mindful about whether or not disabling access to your application is appropriate.  For example, if your application requires high availability (such as a web application or service) and/or your application serves some mission-critical function, it may be more appropriate to have your application display nag messages and/or disable only non-critical features so as to avoid disrupting your customers' business or operation.

Important

When using background checking or license refreshing features, it is very important that you test this functionality in your application while running in an environment where connections are refused or unavailable, and also where connections time out. Doing so can help you identify areas of your application that may become unresponsive or unstable when your application encounters these conditions.

Background Checking

The XML Activation Service web service has a CheckInstallationStatus method, which your application can leverage to check the status of the license and the status of the system's activation. This is particularly ideal when your application needs to check its status frequently (such as every time the application runs, or every time a critical feature of your application is used if it is always running).

Generating the Request

The following code snippet demonstrates generating the activation request:

C/C++
//declare variables
SK_ResultCode result = SK_ERROR_NONE;
SK_ApiContext context = NULL;
SK_XmlDoc request = NULL;
SK_XmlDoc response = NULL;
int resultCode = 0;
int statusCode = 0;
SK_XmlDoc license = NULL;

//initialize API context (usually called during application start-up)
//refer to the Configuring the API Context topic for instructions

//generate the activation request
result = SK_SOLO_CheckInstallationStatusGetRequest(context, SK_FLAGS_NONE, "UB6AL-MAVGB-A2YWX-2Q4QL-UCKMB-2", &request, NULL);

The above request is generated using fictional, hard-coded Installation ID for demonstration purposes. The code snippet above omits many of the parameters necessary to initialize the API context (the context variable). Refer to the Configuring the API Context topic for instructions and a complete example of calling the SK_ApiContextInitialize function.

Calling the Web Service

The code snippet below simply calls the web service using the request we built above. The request will be encrypted and digitally signed with the call to SK_CallXmlWebService provided the API Context has been configured to use the SK_FLAGS_USE_ENCRYPTION and SK_FLAGS_USE_SIGNATURE flags globally.

C/C++
//call the web service
result = SK_CallXmlWebService(context, SK_FLAGS_NONE, SK_CONST_WEBSERVICE_CHECKINSTALLATION_URL, request, &response, &resultCode, &statusCode);

//free our request document
SK_XmlDocumentDispose(SK_FLAGS_NONE, &request);

//check the result
if (SK_ERROR_NONE != result)
{
//handle error condition
...
}

The above call uses the SK_CONST_WEBSERVICE_CHECKINSTALLATION_URL constant that is defined for use with SOLO Server Shared URL. If using SOLO Server Custom URL, Dedicated URL, or Self-Hosted, define and use a new constant in your application's source code which points to your specific domain.

Determining the Result

When SK_CallXmlWebService returns 0 (zero), a valid response with no errors was received.

If you receive a result of SK_ERROR_WEBSERVICE_RETURNED_FAILURE, this indicates we successfully received a response, but SOLO Server encountered errors while processing the request. SOLO Server's web service error condition is returned in the resultCode parameter. References are available online for possible SOLO Server result codes. Optionally, a human-readable error message that can be extracted from the response document's ErrorMessage node.

Important

This basic example omits necessary error checking for the sake of clarity. Many of the functions used could fail for one reason or another, and it's important you make sure the function call succeeds before passing the result from one function to another. Otherwise, you may not be able to tell exactly where the problem occurred.

Refreshing Licenses

The XML License File Service web service has a GetLicenseFile method, which your application can leverage to check the status of the license and the status of the system's activation while simultaneously retrieving an updated copy of the license file. Since the license contains details about when the license expires, the customer who owns the license, custom data you specified, and much more, this is an easy way to get a complete update to pull down any and all changes all in a single web service call.

Determine if a License Refresh is Due

Refreshing the license file requires much more server and application resources, so it is important to avoid using this feature too frequently in your protected applications.

All of the Protection PLUS 5 SDK samples include settings or properties which you may reference and re-configure as needed for your application. For more flexibility, we recommend using the License Validation Options in SOLO Server, as this allows you to configure the refresh frequency values at the Product Option or License level instead of hard-coding them into your application. You can then easily adjust the frequency of when a license refresh is due without having to update your source code and deploy a new build.

The available License Validation Options are:

These values will be added to the license file if the License in SOLO Server is configured with License Validation Options. The license file also contains a SignatureDate property, which tells you when the license file was last issued by SOLO Server. This date field, along with the License Validation Options data allows you to implement logic to only validate with SOLO Server after a certain amount of time has passed:

C/C++
BOOL IsRefreshLicenseAttemptDue(SK_XmlDoc license)
{
char* signatureDate = NULL;
int dateDiff = 0;
BOOL refreshLicenseAlwaysRequired = FALSE;
char* refreshLicenseAlwaysRequiredString = NULL;
int refreshLicenseAttemptFrequency = 0;
int refreshLicenseRequireFrequency = 0;

//Read the License Configuration Options values from the license
SK_XmlNodeGetValueString(SK_FLAGS_NONE, license, "/SoftwareKey/PrivateData/License/LicenseValidationOptions/RefreshLicenseAlwaysRequired", SK_FALSE, &refreshLicenseAlwaysRequiredString);
refreshLicenseAlwaysRequired = strcmp("True", refreshLicenseAlwaysRequiredString) == 0 ? TRUE : FALSE;
SK_StringDispose(SK_FLAGS_NONE, &refreshLicenseAlwaysRequiredString);

SK_XmlNodeGetValueInt(SK_FLAGS_NONE, license, "/SoftwareKey/PrivateData/License/LicenseValidationOptions/RefreshLicenseAttemptFrequency", &refreshLicenseAttemptFrequency);
SK_XmlNodeGetValueInt(SK_FLAGS_NONE, license, "/SoftwareKey/PrivateData/License/LicenseValidationOptions/RefreshLicenseRequireFrequency", &refreshLicenseRequireFrequency);

//Read the SignatureDate from the license
SK_XmlNodeGetValueDateTimeString(SK_FLAGS_NONE, license, "/SoftwareKey/PrivateData/License/SignatureDate", &signatureDate);

//Calculate the number of days since the signature date
SK_DateTimeDaysRemaining(SK_FLAGS_NONE, signatureDate, &dateDiff);
dateDiff = -dateDiff;
SK_StringDispose(SK_FLAGS_NONE, &signatureDate);

if (refreshLicenseAlwaysRequired ||
(refreshLicenseAttemptFrequency > 0 &&
(dateDiff > refreshLicenseAttemptFrequency ||
(refreshLicenseRequireFrequency > 0 && dateDiff > refreshLicenseRequireFrequency))))
{
//license refresh is due
return true;
}
return false;
}
C/C++

BOOL IsRefreshLicenseRequired(SK_XmlDoc license)
{
char* signatureDate = NULL;
int dateDiff = 0;
BOOL refreshLicenseAlwaysRequired = FALSE;
int refreshLicenseRequireFrequency = 0;
char* refreshLicenseAlwaysRequiredString = NULL;

//Read the License Configuration Options values from the license
SK_XmlNodeGetValueString(SK_FLAGS_NONE, license, "/SoftwareKey/PrivateData/License/LicenseValidationOptions/RefreshLicenseAlwaysRequired", SK_FALSE, &refreshLicenseAlwaysRequiredString);
refreshLicenseAlwaysRequired = strcmp("True", refreshLicenseAlwaysRequiredString) == 0 ? TRUE : FALSE;
SK_StringDispose(SK_FLAGS_NONE, &refreshLicenseAlwaysRequiredString);

SK_XmlNodeGetValueInt(SK_FLAGS_NONE, license, "/SoftwareKey/PrivateData/License/LicenseValidationOptions/RefreshLicenseRequireFrequency", &refreshLicenseRequireFrequency);

//Read the SignatureDate from the license
SK_XmlNodeGetValueDateTimeString(SK_FLAGS_NONE, license, "/SoftwareKey/PrivateData/License/SignatureDate", &signatureDate);

//Calculate the number of days since the signature date
SK_DateTimeDaysRemaining(SK_FLAGS_NONE, signatureDate, &dateDiff);
dateDiff = -dateDiff;
SK_StringDispose(SK_FLAGS_NONE, &signatureDate);

if (refreshLicenseAlwaysRequired ||
(refreshLicenseRequireFrequency > 0 && dateDiff > refreshLicenseRequireFrequency))
{
return true;
}
return false;
}

Generating the Request

The following code snippet demonstrates generating the activation request:

C/C++
//declare variables
SK_ResultCoderesult = SK_ERROR_NONE;
SK_ApiContextcontext = NULL;
SK_XmlDocrequest = NULL;
SK_XmlDocresponse = NULL;
intresultCode = 0;
intstatusCode = 0;
SK_XmlDoclicense = NULL;

//initialize API context (usually called during application start-up)
//refer to the Configuring the API Context topic for instructions

//generate the activation request
result = SK_SOLO_GetLicenseFileGetRequest(context, SK_FLAGS_NONE, "UB6AL-MAVGB-A2YWX-2Q4QL-UCKMB-2", &request, NULL);

The above request is generated using fictional, hard-coded Installation ID for demonstration purposes. The code snippet above omits many of the parameters necessary to initialize the API context (the context variable). Refer to the Configuring the API Context topic for instructions and a complete example of calling the SK_ApiContextInitialize function.

Calling the Web Service

The code snippet below simply calls the web service using the request we built above. The request will be encrypted and digitally signed with the call to SK_CallXmlWebService provided the API Context has been configured to use the SK_FLAGS_USE_ENCRYPTION and SK_FLAGS_USE_SIGNATURE flags globally.

C/C++
//call the web service
result = SK_CallXmlWebService(context, SK_FLAGS_NONE, SK_CONST_WEBSERVICE_GETLICENSEFILE_URL, request, &response, &resultCode, &statusCode);

//free our request document
SK_XmlDocumentDispose(SK_FLAGS_NONE, &request);

//check the result
if (SK_ERROR_NONE != result)
{
//handle error condition
...
}

The above call uses the SK_CONST_WEBSERVICE_GETLICENSEFILE_URL constant that is defined for use with SOLO Server. If using SOLO Server Custom URL, Dedicated URL, or Self-Hosted, define and use a new constant in your application's source code which points to your specific domain.

Determining the Result

When SK_CallXmlWebService returns 0 (zero), a valid response with no errors was received.

If you receive a result of SK_ERROR_WEBSERVICE_RETURNED_FAILURE, this indicates we successfully received a response, but SOLO Server encountered errors while processing the request. SOLO Server's web service error condition is returned in the resultCode parameter. References are available online for possible SOLO Server result codes. Optionally, a human-readable error message that can be extracted from the response document's ErrorMessage node.

The code snippet below continues the previous code snippet (calling the web service), and checks the return value error.

C/C++
//...the web service was previously called
//check the result
if (SK_ERROR_NONE != result)
{
//handle error condition
if (5015 == resultCode|| 5016 == resultCode|| 5017 == resultCode|| 5010 == resultCode)
{
//the license is no longer valid//deleting the license file is appropriate when using read-only license files
//if you use writable license files, you will need to add logic to update the license file to correctly
//reflect the new state of the license (i.e. you could make it act like an expired evaluation/trial)
}
else
{
//handle error condition
...
}
}

If the result from SK_CallXmlWebService returned success, you will retrieve the new license from the web service response and replace the previous license file. The code snippet below demonstrates this.

C/C++
SK_XmlDoc license = NULL;
char *filename = ...;
//...the web service was previously called with a successful result
//retrieve the license from the web service response
result= SK_XmlNodeGetDocument(SK_FLAGS_NONE, response, "/GetLicenseFile/PrivateData/License", &license);
//check the result
if (SK_ERROR_NONE != result)
{
//handle error condition
...
}
//save the license as read-only
//if you are using writable licenses, be sure to set the license to writable before saving
//for writable licenses: SK_ApiContextSetFieldInt(context, SK_FLAGS_NONE, SK_APICONTEXT_LICENSE_WRITABLE, SK_TRUE);
result = SK_PLUS_LicenseFileSave(context, SK_FLAGS_NONE, filename, license));
if (SK_ERROR_NONE != result)
{
//handle error condition
...
}
Important

This basic example omits necessary error checking for the sake of clarity. Many of the functions used could fail for one reason or another, and it's important you make sure the function call succeeds before passing the result from one function to another. Otherwise, you may not be able to tell exactly where the problem occurred.

Revoking or Removing a License

When determining the result of a web service call made to either CheckInstallationStatus or GetLicenseFile, you may decide that certain responses received should revoke or remove your application's license. For example, you might take this sort of action when you receive a result code of 5010 (invalid Product ID), 5015 (invalid Installation ID), 5016 (deactivated Installation ID), or 5017 (invalid license status). Deleting the license file is appropriate when using read-only license files. However, if you use writable license files, you will need to add logic to update the license file to correctly reflect the new state of the license (i.e. you could make it act like an expired evaluation/trial).

Important

Be mindful about whether or not disabling access to your application is appropriate.  For example, if your application requires high availability (such as a web application or service) and/or your application serves some mission-critical function, it may be more appropriate to have your application display nag messages and/or disable only non-critical features so as to avoid disrupting your customers' business or operation.

Manually Refreshing a License

There may be cases where a user does not have direct access to the Internet, yet there is a requirement to allow license refreshing. In these cases, a manual refresh can be performed. To perform a refresh, it’s nearly identical to doing manual activations, except you’ll use SK_SOLO_GetLicenseFileGetRequest to generate the request data the user will copy to a system with Internet access, then process the response data as you would for an online refresh as shown above.