Products | Scripts | Services | Tutorials | Books | Links | Contact | Bulletin Board

The CITY Shop

Overview | Concept | Licensing | Demo/Download | Installation | Gallery | Last Updates | FAQ

Templates | Views | Engines | Libraries | Main Setup | Shop Setup | Database | Session | Shipping

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.

TOP | AuthLib | CreditCard | DBLib | CryptLib | FormLib | MainLib | SendMail | SessionLib | ShippingUPS

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.

 

 

 

TOP | AuthLib | CreditCard | DBLib | CryptLib | FormLib | MainLib | SendMail | SessionLib | ShippingUPS

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.


 

 

 

TOP | AuthLib | CreditCard | DBLib | CryptLib | FormLib | MainLib | SendMail | SessionLib | ShippingUPS

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.

 

 

 

 

TOP | AuthLib | CreditCard | DBLib | CryptLib | FormLib | MainLib | SendMail | SessionLib | ShippingUPS

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.