|Issue 59 October 22 2008|
Build a Registration Scheme Part II
In Part I of this article, I discussed the scripts needed to create a registration scheme. In Part II, I will show how to create a simple interface for the registration scheme, how to save the user data to disk, and how to check whether the software has been registered when a new user session starts.
Please note that the system described here is merely meant as an illustration of the concept. You should not take the code and stacks provided here and use them in your own project, without modifying them to make your registration system unique and more secure. Just like in the previous part, I have kept the scripts as simple as possible.
To demonstrate the use of the registration scheme, I have created a very tiny stack. This stack lets you take a screenshot and saves the screenshot to disk. The stack consists of one mainstack and two substacks. One substack is called “Screen Shot”, the other stack is called “Register”.
When the mainstack opens, it hides itself and shows the stack Screen Shot. This is similar to the so-called “splash-stack approach”, which has been discussed frequently on the Revolution Use List and the Forum. I won’t discuss this any further now, because it falls beyond the scope of this article.
Later, we will need the scripts from stack “Register”. With the start using command, I make its scripts available to all other stacks. The following line is included in a preOpenStack handler of the script of card 1 of stack “Main”:
start using stack Register
The stack Screen Shot contains three buttons. Two give access to the main functions of the stack. The third button, which is labelled “Register”, opens the registration stack. Before it opens the registration stack, it needs to check whether the programme has been registered already. For this check, we use the function checkLicense, which returns true if the programme has been registered and false otherwise. The script of button “Register” of stack “Screen Shot” follows here, the checkLicense function is discussed later in the text.
on mouseUp if not checkLicense() then go stack "Register" in new window else beep answer information \ "This programme has been registered already." with "Okay" end if end mouseUp
If the programme has been registered already, the script doesn’t open the registration window but shows an answer dialog with a message instead.
The stack Register just contains a field, a button to register the software and a button to close the stack. I have also added a field with some explanatory text. If the user decides not to register yet, he or she can click the Cancel button, which closes the stack without any further action.
on mouseUp close this stack end mouseUp
The user will paste the license code into the field. When the user clicks the Register button, we need to check whether the license code is correct. The script of the Register button follows here.
on mouseUp if validateLicense(fld 1) then saveLicense fld 1 close this stack beep answer information "Thank you for registering." with "Okay" else beep answer error "This license code is incorrect." with "Okay" end if end mouseUp
If the license code appears to be correct, we can save it in a preferences file. The saveLicense handler does this. Before we can write the saveLicense script, we need to have a function that determines where we can save our preferences file. Here, I present one of many ways to do this. I don’t use the specialFolderPath function, because this function needs a different parameter on every platform. To retrieve the path to a location for a preferences file, I prefer to get the information directly.
For Mac OS X, I use a string, which can be used by the operating system to find the current user’s preferences folder: “~/library/preferences”, on Windows, I use the $APPDATA environment variable and on Linux I use the $HOME environment variable. This produces the location where the preferences file can be stored. Subsequently, a slash is added. On Linux and Mac OS X, we also add a period, to make the file invisible to modal computer users. Finally, we add the name of the file. The whole procedure takes a script of only 11 lines, including the definition of the constant dot.
constant dot = "." function prefsFile if the platform is "MacOS" then put "~/library/preferences/" & dot into myFolder else if the platform is "Win32" then put replaceText($APPDATA,backslash,slash) into myFolder else if the platform is "Linux" then put $HOME & slash & dot into myFolder end if return myFolder & "revscreenshot.pref" end prefsFile
The prefsFile function resides in the script of stack Register. Whenever I need to refer to the preferences file, I simply type prefsFile(). You can copy this function to your own project, but make sure to change revscreenshot.pref into a different, presumably unique file name.
Now that we know where to store the preferences file, we can determine the format for this file. For slightly more complex programmes, you might want to use a pseudo-XML format, but since we are dealing with a very simple application that doesn’t need any additional preferences, I will just store the license key in the file. To prevent search utilities from finding the file by contents, I use the compress function to scramble the information in the file.
local lKey on saveLicense theLicense put prefsFile() into myFile open file myFile for binary write write compress(theLicense) to file myFile close file myFile put theLicense into lKey end saveLicense
You can find this script in the script of stack Register. As you can see at the end of the saveLicense script, we store the license key in a file as well as in a local variable. By storing the key in a variable, we don’t need to read it from disk every time when we want to check whether the software has been registered (see below).
The registration scheme is almost complete now. It just needs one more function, which checks whether the software has been registered. Let’s call this function checkLicense and add it to the script of stack Register. The checkLicense function starts with checking whether the software has been registered during the current user session or whether the license has already been checked during the current session. So, first it checks the local variable lKey (which we defined when writing the saveLicense script).
function checkLicense if lKey is empty then
If the variable lKey is empty, a valid license key cannot have been entered during the current session and the license check cannot have been performed already.
Next, we need to find the preferences file, read the license key from it and decompress it. Because compressed data is binary, I open the file “for binary read”.
put prefsFile() into myFile open file myFile for binary read read from file myFile until EOF close file myFile
We need to check whether the file actually contains any data to avoid an execution error. The decompress function can’t handle empty variables.
if it is not empty then put decompress(it) into myLicense end if
I store the license key into the local variable myLicense. If lKey is not empty, we don’t need to read the license key from disk and can store it into myLicense directly.
else put lKey into myLicense end if
Now follows the part that actually checks the validity of the license key. We use the function validateLicense (see Part I) to check the license key.
if validateLicense(myLicense) then put myLicense into lKey return true
If the key stored in myLicense is valid, it is stored in the local variable lKey and the function returns true.
Now we can close the standalone application and reopen it. When the application checks the license for the first time, it reads the preferences file, stores the license key and returns true if it is valid. If, however, the licence key is invalid, it is not stored and the function returns false.
else return false end if end checkLicense
Below follows the checkLicense function as a whole.
function checkLicense if lKey is empty then put prefsFile() into myFile open file myFile for binary read read from file myFile until EOF close file myFile if it is not empty then put decompress(it) into myLicense end if else put lKey into myLicense end if if validateLicense(myLicense) then put myLicense into lKey return true else return false end if end checkLicense
You might wonder why we don’t simply store true or false in a global variable after checking the license key once. The reason is that such a variable is easily manipulated. This might render unregistered copies of the software completely functional. By calling the checkLicense function, we demand at least some minimal hacking skills from the hacker.
Now we are ready to impose a limitation on the demo version of the software. There are several ways to do this. Limitations can be time-based or functional, or both. A time-based limitation may cause an application to shut down after a particular amount of time. A functional limitation prevents the user from saving his or her work. Another functional limitation is the display of a message, saying that the software hasn’t been registered, every 5 minutes. I will discuss one simple functional limit in the remainder of this article.
Our programme creates a screenshot and stores this in an image object. To save the screen shot, I have created a script to write the text property of the image object to a file. Let’s allow the user to create a screenshot, but not save it as long as the programme has not been registered. This is a very easy limitation to implement. Just use the checkLicense function to make sure that the user has a valid license, before saving the screenshot. Have a look at the script of the Save button of stack “Screen Shot”.
First, the script uses the checkLicense function to check whether the software has been registered. Next, the user is asked to select a location for the file and to choose a file name for the file, after which the text of the image is written to the file.
on mouseUp if checkLicense() then ask file "Save screenshot as..." with "Untitled.png" if it is not empty then put the text of img 1 into url ("binfile:" & it) end if else beep answer error \ "Sorry, this programme has not been registered." with \ "Okay" end if end mouseUp
If the software has not been registered, the script displays a message in an answer dialogue window.
The same principle can easily be applied to many functions of the software. Whenever the user presses a button, pastes a text, selects a menu item, or closes a window, you might call the checkLicense function. It is not recommended to do this with too many features of your software, though. The user needs to get an opportunity to discover that the features offered by your product work well.
If you want to experiment with the scripts in this and the previous article, you can download a stack here. This stack contains all of the scripts discussed in the articles and a few more to make it a complete, useable utility.
Use the licenseKey function from Part I to create a license key for this stack. Just in case you are testing the stack and want to unregister it, here’s a bonus script.
on unregister delete file prefsFile() put empty into lKey show btn "Register" of stack "Screen Shot" end unregister
This script deletes the file with the license key and clears the variable lKey. After running this script from the message box, you can click the Register button of stack Screen Shot again. You can find the unregister handler in the script of stack Register.
Please, don’t forget to protect your stacks with a password. Password protection encrypts the scripts in your stack and makes the license scheme inaccessible to prying eyes.
The scripts presented in this article are, with slight modifications, sufficient for hobbyists to add to their own, small shareware projects. If you want to secure a large commercial project, I highly recommend hiring a pro who knows all the peculiarities of protecting your hard work. A professional knows how to make the scripts sufficiently complex, how to hide the scripts from prying eyes, and how to tie a license key with a user name, e-mail address and hardware. If you have any questions about this article, you can send them to firstname.lastname@example.org. Many thanks to Franz Böhmisch for comments on Part Ii of this article. Thank you all, who sent me useful comments on Part I!