Tel: +44(0)1865 300 579
Fax: +44(0)1865 300 232

Programs for Programmers

Polyhedron Licensing System - Version 1.5

Overview

Polyhedron Licensing System (PLS) is a simple but flexible package for controlling the use of licensed software without the expense or inconvenience of "dongle" or licence server systems.

When using PLS, the vendor may distribute the software freely, but users will find it inoperative (or restricted) unless a special "licence file" is also installed. This is a plain text file, which normally contains the name of the user, details of the licensed application, and of any restrictions on its use. PLS detects any attempts to tamper with this file and optionally enforces restrictions on the date of the run and the serial number of the primary disk on the host machine.

An example licence file is shown below:

Licensor
Program Name
Licensee
Make one up
Facility Code
Expiry M/D/Y
Disk Serial
Authorization

: Sausage Software plc
: Sausage Simulation Environment 2.4
: John Jones, Newton Sausage Manufacturing Company
: Anything you like
: AABFQAAA
: 11/08/2006
: 2868-12f5
: KHNMDKOJLPGOLBAJ

The first 2 lines of the file contain the names of the licensor, and the licensed software. The software vendor has defined the keywords and values on the next 3 lines. There may be as many such lines as required, and the application may access and use them in many different ways. For example, the "Licensee" name could be displayed on the initial screen, or the "About" page to discourage illegal copying, and the "Facility Code" could be used to enable and disable facilities within the application.

The next two lines contain keywords that are recognised by PLS. ; "Expiry M/D/Y" is used to specify a licence expiry date (Expiry D/M/Y may also be used if you prefer European style dates), and "Disk Serial" is used to specify the serial number of the primary hard disk. PLS will report to the application if the expiry date has passed, or the disk serial is not as specified. The application may then display a message, or take whatever other action is required. These lines are optional, and if they are omitted, the corresponding check is not done. A full list of keywords that are recognised by PLS may be found in the next section.

The last line contains an authorization code, which depends on the other lines in the file, and can be checked by the licensed application as required. Any attempt to alter any part of the licence file (for example to extend a trial period, or change the name of the licensee) can be spotted, allowing the application to halt with an appropriate message. The user might then contact the vendor, and be sent a new licence file. Because the file is plain text, it can easily be sent by email or fax, or even dictated over the telephone.

From the end user's viewpoint, the system is very unobtrusive, and comes into play only when action is required. Extending or changing a licence involves extracting a small file from an email, or typing a few lines with a text editor.

Generating the Licence File

From the vendor’s viewpoint, the process is quite simple. The first step is to produce a base file containing the information to be protected. The file may consist of up 50 lines each containing a keyword, followed by a colon, followed by a value. The following example shows the base file which was used to create the above license file.

Licensee
Make one up
Facility Code
Expiry M/D/Y
Disk Serial

: John Jones, Newton Sausage Manufacturing Company
: Anything you like
: AABFQAAA
: 11/08/2006
: 2868-12f5

The base file may be created manually, using a text editor, or using purpose built software. Keywords are not case-sensitive, and spaces are ignored. Colons are not permitted within keywords. Certain keywords are reserved, because they have a special meaning. These are:-

Licensor
Program Name
DiskSerial
Expiry M/D/Y
Expiry D/M/Y
Authorization
Authorisation

The name of the software licensor
The name of the licensed program (not reserved in multi-program PLS installations)
used to specify that PLS should check the system drive disk serial number.
used to specify a licence expiry date (US format)
used to specify a licence expiry date (European format)
used by PLS to specify the authorization code
synonym for Authorization

If you wish to use the disk serial number check, you will have to ask the end-user for the disk serial number of the system drive on the target machine, so that it can be included in the file. Generally, the system drive is C:, but could be different if Windows is installed on a differnet disk. The serial number can be found by typing "dir %systemdrive%:" at a command prompt.

C:\-->dir %systemdrive%|more
Volume in drive C has no label.
Volume Serial Number is 2868-12f5

Note that the Serial Number of the system drive is required, even if the program is not installed on that drive.

Once the base file is ready, it can be converted to a licence file by running PLS_SEAL from a command prompt. PLS_SEAL uses standard input for the base file, and standard output for the protected license file

PLS_SEAL <original.fig >protected.fig

The protected licence file is identical to the original base file, but with Licensor, Program Name and Authorization lines added (Program Name is not added if you have a multi-program PLS installation).

Licensor
Program Name
Licensee
Make one up
Facility Code
Expiry M/D/Y
Disk Serial
Authorization

: Sausage Software plc
: Sausage Simulation Environment 2.4
: John Jones, Newton Sausage Manufacturing Company
: Anything you like
: AABFQAAA
: 11/08/2006
: 2868-12f5
: KHNMDKOJLPGOLBAJ

When this file is installed on the end-user’s machine, it activates the otherwise non-functional application.

PLS_SEAL is itself protected by a license file called PLS_SEAL.FIG. It is very important to keep this file secure, as it is the key which could allow anyone to generate valid license files for your program.

Linking PLS into your program

The current version of PLS includes versions for Intel Fortran, Compaq Visual Fortran, Salford FTN95 and Lahey LF95. The corresponding object files are named PLS_INTEL.OBJ, PLS_CVF.OBJ, PLS_SALF.OBJ and PLS_LF95.OBJ. Simply include the appropriate object file when you link your program.

e.g.

lf95 MYPROG.F90 PLS_LF95.OBJ

A simple test program called PLS_TEST.FOR is included with the distribution. We suggest that you compile and link this sample program to familiarise yourself with PLS.

In addition to these object files, a DLL (dynamic link library) version of PLS is included. This version can be called from Visual Basic, Delphi, Excel, or any other application that can call 32 bit DLLs. Example projects written using Delphi (PLS_DELPHI) and Visual Basic (PLS_VB) are included with the package.


API Specification (Fortran)

In order to use PLS, the licensed application should first call PLS_READFIG to read the contents of the licence file. After that, PLS_CHECKCODE may be called to validate the authorization code, expiry date, disk serial etc., and PLS_GETVALUE to retrieve values corresponding to specified keywords.


PLS_READFIG Subroutine

Interface

SUBROUTINE PLS_READFIG(Zfig,Code)
CHARACTER*(*) Zfig
INTEGER Code

Description

Reads the content of the specified configuration file. If the configuration file is in the same directory as the program executable, you can use PLS_GETHOMEDIRECTORY to help construct Zfig.

Arguments

Zfig
Code

The name of the configuration file to be read
The return code
 

0 ; ; ; ;
40 ; ; ; ;
41 ; ; ;
50 ; ; ;
51

No errors.
the unit on which Zfig was to be opened is in use.
unable to open Zfig.
Unable to parse a line in Zfig.
too many values (the limit is 100).


PLS_CHECKCODE Subroutine

Interface

SUBROUTINE PLS_CHECKCODE(Code)
INTEGER Code

Description

Checks the authorization code, and other keywords recognised by PLS

Arguments

Code

The return code
 

0
90
91
97
98
99

No errors.
there is no "AUTHORIZATION" line.
the Authorization code is the wrong length.
the disk serial number is incorrect.
the licence expiry date has passed
the Authorization code is incorrect.

Notes

The disk serial number is specified in the licence file using the "DISKSERIAL" keyword. Note that case and spacing are ignored

e.g.

Disk Serial : 2868-12f5

The expiry date is specified using either "EXPIRY D/M/Y" or "EXPIRY M/D/Y". In both cases the day moth and year are separated by ‘/’ characters. In the former, the order is day/month/year (European format), whereas the latter uses month/day/year (US format).

e.g.

Expiry D/M/Y : 31/12/2003
Expiry M/D/Y : 12/31/2003

PLS_GETVALUE Subroutine 

Interface

SUBROUTINE PLS_GETVALUE(Name,Value,Code)
CHARACTER*(*) Name , Value
INTEGER Code

Description

Returns the value corresponding to a specified keyword in the configuration file.

Arguments

Name
Value
Code

The name of the keyword to be queried
Returned with the value associated with the specified keyword.
The return code
 

0
99

No errors
The keyword could not be found

Notes

If the length of Value differs from the length of the string being returned, it is padded with trailing spaces or truncated as necessary.

PLS_GETENV Subroutine

Interface

SUBROUTINE PLS_GETENV(Name,Value)
CHARACTER*(*) Name , Value

Description

Returns the value corresponding to a specified system environment variable.

Arguments

Name
Value

The name of the environment variable to be queried
Returned with the value associated with the specified environment variable.

Notes

If the length of Value differs from the length of the string being returned, it is padded with trailing spaces or truncated as necessary.


PLS_GETVOLSER Subroutine

Interface

SUBROUTINE PLS_GETVOLSER(VolSer)
INTEGER VolSer

 

Description

Returns the volume serial number of the system drive on the current computer.

Arguments

VolSer the volume serial number.

 

Notes

In general the volume serial number is displayed as 2 sets of 4 hexadecimal digits, separated by a hyphen.

 

 PLS_GETHOMEDIRECTORY Subroutine

Interface

SUBROUTINE PLS_GETHOMEDIRECTORY(Home)
CHARACTER*(*) Home

Description

Returns the name of the directory containing the current program.

Arguments

Home returned with the name of the directory containing the current program, including a trailing ‘\’.

 

Notes

The licence file is normally installed into the same directory as the program it applies to. This routine gives the programmer a simple way to find the directory containing the executable, and hence the licence file.

 PLS_CHECKPOSTDATED Subroutine 

Interface

SUBROUTINE PLS_CHECKPOSTDATED(Directory,Filename,PostDated)
CHARACTER*(*) Directory , Filename
LOGICAL PostDated

Description

Scans the specified directory for postdated files (i.e. files with a creation, modification or last access time which is in the future (or after the date specified using PLS_SETPOSTDATE).

Arguments

Directory
FileName
PostDated

The name of the directory to be scanned
Returned with name of the first postdated file (if any).
.TRUE. if there is a postdated file

Notes

The existence of post-dated files suggests, but does not prove, that the end-user may have tampered with his/her clock, possibly with a view to bypassing license expiry conditions. It is recommended that the licensor treads carefully when using this check to enforce a license expiry date. There are many valid reasons why a user might alter the clock on his/her computer!

  PLS_SETPOSTDATE Subroutine

Interface

SUBROUTINE PLS_SETPOSTDATE(Year,Month,Day,Hour,Minute,Second)
INTEGER Year,Month,Day,Hour,Minute,Second

Description

Specifies the date and time beyond which PLS_CHECKPOSTDATED will regard a file as post-dated

Arguments

Year
Month
Day
Hour
Minute
Second

4 digits
1-12
1-31
0-23
0-59
0-59

Notes

If PLS_SETPOSTDATE is not called, PLS_CHECKPOSTDATED will use the current date and time for its checks.

 

Example Program

      PROGRAM PLS_TEST
      IMPLICIT NONE
      INTEGER icode , volser
      CHARACTER zval*60 , zhome*260 , zfile*260
      LOGICAL PostDated

! Find location of .EXE file - configuration file assumed to
! be in the same directory

      CALL PLS_GetHomeDirectory(zhome)
      print * , 'Home Directory             '//Trim(zhome)
      print * , 'Reading Authorization file '//Trim(Zhome)//'test.fig'
      print *

      CALL PLS_READFIG(Trim(Zhome)//'test.fig',icode)
      PRINT * , 'Authorization file read - return code ' , icode
      PRINT *

      CALL PLS_GETVALUE('Licensor',zval,icode)
      IF ( icode.EQ.0 ) THEN
         print * , 'Licensor      = ' , zval
      ELSE
         print * , 'Licensor not found'
      ENDIF

      CALL PLS_GETVALUE('Program Name',zval,icode)
      IF ( icode.EQ.0 ) THEN
         print * , 'Program Name  = ' , zval
      ELSE
         print * , 'Program Name not found'
      ENDIF

      CALL PLS_GETVALUE('Disk Serial',zval,icode)
      IF ( icode.EQ.0 ) THEN
         print * , 'Disk Serial   = ' , zval
      ELSE
         print * , 'Disk Serial not found'
      ENDIF

      CALL PLS_WAIT(2000)
      CALL PLS_GETVALUE('Grandfather',zval,icode)
      IF ( icode.EQ.0 ) THEN
         print * , 'Grandfather   = ' , zval
      ELSE
         print * , 'Grandfather not found'
      ENDIF

      CALL PLS_GETVALUE('expirym/d /Y',zval,icode)
      IF ( icode.EQ.0 ) THEN
         print * , 'Expiry M/D/Y  = ' , zval
      ELSE
         print * , 'Expiry M/D/Y not found'
      ENDIF

      CALL PLS_GETVALUE('expiry D/M/Y',zval,icode)
      IF ( icode.EQ.0 ) THEN
         print * , 'Expiry D/M/Y  = ' , zval
      ELSE
         print * , 'Expiry D/M/Y not found'
      ENDIF

      CALL PLS_GETVALUE('LICENSEE',zval,icode)
      IF ( icode.EQ.0 ) THEN
         print * , 'Licensee      = ' , zval
      ELSE
         print * , 'Licensee not found'
      ENDIF

      CALL PLS_GETVALUE('Authorization',zval,icode)
      IF ( icode.EQ.0 ) THEN
         print * , 'Authorization = ' , zval
      ELSE
         print * , 'Authorization not found'
      ENDIF
      print *

! check that code is valid

      CALL PLS_CHECKCODE(icode)
      IF ( icode.EQ.90 ) then
         print * , 'Authorization not found'
      ELSEIF ( icode.EQ.91 ) then
         print * , 'Authorisation Code is incorrect length'
      ELSEIF ( icode.EQ.97 ) then
         print * , 'Incorrect Disk Serial Number'
         CALL PLS_GETVALUE('DISKSERIAL',zval,icode)
         print *, ' Expected value ' , zval(1:4)//'-'//zval(5:8)
         CALL PLS_GETVOLSER(volser)
         WRITE(zval,'(Z8.8)') volser
         print *, ' Actual   value ' , zval(1:4)//'-'//zval(5:8)
      ELSEIF ( icode.EQ.98 ) then
         print * , 'Licence Expiry Date has Passed'
      ELSEIF ( icode.EQ.99 ) then
         print * , 'Incorrect Authorization Code'
      ELSE
         print * , 'Authorization Successful'
      ENDIF

! Check for PostDated files in TEMP and Home directories
      CALL PLS_GetEnv('TEMP',zhome)
      CALL PLS_CheckForPostDated(TRIM(zhome)//'\*.*',zfile,postdated)
      IF ( postdated ) THEN
         print *,'Post-dated file ('//TRIM(Zfile)//') in '//trim(zhome)
      ELSE
         print *,'No post-dated files in '//TRIM(zhome)
      ENDIF
     
      CALL PLS_GetHomeDirectory(zhome)
      CALL PLS_CheckForPostDated(TRIM(zhome)//'*.*',zfile,postdated)
      IF ( postdated ) THEN
         print *,'Post-dated file ('//TRIM(Zfile)//') in '//trim(zhome)
      ELSE
         print *,'No post-dated files in '//trim(zhome)
      ENDIF
      END

 

 for further information or to place an order

(C) Polyhedron Software Ltd. (2000-2004)