By Barry Rochford
| Barry is the president of the NY area Alpha Users Group. Here he presents a method for controlling the distribution of his successful application. |
One of my Clients requested I design a Security Sub-system for their Dietary Application. Since last September, I have been adding in more and more Sub-systems and making changes as they occur. The existing Application is running in five different locations, three in Maryland, one in North New Jersey and I believe one is in Virginia. All five are Company managed Dietary Operations in various Extended Care Facilities.
Now they have contracted to install the same Application in 10 more locations. They wanted a way of controlling the annual Licensing. Fortunately, whenever I begin an Application I always create a Start table. This table usually (though not always) contains 1 dummy record. In this case though I use it for the Client Name, address, phone number etc. Luckily, when I designed the Start Table I added some un-used fields, 2 character fields (C 20 each), 3 date fields, 2 numeric fields (N 12 2 each) and 2 logical fields. With just one record, you can add as many unused fields as you wish and not really waste disk space. All of my menus and most date_dialog forms are always attached to this Start table it makes them pretty much portable when I need them for a new Application.
First I had to come up with a License Number. Much earlier I had added a Client Number with a check digit so that part was already done. I needed to be able to generate a License Number that would be unique for each Installation, yet be secure enough that the average 14 year old would not be able to crack it. Incidentally, I chose the Mod 10 double-add-double method. Its not the best, but its the same as all Credit Card Issuers use.
This is my License Number structure. It is 8 digits (I hate keying in a big long License Number).
Example: Client Number=00141 (1 is the check digit)
License Number:07132079
07=the hash total of the client number + # of renewals (including installation)
1=the Check Digit of Client Number
3207=random number between 1 and 9999
9=Check Digit of the License Number
This gives me a unique license number which can be entered into the Application and, after being validated, increments their expiration date by 1 year. So far everything looks good, right? Well, yes but, remembering that whatever can happen probably will happen, I designed a trapdoor for Emergency use only. In an emergency, my Client can, using pencil & paper, generate a five digit number that will extend the License. Again this number will be unique for each Installation and only valid for 1 day.
Write down 3099 then write the last digit of the current year (9)
You now have 30999, underneath, write the current month and day (March 5 0305), underneath that, write the client number (00141). Now add up the numbers. That is the Trapdoor License Number.
Example: 30999 3099 + 9 for 1999
0305 March 5
00141 Client No.
31445=Trapdoor
NOTE: This is shown in the Scripts, but it is not exactly the same as provided to my Client.
OK, now I know what Im going to do, lets start. First I knew I needed another Database for use only by the Client. I added a new Folder to my Alpha_Data folder.( C:\Alpha_data\SAQSecurity ) The DataBase is named SECURE. This is a very small System, only 2 tables (plus my Start table).
| CLIENT | LICENSE | ||
| Raw_client_no | C,5 | Ckd_client_no | C,6 |
| Ckd_client_no | C,6 | License_no | C,8 |
| Name | C,35 | Date | D,8 |
| Address_1 | C,35 | ||
| Address_2 | C,35 | ||
| City_State_Zip | C,40 | ||
| Installation_Date | D,8 | ||
| Last_renewal_Date | D,8 | ||
| Hash_total | N,5,0 | ||
| License | C,8 | ||
| Phone | C,20 | ||
| Expiration | D,8 | ||
| Renewals | N,3,0 |
NOTE: Ckd_client_no is the Client Number + the check digit
I added a Set Named CLIENT_1
CLIENT
|
|=======License (1 to many, linked by ckd_client_no)
Next I designed a Form to be used to Enter/edit a client and generate a new License number. This form is attached to the Client_1 Set. The Browse will display all of the license numbers and dates.
Fig 1. Client Data Form
This form shows a new Client, just installed (the installation date is
backdated in this example). Look at the New Record button, it has a
script attached to the OnPush Event. This script changes the Text of the
Extend for 1 Year button.
Fig 2 New_client_button OnPush Event Script.
Now the Mechanics are there, but the whole Engine is contained in the OnPush Event Script for the ''Extend for 1 Year Button. Line numbers are only for this article and are not included in the actual script.
XBasic
dim x as c
dim ht as n
dim number as n
dim lic_no as c
dim t as p
dim mode as c
1 if parent.mode_get()="ENTER" then
2 x=client->ckd_client_no
3 ht=val(substr(x,1,1))+val(substr(x,2,1))\
+ val(substr(x,3,1))+val(substr(x,4,1))+\
val(substr(x,5,1))
4 hash_total.text=padl(ltrim(str(ht,2,0)),2,"0")
5 hash_total.refresh()
6 button3.text="Extend for 1 Year"
7 button3.refresh()
8 else
9 ht=client->hash_total
10 end if
11 ht=ht+(client->renewals+1)
12 number=rand()*9999
13 lic_no=padl(ltrim(str(ht,2,0)),2,"0")
14 lic_no=lic_no+substr(client->ckd_client_no,5,1)+\
padl(ltrim(str(number,4,0)),4,"0")
15 lic_no=lic_no+alltrim(chkdigit(lic_no))
16 if .not.ccvalid(lic_no) then
17 ui_msg_box("Internal Error","Check digit not Valid!")
18 end
19 end if
20 renewals.text=str(client->renewals+1)
21 renewals.refresh()
22 license.text=lic_no
23 license.refresh()
24 if expiration.text="" then
25 expiration.text=dtoc(addyears(ctod(installation_date.text),1))
26 else
27 expiration.text=dtoc(addyears(ctod(expiration.text),1))
28 end if
29 last_renewal_date.text=dtoc(date())
30 t=table.current(2)
31 if parent.mode_get()<>"VIEW" then
32 parent.commit()
33 end if
34 t.enter_begin()
35 t.license_no=lic_no
36 t.date=date()
37 t.enter_end(.t.)
38 browse1.refresh()
end
Lines
1 10 If this is a new client, calculate the Hash Total, change Button Text back to Extend for 1 Year
11 12 Add renewals to Hash Total & get a random number.
13 - 15 Put together the license no. (this could all be in one instruction, but makes it more difficult to understand).
16 19 Validate the new checkdigit.
20 23 Refresh screen fields.
24 28 If no expiration date, this is a new installation, use installation date to get expiration, else use the previous expiration date + 1 year.
30 37 Build the child record.
38 Refresh the Browse.
NOTE: I usually try to write up pseudo code before writing the script, this is a fairly simple script so it is not included.
I designed 2 very simple reports. A listing by Client (using the Set) and a listing of Clients by Expiration date. They are simple, straightforward and are not included in this Article.
Now to tie this Sub-system together, I added a Menu Form (attached to my START table). I usually use the same style of Menu, all Hot Spots and Conditional Objects. See Fig 3.
Fig. 3 SAQ Security Sub-system Menu.
Just a quick explanation. The Menu form is divided into 2 parts, the left raised panel has 3 Hot Sports, clicking on client data calls in the Client data form. Clicking on Reports changes the variable for the conditional object so that the right hand raised panel switches to the report menu. Which contains 2 Hot spots, 1 for each report. Clicking on Exit To Windows brings up a confirmation box asking if you really want to exit. If you wonder how I found the bitmaps, there are over 500 available for you to use. I have a printed Report courtesy of Steve Workings displaying the entire Visual Bitmap Library. The program to print this listing is still available on Steves Webpage. I believe it is www.workings.com, and Im sure it is still available for download and well worth the small amount of time.
Well, were all done right? Wrong, not yet. Weve got the whole Security System tied together, but how do we use it? Heres how. Fig 4 shows the Primary Application Main Menu (Gee, somehow it looks familiar.)
Fig. 4 Dietary Mgmt. Main Menu
Every time the Application is started, a script is executed. In the Script I test the current date vs. (expiration_date 30). I knew what I had to do but, I had a problem executing the script. I tried OnInit, OnActivate etc. None worked properly. I decided that I would let something on the form trigger the script. Well, with all Hotspots, there was no way. Well, I thought, theres more than one way to skin a cat. I added a Hidden button to the form and attached my Script to its OnArrive Event.
| Editor's note: In these scripts we have used the character "\" as a line continuation character for the Xbasic scripts. This is perfectly legal Xbasic and helps make long lines more readable. When Alpha Five encounters a line that ends in "\" it ignores the end of the line and continues with the next line. You can use the line continuation character in Alpha Five v. 4 but not in v. 3. |
''XBasic
dim p as p
p=obj("entry_form")
if cdate(date())>cdate(start->start_date3-30) .or. \
isblank("start->start_field_1") then
if .not. is_object(p) then
:form.view("entry_form")
end
end if
end if
end
All Im doing is checking for an expiration within 30 days or is the License No blank? If yes, bring up the emtry_form. This form is shown in Fig. 5
NOTE: Remember I had extra fields in my Start table? Start_date3 is actually
expiration date, Start_field1 is actually License_no. When you have an
Application running in different locations you dont really want to be
renaming fields or adding fields unless absolutely necessary. In addition they
are using the Run-time version which makes re-structuring any tables very
difficult.
Fig. 5 Entry_Form (in Design Mode)
Several things about the form. See the 2 WARNINGS? By use of the Form timer(set to .75) they flash, first the top then the bottom. No need for two little trenchcoats, just a little script for the form OnTimer Event.
Xbasic if text2.object.visible then 'test, if text2 is ' visible, hide it and show text3, ' if text2 not visible,?then show text2 and hide text3. ' This causes flashing. text2.hide() text3.show() else text2.show() text3.hide() end if end
Pressing the Enter New License Button changes the variable attached to the Conditional Object which contains the New License No. box, (the License Updated button remains hidden).
The Lic_no field has a script attached to the CanDepart event. It is shown below.
''XBasic CanDepart event for License No.
dim t as p
dim shared lic_no as c
dim x as c
dim calc as n
1 t=table.current()
2 if len(lic_no.text)=5 then
3 calc=30990+val(right(dtoc(date()),1))+\
val(t.start_cl_fax)+val(substr(dtos(date()),5,4))
4 if val(lic_no.text)<>calc then
5 cancel()
6 ui_msg_box("This 5 Digit License is Invalid",\
"You entered "+trim(lic_no.text))
7 end
8 else
9 GOTO update
10 end if
11 end
12 end if
13 if .not. ccvalid(lic_no.text) then
14 cancel()
15 ui_msg_box("Error V - License Number","INVALID - Try Again")
16 end
17 end if
18 x=t.start_cl_fax
19 ht=val(substr(x,1,1))+val(substr(x,2,1))+\
val(substr(x,3,1))+val(substr(x,4,1))+val(substr(x,5,1))
20 ht=ht+t.start_numeric2+1
21 if ht <> val(substr(lic_no.text,1,2))
22 cancel()
23 ui_msg_box("Error H"\
,"This License Number is not for Client "+trim(t.start_cl_fax))
24 end
25end if
26 if substr(lic_no.text,3,1)<>substr(t.start_cl_fax,5,1) then
27 cancel()
28 ui_msg_box("Error C","This License Number is not for\
Client "+trim(t.start_cl_fax))
29 end
30 end if
' *********** update fields *********
31 update:
32 t.change_begin()
33 t.start_field_1=lic_no.text
34 t.start_date3=addyears(t.start_date3,1)
35 t.start_numeric2=t.start_numeric2+1
36 t.change_end()
37 button3.show()<
38 end
Line #
1 - 12 Checks, if 5 digits entered, see if it is a valid number, if true, goto update, if not, error msg.
13 17 validate license number check digit
18 20 Calculate hash total of client number
21 25 compare hash totals (which include no. of renewals)
26 30 Check if client checkdigit same as license client check digit
31 37 Save typed in license number, add 1 year to expiration , increment renewal count, show the button License updated, press to close
Incidentally my Client did not want to shut down the Application on Expiry, though it could easily have been added in.
Im sure there are many more sophisticated ways of calculating a License number but, this works fine for my Client and they are happy with it.
Barry Rochford is an Alpha Developer living in Milford Square, Pa, though soon moving South to Newtown, PA. He can be reached at brochford@enter.net
last revised 3/21/99 - pkw
Don't forget, we need your feedback to make this site better!