Libraries
These are the common packages used all through the shop. We use the term
"Library" since they reside in the folder with the same name,
and we want to take them apart from the shop engines.
They provide basic functionality and features to certain modules, and
will be described individually. Some of them have an object constructor,
others export some methods, others don't. Please read this page carefully
before making changes in the shop code.
AuthLib.pm
This package provides basic authentication for the users of the shop,
or for the manager interface.
The constructor method is simple:
my $AuthLib = $S->{-LIB_AUTH} = new AuthLib($S);
For this package to be built, we need first the description hash, defined
in the ShopSetup.pm under the key $S->{-AUTH_SETUP}:
my $AuthSetup = {
-SCRIPT_URL => $self->{-SHOP_SCRIPT_URL},
-PASSWORD_ENCRYPT => 'TEXT',
-GEN_PASSWORD => 0,
-AUTH_CGI => 1,
-DB_USER => $DatabaseList->{'user'},
-EMAIL_FROM => $self->{-MAIL_SITE_ADMIN},
-ADMIN_EMAIL => $self->{-MAIL_SITE_ADMIN},
-EMAIL_REGISTER => 0,
-WRITE_SESSION => 0, -SESSION_RECORD => 'auth_session',
-AUTH_DEF_GROUP => 'normal',
-DB_MAP_TO_FORM => $DatabaseMapToForm->{'user'}, -DB_MAP_TO_SESSION => $DatabaseMapToSession->{'user'}, -FORM_PASSWORD_FIELDS => [qw(auth_password auth_password1 auth_password2)], -USER_FIELDS_MAP_TO_DB => { -USER_ID => 'auth_user_name', -USER_PASSWORD => 'auth_password', -USER_NAME => 'auth_full_name', -USER_ACCESS => 'auth_user_access', -USER_EMAIL => 'auth_email', -USER_ENABLED => 'UserEnabled', }, };
Although most of the keys are self-explanatory, we will still give here
some details:
- -SCRIPT_URL - the full URL of the main script, that will be
used for building links
- -PASSWORD_ENCRYPT - the form of encryption for the password
field in the user database. As you might have guessed, we entrust this
package with inserting records in the user database.
- -GEN_PASSWORD - when set on "1", this key
determines a random password to be created, and the account info to
be emailed to the user. By default it is "0", which
means that the user can choose the password at the time the account
is added.
- -AUTH_CGI - when set on "1", we use our user
database for authentication. When "0", server-side
authentication will be presumed.
- -DB_USER - this is the reference to the description of the
user table/database.
- -EMAIL_FROM - the email that should appear in the emails sent
by this package, at the account creation time.
- -ADMIN_EMAIL - the actual email of the site admin, that will
get the emails about new accounts.
- -LIB_SESSION - the handle to the session object. It needs to
be added to the authentication hash after the creation of the session
object, and before building the AuthLib object.
- -EMAIL_REGISTER - when set on "1", will trigger
emailing the registration data to the user. If set on "0"
(default), the registrations will not be emailed. If the user is auto-logged
in, like in the case of the order form, the email with the registration
info will be sent regardless of this flag.
- -WRITE_SESSION - when set on "1", this flag
will determine the write-back to the session file immediately after
the authentication process. The default value of "0"
leaves us open if we want to commit the data to the session file and
when.
- -SESSION_RECORD - this is the name of the session record used
to hold this authentication data
- -AUTH_DEF_GROUP - this is the value taken by the "auth_user_access"
record field, for a normal user creation. It does not give the user
any rights to the manager, nor anywhere else in the shop.
- -DB_MAP_TO_FORM - is the reference to a hash of fields that
map between the record field names and the form field names. This hash
will be used to assign the record values when we create an user account,
or even to restore values of some form fields, when needed. The DatabaseMapToForm
hash is in the ShopSetup.pm.
- -DB_MAP_TO_SESSION - as the one before it, this hash maps the
fields to the session record
- -FORM_PASSWORD_FIELDS - these are the form fields used as password
fields. They will be used for the password generation/check
- -USER_FIELDS_MAP_TO_DB - this is the mapper between the authentication
module meta fields, and the user database field names.
The main method for using this package is the _login(). We need to read
first the session file, then we can call the login routine, as follows:
$SLib->_read('auth_session'); $Auth->_login($S);
That is pretty much it, considering that the handle to the session file
was passed to the object constructor. If this is rather a new login, the
calling method is a bit different:
my $SessionData = $SLib->_read('auth_session'); if (! $SessionData->[0]) {
$Auth->_login($S,
$FX->{'auth_full_name'},
$FX->{'auth_email'},
$Password);
}
The $Password is optional; if it is missing, it will be automatically
generated. The routine updates the session data record corresponding to
the authentication.
After authentication, we need to check for errors, pass them to the main
routine and eventually write back the session file:
$S->{-ERROR} = $Auth->{-ERROR} if $Auth->{-ERROR};
$SLib->_write() if ! $S->{-ERROR};
For a complete version of the above example, you can check the routine
Authenticate() in the shop.cgi.
After performing the _login(), the Auth object may contain one
or more of the following keys:
- -ERROR - the error returned by the auth routine. It is in the
format: "1234: Error Message", which allows us an easy
translation to another language, when we display the message in the
AuthVIEW.pm.
- -NEW_USER - is set on "1" if a new account
was created.
- -SPELL_PASS - if a password was generated, a spelled-out version
of it will be found here
- -FORM_ERROR - contains a reference to an array of the missing
or invalid fields in submitted the login form.
CreditCard.pm
This package provides validity checking of a credit card number. It only
makes sure it is a valid card number, but of course, it might not be an
existing number. The constructor is simple:
require "CreditCard.pm";
my $CC = new CreditCard([qw(MASTERCARD VISA)]);
Where the parameters passed are the list of allowed cards. The implemented
methods, and their parameters are:
my $Error = $CC->Validate($CardNumber, $CardExpires);
my $Error = $CC->CheckCreditCardNumber($CardNumber);
my $Error = $CC->CheckCreditCardExpirationDate($CardExpires);
All the methods return the reason this card number did not pass validation,
or an empty string if passed.
DBLib.pm
This package provides access to databases. The package was developed
for The CITY Shop, and aimed to provide a very simple interface to a flat
file database. It does have a DBI wrapper, implementing a subset of the
SQL methods, to ensure maximum portability. The package interfaces with
the following:
- DBI and the associated plugins. It uses a restricted set of
features, for the sake of portability.
- Flat files. It implements basic operation using flat files,
and offers some cache features, both for the query and the entire database.
For the DBI implementation we would like to make the following
comments:
This shop uses some tables with auto-increment fields. Not all of the
DBD modules support this features, which is why we chose not to support
them for the time being.
Supported are the following databases:
-
MySQL - certified
-
Oracle - not tested
-
Sybase - not tested
-
Informix - not tested
If you need help with one of the not certified database listed above
and you can give us telnet or at least ftp access and set up information,
we would be happy to help you, and certify the database.
Not directly supported, are:
- ADO - not enough documentation to date
- CSV - not supported
- DB2 - does not provide the last inserted
- Empress - not supported
- Ingres - not yet supported
- InterBase - requires extra coding, too complex for now
- Pg (PostgreSQL) - requires extra coding, see the comment in
the DBLib about it
- SearchServer - requires extra coding, see the comment in the
DBLib about it
- XBase - not supported
We do our tests currently on MySQL, and we can debug only this implementation.
If somebody can provide us with a test bed for another databases, we would
be happy to debug it, and certify the functionality.
This package is dependent on MainLib.pm, for a few routines exported
by this package. It could be made independent, by grafting the needed
routines from MainLib, about 15 KB worth of code.
The constructor for this package has the following format:
my $DBLib = new DBLib($S, \%Databases);
Where the %Databases hash contains the connection information
for all the databases that are supposed to be opened. This hash has the
following format:
%Databases = (
'city' => {
-DB_NAME => 'city',
-DB_TYPE => 'dbi:mysql',
-DB_PARAMS => {
PrintError => 0,
RaiseError => 0,
LongReadLen => 128 * 16384,
},
-DB_HOST => '',
-DB_PORT => '',
-USER_ID => 'cityadmin',
-USER_PASSWORD => 'cityadmin',
-DEFAULT_TABLE_TYPE => 'MyISAM',
},
);
The methods offered by this package are:
- $Response = _access($S, \%DBRequest) - All-purpose database
access
- $Response = _read($S, %DBRequest) - a wrapper for the _access()
method, where only SELECT is allowed
- _exit($S) - closes all database connections
Please do not rely on any other methods in this package, since they are
subject to change.
The $S is the common application hash, and is currently used for
passing the -DB, -DB_MAP_TO_FORM, and -SQL_QUERY
parameters in and out, but we have plans to use the database object instead,
in a future release.
The $Response variable contains the returned data, an index to
it, and eventual errors in the processing. Here is an example of how the
$Response is structured:
$Response = {
-DATA => $DataArrayRef,
-INDEX => $IndexHashRef,
-ERROR => $ErrorString,
};
When called in array context, the methods return the values in the format:
($DataArrayRef, $IndexHashRef, $ErrorString) = _access($S, \%DBRequest);
If no data is returned by the routine, an empty string will be returned
instead.
The %DBRequest hash contains all the data necessary for the database
operation. The supported actions are: SELECT, INSERT, UPDATE,
DELETE. Here are a few examples for the basic operation:
The description of the $DatabaseDescriptionHashRef and the $DBQueryArrayRef
will be made after all the examples.
A SELECT operation has the following basic format:
%DBRequest = (
-NAME => $DatabaseDescriptionHashRef,
-ACTION => 'SELECT',
-QUERY => $DBQueryArrayRef,
-SELECT => [qw(ItemID ItemDescription ItemPrice)],
-SORT => [qw(0, -2)],
-CACHE => 1,
);
The above construct will return an array of arrays, one per row, of
the three listed fields, sorted by the first and then descendent by
the third element of the array. If the -CACHE flag is set, the
%DBRequest will contain the returned data in the key -DATA,
and the index in the key -INDEX. We recommend not to access these
keys directly, since their name may change. They are used internally
by the DBLib.pm.
For the SELECT operation, almost all the keys are optional,
except for the key -NAME, wich describes the database and the
table.
An INSERT operation has the following basic format:
%DBRequest = (
-NAME => $DatabaseDescriptionHashRef,
-ACTION => 'INSERT',
-SELECT => [qw(ItemID ItemDescription ItemPrice)],
-VALUES => $ValuesArrayRef,
);
The $ValuesArrayRef is an array of all the records to be inserted.
If your table has an auto-field, you should pass the list of fields
excepting the auto-field, from the -SELECT array, otherwise the
package will return an error.
The -SELECT key is here optional, implying all the fields in
the table. As described above, in the case of tables that have an auto-increment
key, this will result in an error.
An UPDATE operation has the following basic format:
%DBRequest = (
-NAME => $DatabaseDescriptionHashRef,
-ACTION => 'UPDATE',
-QUERY => $DBQueryArrayRef,
-SELECT => [qw(ItemID ItemDescription ItemPrice)],
-VALUES => $ValuesArrayRef,
);
The $ValuesArrayRef is an array of all the records to be updated.
If you provide no query, you should pass the key field in the -SELECT
array, otherwise the package will return an error.
A DELETE operation has the following basic format:
%DBRequest = (
-NAME => $DatabaseDescriptionHashRef,
-ACTION => 'UPDATE',
-QUERY => $DBQueryArrayRef,
);
The -QUERY is in this case mandatory.
The Database Description hash ($DatabaseDescriptionHashRef)
For all the operations we have to provide the database description. This
is passed in the key -NAME, and has the following format (the example
is the user shipping table in The CITY Shop):
$DatabaseDescriptionHashRef = {
-TYPE => 'File',
-FILE_NAME => 'usershipping.db', -FILE_PATH => $Path.'Data_files', -FIELD_NAME => [qw(ItemID UserID SFirstName SLastName SCompany SAddress SCity SState SZip SCountry SPhone SPhoneExt SFax SInCityLimits)], -FIELD_KEY => 'ItemID', -FIELD_TYPE => { 'ItemID' => 'auto', }, -CREATE_IF_MISSING => 1,
-USE_CACHE => 1,
};
-TYPE key can be either 'File' or 'DBI'. If using DBI,
you need to also set up the following keys:
-DB_NAME => 'city',
-TABLE_NAME => 'usershipping',
-FILE_NAME and -FILE_PATH refer to the file containing
this table.
-FIELD_NAME contains the array of fields in the table.
-FIELD_KEY points to the key field of this table
-FIELD_TYPE is a hash reference of field types. Currently only
the 'auto' is supported, and defines that field as an auto-increment
field.
-CREATE_IF_MISSING is a flag to whether this table will be created
on the fly with the first insert. Currently, the -CREATE_IF_MISSING
is only supported by the flat file section, your SQL tables have to be
all set up in advance. We have plans to implement in in the SQL section
at a later date.
-USE_CACHE allows the DBLib.pm to keep the whole table contents
in the memory.
The database query ($DBQueryArrayRef)
This is maybe the most complex parameter in the DBLib.pm package. It
is either '*' which can be used for a SELECT, or is a reference
to an array containing the query, in one of the formats below:
$QueryArrayRef
[$QueryArrayRef, $QueryArrayRef,...]
A query is basically an array composed of three values:
$QueryArrayRef = [$FieldName, $Operator, $Value]
The $FieldName can be a string containing a table field name,
or an array reference of field names.
The $Value is either a scalar reference or an array reference
to values to check for.
The $Operator can be one of the standard comparison operators:
=~ !~ == != < > <= >= eq ne le lt ge gt. The string
operators are effective for the flat file module, but are mapped to the
numeric operators in the case of DBI. Another form to pass the operator,
is as a hash reference, having the following structure:
$Operator = {
-FUNCTION => '=~',
-LOGIC => 'OR',
-CASE_SENSITIVE => 1,
-EXACT_MATCH => 0,
},
Where the -LOGIC is how this operator applies to the values at
the right. To explain this a bit, if the logic is set to "AND",
all the values in the values array have to match in the field/fields to
the left. If the logic is set to "OR", then either value
matched in either field would return true.
If you have multiple queries, you can place a -LOGIC parameter
directly in the %DBRequest, to define how these queries interact.
This might seem a bit rigid at the first glance, but you come clear usually
with a couple queries, and then the -LOGIC parameter is good enough.
We plan on building later on a better query parser, which would allow
a more complex query building, but the DBLib was meant to be simple, so
this structure will remain as it is right now.
CryptLib.pm
This package provides a basic interface to a few major encryption methods.
It does not have a constructor (yet). The following encryption methods
are currently implemented:
| Main Method |
Submethods |
Description |
Supported Operations |
| |
|
encrypt |
decrypt |
compare |
| PGP |
PGP, PGP5 |
Supports most PGP implementations, on Win32 or Unix |
yes |
yes |
no |
| GPG |
GPG |
Supports all GPG implementations, on Win32 or Unix |
yes |
yes |
no |
| crypt |
crypt |
The basic Unix crypt |
yes |
no |
yes |
| SHA |
SHA |
The SHA1 algorithm |
yes |
no |
yes |
| |
SHA.htaccess
SHA.htaccess.apache |
The SHA to the specifications for .htaccess files, which
have a slightly different format for Apache |
yes |
no |
yes |
| MD5 |
MD5 |
The MD5 algorithm, using the Crypt::MD5 Perl module |
yes |
no |
no |
| |
MD5.htaccess
MD5.htaccess.apache |
The MD5 to the specifications for .htaccess files, which
have a slightly different format for Apache |
yes |
no |
no |
The "htaccess" submethods are used to generate passwords
in the format required by common web servers. Apache alone, has some extra
requirements, of how the password should be formatted.
If you want to implement encryption somewhere in the shop, the procedure
is to first define a key in the ShopSetup.pm:
-SHOP_CUSTOM_ENCRYPT_METHOD_1 => 'gpg',
Then you can go in your module and insert the code:
my $EncryptMethod = $S->{-SHOP_CUSTOM_ENCRYPT_METHOD_1} || '';
my $EncryptSetup = $S->{-SHOP_ENCRYPTION_SETUP} || '';
my $CryptConfig;
if ($EncryptMethod && $EncryptSetup && $EncryptSetup->{$EncryptMethod}) {
eval "use CryptLib;";
$CryptConfig = $EncryptSetup->{$EncryptMethod} if ! $@;
}
my $EncryptedValue = ! $CryptConfig ? $Value :
CryptLib::encrypt($EncryptMethod, $Value, $CryptConfig);
Then you can do what you need to do with the variable $EncryptedValue.
Of course, if you know that you don't make mistakes (remember though,
nobody's perfect), you can shorten the code a bit:
eval "use CryptLib;";
my $EncryptedValue = $@ ? $Value :
CryptLib::encrypt(
$S->{-SHOP_CUSTOM_ENCRYPT_METHOD_1},
$Value,
$S->{-SHOP_ENCRYPTION_SETUP}->{-SHOP_CUSTOM_ENCRYPT_METHOD_1});
The reason for passing data in this way, is that the CryptLib.pm
is not a shop-specific module, but rather intended for common use, meant
to be integrated in future Nightmedia applications.
The ShopSetup.pm should contain a key in the main shop has definition,
where you define the encryption methods. We will give here a couple examples:
For PGP, considering you have a Unix installation:
-SHOP_ENCRYPTION_SETUP => {
'pgp' => {
-PGP_PUBLIC_KEY_NAME => 'The CITY',
-PGP_BINARY_PATH => '/usr/bin/pgp',
-PGP_CONFIG_PATH => '/usr/home/myaccount/cgi-bin/store/pgp',
-PGP_TEMP_DIR => $LogPath,
-USE_IPCOPEN3 => 1,
},
},
For GPG, considering you have a Win32 installation:
-SHOP_ENCRYPTION_SETUP => {
'gpg' => {
-GPG_PUBLIC_KEY_NAME => 'The CITY',
-GPG_BINARY_PATH => 'c:/apps/gnupg/gpg.exe',
-GPG_CONFIG_PATH => 'w:/cgi/city/store/gpg',
-GPG_TEMP_DIR => $LogPath,
-USE_IPCOPEN3 => 0, # will not work on NT
},
},
We recommend the usage of the GPG for the Win32 environments,
since we got it to work easily. We found impossible to configure the PGP
on the Win32 platform, and place the keys in your own directory. Of course,
you can set the home directory in an environment variable, and it works
when called by hand, but its value dissapears when running the routine
from the web server.
We tried on Win32 with the version 7.0 (latest) of the PGP server. If
you try with an older version, you might get it running, but we found
it's not worth the trouble. GPG has an command line option where you can
pass the home directory directly, which makes the implementation very
simple and reliable.
Also, using the IPC::Open3 on the Win32 platform will hang the encryption
process. We think that this is caused by the defective forking in
the Activestate software. Just use the temp file method, it works.
A typical example of implementation is in emailing the orders to the
shop admin, where the payment data is encrypted using PGP or GPG. For
this case, the key -MAIL_ENCRYPT_METHOD is set in the ShopSetup.pm,
on 'gpg', or 'pgp' as needed.
|