#!/usr/bin/perl
##
## core.cgi 3.3
$versions{'core.cgi'} = "3.3e" . $ENV{"coreWRAP"};
$versions{'perl'} = "$]";
$versions{'OSNAME'} = "$^O";
$versions{'server'} = $ENV{'SERVER_SOFTWARE'} if $ENV{'SERVER_SOFTWARE'};
##
## This version is heavely modified by Lennie Core
## http://www.coreave.com
#### THIS IS NOT FREE SOFTWARE. YOU ARE LICENSED TO USE THIS
## CART SYSTEM ONLY IF IT WAS INSTALLED AND UPDATED BY
## LENNIE CORE. IT MAY BE PURCHASED FOR INDIVIDUAL WEB
## SITE USE. Contact: operations@coreave.com for information.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
##
## There are several add-on modest-cost modules planned, see the
## http://www.coreave.com web site for more details. Licensing
## for these add-on modules will be different than for this program.
##
$| = 1;
$ENV{"PATH"} = "/bin:/usr/bin";
$test=`whoami`;
$versions{'whoami'} = $test if $test;
$versions{'id'} = `id`;
if ((-f "./wrap_agc.o") && (!($ARGV[0] =~ /nowrap/i))) { # use wrapper
$ENV{"coreWRAP"}="*";
print `./wrap_agc.o`;
&call_exit;
}
$time = time;
$main_program_running = "yes";
&require_supporting_libraries (__FILE__, __LINE__,
"./admin_core/core_user_lib.pl",
"./dll_lib/cgi-lib.pl");
# read and parse here, if just serving images no need to load
# up all the other libraries, makes it much faster
&read_and_parse_form_data;
&require_supporting_libraries (__FILE__, __LINE__,
"./dll_lib/core.setup.db");
&codehook("after_loading_setup_db");
&require_supporting_libraries (__FILE__, __LINE__,
"./admin_core/$sc_gateway_name-user_lib.pl",
"./dll_lib/core_order_lib.pl",
"$sc_html_setup_file_path",
"$sc_mail_lib_path",
"$sc_cookie_lib");
$sc_loading_primary_gateway = "yes";
&require_supporting_libraries(__FILE__,__LINE__,"$sc_process_order_lib_path");
$sc_loading_primary_gateway = "no";
&codehook("before_loading_custom_libs");
opendir (USER_LIBS, "./custom") || &codehook("cannot-open-custom-dir");
@mylibs = readdir(USER_LIBS);
closedir (USER_LIBS);
foreach $zlib (@mylibs) {
$lib = $zlib;
$lib =~ /([\w\-\=\+]+)(\.pl)/i;
$zfile = "$1$2";
$zlib =~ /([^\n|;]+)/;
$lib = $1;
if ((-f "./custom/$lib") && ($lib eq $zfile)) {
&require_supporting_libraries(__FILE__, __LINE__,"./custom/$lib");
}
}
&codehook("after_loading_custom_libs");
&get_cookie;
&alias_and_override;
&error_check_form_data;
$cart_id = $form_data{'cart_id'};
if ($cookie{'cart_id'} eq "" && $form_data{'cart_id'} eq "") {
&delete_old_carts;
&assign_a_unique_shopping_cart_id;
$cart_id_history .= "set new cart value "; #for debugging of course
&codehook("got_a_new_cart");
} else {
if ($form_data{'cart_id'} eq "") {
$cart_id = $cookie{'cart_id'};
$cart_id_history .= "from cookie "; #for debugging of course
&set_sc_cart_path;
} else {
$cart_id = $form_data{'cart_id'};
$cart_id_history .= "set from form data "; #for debugging of course
&set_sc_cart_path;
}
}
&codehook("pre_header_navigation");
print $sc_browser_header;
$sc_header_printed = 1;
#print "cart_id: $cart_id $cart_id_for_html $cart_id_history \n";# debugging
$are_any_query_fields_filled_in = "no";
&codehook("open_for_business");
foreach $query_field (@sc_db_query_criteria)
{
@criteria = split(/\|/, $query_field);
if ($form_data{$criteria[0]} ne "")
{
$are_any_query_fields_filled_in = "yes";
}
}
if (($search_request ne "") && ($are_any_query_fields_filled_in eq "no")) {
$page = "searchpage.html"; #load up the advanced search form page
$search_request = ""; # not really a search, nothing filled in!
if (!(-f "$sc_html_product_directory_path/$page")){
#no such page, just show everything
$page = "";
$form_data{'product'} = "."; # show everything
$are_any_query_fields_filled_in = "yes";
} else {
$form_data{'page'} = $page; # set it back, in case somebody uses it
}
}
&codehook("special_navigation");
if ($form_data{'display_cart'} ne "")
{
&display_cart_contents;
&call_exit;
}
if (($form_data{'add_to_cart_button.x'} ne "") ||
($form_data{'add_to_cart_button'} ne ""))
{
&add_to_the_cart;
&call_exit;
}
elsif (($form_data{'modify_cart_button.x'} ne "") ||
($form_data{'modify_cart_button'} ne ""))
{
&display_cart_contents;
&call_exit;
}
elsif (($form_data{'change_quantity_button.x'} ne "") ||
($form_data{'change_quantity_button'} ne ""))
{
&output_modify_quantity_form;
&call_exit;
}
elsif (($form_data{'submit_change_quantity_button.x'} ne "") ||
($form_data{'submit_change_quantity_button'} ne ""))
{
&modify_quantity_of_items_in_cart;
&call_exit;
}
elsif (($form_data{'delete_item_button.x'} ne "") ||
($form_data{'delete_item_button'} ne ""))
{
&output_delete_item_form;
&call_exit;
}
elsif (($form_data{'submit_deletion_button.x'} ne "") ||
($form_data{'submit_deletion_button'} ne ""))
{
&delete_from_cart;
&call_exit;
}
elsif (($form_data{'order_form_button.x'} ne "") ||
($form_data{'order_form_button'} ne ""))
{
&require_supporting_libraries (__FILE__, __LINE__, "$sc_order_lib_path");
&display_order_form;
&call_exit;
}
elsif ($form_data{'mail_order_form_button'} ne "")
{
&require_supporting_libraries (__FILE__, __LINE__, "$sc_order_lib_path");
&process_Mail_Order;
&call_exit;
}
elsif (($form_data{'clear_order_form_button'} ne "") ||
($form_data{'clear_order_form_button.x'} ne ""))
{
&require_supporting_libraries (__FILE__, __LINE__, "$sc_order_lib_path");
&clear_verify_file;
&codehook("display_cleared_order_form");
&display_order_form;
&call_exit;
}
elsif (($form_data{'submit_order_form_button'} ne "") ||
($form_data{'submit_order_form_button.x'} ne ""))
{
&require_supporting_libraries (__FILE__, __LINE__, "$sc_order_lib_path");
if ($sc_test_repeat) {
&display_order_form;
} else {
&process_order_form;
}
&call_exit;
}
elsif (($page ne "" || $search_request ne ""
|| $form_data{'continue_shopping_button'}
|| $form_data{'continue_shopping_button.x'}
|| $are_any_query_fields_filled_in =~ /yes/i) &&
($form_data{'return_to_frontpage_button'} eq ""))
{
&display_products_for_sale;
&call_exit;
}
$sc_processing_order="yes"; # assume unless we fall through
&codehook("gateway_response");
#AuthorizeNet
if ($form_data{'x_response_code'})
{
$cart_id = $form_data{'x_cust_id'};
&set_sc_cart_path;
&codehook("AuthorizeNet_order");
&process_AuthorizeNet_Order;
&call_exit;
}
#iTransact
elsif ($form_data{'authcode'})
{
$cart_id = $form_data{'p4'};
&set_sc_cart_path;
&codehook("iTransact_order");
&process_iTransact_Order;
&call_exit;
}
# Linkpoint
elsif ($form_data{'approval_code'})
{
$cart_id = $form_data{'custnum'};
&set_sc_cart_path;
&codehook("Linkpoint_order");
&process_Linkpoint_Order;
&call_exit;
}
#Offline
elsif ($form_data{'process_order'})
{
$cart_id = $form_data{'cart_id'};
&set_sc_cart_path;
&codehook("Offline_order");
&processOrder;
&call_exit;
}
else
{
$sc_processing_order="no"; # fell through!
&output_frontpage;
&call_exit;
}
#######################################################################
# Require Supporting Libraries. #
#######################################################################
# require_supporting_libraries is used to read in some of
# the supporting files that this script will take
# advantage of.
#
# require_supporting_libraries takes a list of arguments
# beginning with the current filename, the current line
# number and continuing with the list of files which must
# be required using the following syntax:
#
# &require_supporting_libraries (__FILE__, __LINE__,
# "file1", "file2",
# "file3"...);
#
# Note: __FILE__ and __LINE__ are special Perl variables
# which contain the current filename and line number
# respectively. We'll continually use these two variables
# throughout the rest of this script in order to generate
# useful error messages.
sub require_supporting_libraries
{
# The libraries are required by us,so exit if loading error
local ($file, $line, @require_files) = @_;
local ($require_file);
&request_supporting_libraries("warn exit",$file, $line, @require_files);
}
sub request_supporting_libraries
{
# The incoming file and line arguments are split into
# the local variables $file and $line while the file list
# is assigned to the local list array @require_files.
#
# $require_file which will just be a temporary holder
# variable for our foreach processing is also defined as a
# local variable.
local ($what_to_do_on_error, $file, $line, @require_files) = @_;
local ($require_file);
# Next, the script checks to see if every file in the
# @require_files list array exists (-e) and is readable by
# it (-r). If so, the script goes ahead and requires it.
foreach $require_file (@require_files)
{
if (-e "$require_file" && -r "$require_file")
{ # file is there, now try to require it
$result = eval('require "$require_file"'); # require it in a not-fatal way
if ($@ ne "") {
if($what_to_do_on_error =~ /warn/i) {
if ($error_header_done ne "yes") {
$error_header_done = "yes";
print "Content-type: text/html\n\n";
}
print "
\n";
print "Error loading library $require_file:
\n $@\n";
print "
Please contact the site administrator to ",
"fix the error. \($ENV{'SERVER_ADMIN'}\) \n";
print "
\n";
}
if($what_to_do_on_error =~ /exit/i) {
&call_exit;
}
}
}
# If not, the scripts sends back an error message that
# will help the admin isolate the problem with the script.
else
{
if($what_to_do_on_error =~ /warn/i) {
if ($error_header_done ne "yes") {
$error_header_done = "yes";
print "Content-type: text/html\n\n";
}
print "I am sorry but I was unable to require $require_file at line
$line in $file. \nWould you please make sure that you have the
path correct and that the permissions are set so that I have
read access? Thank you. \($ENV{'SERVER_ADMIN'}\) \n";
}
if($what_to_do_on_error =~ /exit/i) {
&call_exit;
}
}
} # End of foreach $require_file (@require_files)
} # End of sub require_supporting_libraries
#######################################################################
# Read and Parse Form Data. #
#######################################################################
# read_and_parse_form_data is a short subroutine
# responsible for calling the ReadParse subroutine in
# cgi-lib.pl to parse the incoming form data. The script
# also tells cgi-lib to prepare that information in the
# associative array named %form_data which we will be able
# to use for the rest of this script.
#
# read_and_parse_form_data takes no arguments and is
# called with the following syntax:
#
# &read_and_parse_form_data;
sub read_and_parse_form_data
{
local ($junk);
&ReadParse(*form_data);
# DELUXE feature ... check here if we are just serving images
if ($form_data{'picserve'} ne "") {
&serve_picture($form_data{'picserve'},$sc_path_of_images_directory);
&call_exit;
}
if ($form_data{'secpicserve'} ne "") {
&serve_picture($form_data{'secpicserve'},"./protected/images/");
&call_exit;
}
}
#########################################################################
#
# Writen by Steve K to serve images 04-FEB-2000
# HTML usage examples:
#
#
#
# Note: using the http:// format is less efficient
# converted to taint-mode sub 2/5/2000
sub serve_picture
{
local ($qstr,$sc_path_of_images_directory) = @_;
local ($test, $test2, $my_path_to_image);
$qstr =~ /([\w\-\=\+\/\.\:]+)/;
$qstr = "$1";
$my_path_to_image = $sc_path_of_images_directory . $qstr ;
$test = substr($my_path_to_image,0,6);
$test2 = substr($my_path_to_image,(length($my_path_to_image)-3),3);
if ($test2=~ /jpg/i || $test2 =~ /gif/i || $test2 =~ /png/i) {
# file is ok to display
if ($test2=~ /jpg/i) {# .jpg is jpeg file
$test2 = "jpeg";
}
if ($test=~ /http:\//i || $test =~ /https:/i) {
# need to GET the info .. no implemented here in core
# use LWP::Simple;
# print "Content-type: image/$test2\n\n";
# print get($my_path_to_image);
} else { # is a filename we can load up directly
print "Content-type: image/$test2\n\n";
if (!(-f $my_path_to_image)) {# try adding another slash!
$my_path_to_image = $sc_path_of_images_directory ."/" . $qstr ;
}
open (MYPIC,$my_path_to_image);
$size = 250000;
while ($size > 0) {
$size = read(MYPIC,$the_picture,$size);
print "$the_picture";
}
close(MYPIC);
}
}
}
#######################################################################
# Alias and Override
#
# This routine allows the use of aliases for switches, such as
# using xm= instead of exact_match=
#
# Also, override certain setup variables under certain conditions
#
#######################################################################
sub alias_and_override {
local ($junk,$raw_text)="";
local (@mylibs,$lib);
local ($testval,$testval2,$found_response);
&codehook("alias_and_override_top");
if (defined($form_data{'versions'})) {
print "Content-type: text/html\n\n";
print "\nVERSIONS\n\n";
print "
Info and Versions of loaded libraries: \n";
print "
\n";
# }
print "\n\n";
&call_exit;
}
# Get rid of extraneous stuff, if present, on the cart id
# need to test for a repeated loading of critical pages ...
if (defined($form_data{'cart_id'})) {
($form_data{'cart_id'},$junk) = split(/\*/,$form_data{'cart_id'},2);
$sc_unique_cart_modifier = $junk;
}
# Check for proper URL in use, helps with cookies but not required
$found_response = "";
foreach $testval (keys %sc_order_response_vars) {
$testval2 = $sc_order_response_vars{$testval};
if ($form_data{$testval2} ne "") {
$found_response .= "*";
}
}
if (("$sc_domain_name_for_cookie" ne $ENV{'HTTP_HOST'}) &&
($sc_allow_location_redirect =~ /yes/i ) &&
($form_data{'process_order.x'} eq "" ) &&
($form_data{'process_order'} eq "" ) &&
($found_response eq "" ) &&
($form_data{'submit_order_form_button.x'} eq "" ) &&
($form_data{'submit_order_form_button'} eq "" ) &&
($form_data{'order_form_button.x'} eq "" ) &&
($form_data{'order_form_button'} eq "" )){ #redrect them to standard URL
if ($cookie{'cart_id'} ne "") {
$cart_id = $cookie{'cart_id'};
}
if ($form_data{'cart_id'} ne "") {
$cart_id = $form_data{'cart_id'};
}
$sc_cart_path = "$sc_user_carts_directory_path/${cart_id}_cart";
if (!(-f $sc_cart_path)){ #no cart, forget the number
$cart_id = "";
}
$href = "$sc_store_url";
if ($cart_id ne "") {
$href .= "?cart_id=$cart_id";
}
print "Location: $href\n\n";
&call_exit;
}
$search_request = ($form_data{'search_request_button'} ||
$form_data{'search_request_button.x'});
if (($form_data{'maxp'} > 0) && ($form_data{'maxp'} < 301)) {
$sc_db_max_rows_returned = $form_data{'maxp'};
}
if (defined($form_data{'srb'})) { #is an override/shortcut
$search_request = $form_data{'srb'};
}
if (defined($form_data{'xc'})) {
$form_data{'exact_case'} = $form_data{'xc'};
}
if (defined($form_data{'xm'})) {
$form_data{'exact_match'} = $form_data{'xm'};
}
if (defined($form_data{'dc'})) {
$form_data{'display_cart'} = $form_data{'dc'};
}
if (defined($form_data{'pid'})) {
$form_data{'p_id'} = $form_data{'pid'};
}
if (defined($form_data{'p'})) {
if ($form_data{'product'} ne "") {
$form_data{'product'} .= " " . $form_data{'p'};
} else {
$form_data{'product'} = $form_data{'p'};
}
}
if (defined($form_data{'ppovr'})) {
$form_data{'ppinc'} = $form_data{'ppovr'};
}
if (defined($form_data{'k'})) {
if ($form_data{'keywords'} ne "") {
$form_data{'keywords'} .= " " . $form_data{'k'};
} else {
$form_data{'keywords'} = $form_data{'k'};
}
}
if (defined($form_data{'kovr'})) {
$form_data{'keywords'} = $form_data{'kovr'};
}
if (($form_data{'add_to_cart_button'} eq "") &&
($form_data{'add_to_cart_button.x'} ne "")) {
$form_data{'add_to_cart_button'} = "1";
}
if ($form_data{'viewOrder'} eq "yes") {
$sc_should_i_display_cart_after_purchase = "yes";
} else {
$sc_should_i_display_cart_after_purchase = "no";
}
&codehook("alias_and_override_end");
}
#######################################################################
# Error Check Form Data. #
#######################################################################
# error_check_form_data is responsible for checking to
# make sure that only authorized pages are viewable using
# this application. It takes no arguments and is called
# with the following syntax:
#
# &error_check_form_data;
#
# The routine simply checks to make sure that if
# the page variable extension is not one that is defined
# in the setup file as an appropriate extension like .html
# or .htm, or there is no page being requestd (ie: the
# store front is being displayed) it will send a warning
# to the user, append the error log, and exit.
#
# @acceptable_file_extensions_to_display is an array of
# acceptable file extensions defined in the setup file.
# To be more or less restrictive, just modify this list.
#
# Specifically, for each extension defined in the setup
# file, if the value of the page variable coming in from
# the form ($page) is like the extension (/$file_extension/)
# or there is no value for page (eq ""), we will set
# $valid_extension equal to yes.
sub error_check_form_data
{
# These expressions will strip of any path information so
# files are only loaded from the appropriate directory.
# We will also only load pages of the proper extension,
# which is checked in sub error_check_form_data.
$page = $form_data{'page'};
$page =~ /([\w\-\=\+\/]+)\.(\w+)/;
$page = "$1.$2";
$page_extension = ".$2";
$page = "" if ($page eq ".");
$page =~ s/^\/+//; # Get rid of any residual / prefix
$form_data{'page'} = $page; # set it back, in case somebody uses it
foreach $file_extension (@acceptable_file_extensions_to_display)
{
if ($page_extension eq $file_extension || $page eq "")
{
$valid_extension = "yes";
}
}
# Next, the script checks to see if $valid_extension has
# been set to "yes".
#
# If the value for page satisfied any of the extensions
# in @acceptable_file_extensions_to_display, the script
# will set $valid_extension equal to yes. If the value
# is set to yes, the subroutine will go on with it's work.
# Otherwise it will exit with a warning and write to the
# eror log if appropriate
#
# Notice that we pass three parameters to the
# update_error_log subroutine which will be discussed
# later. The subroutine gets a warning, the
# name of the file, and the line number of the error.
#
# $sc_page_load_security_warning is a variable set in
# core.setup.db If you want to give a more or less
# informative error message, you are welcome to change the
# text there.
if ($valid_extension ne "yes") {
print "Content-type: text/html\n\n$sc_page_load_security_warning\n";
&update_error_log("PAGE LOAD WARNING", __FILE__, __LINE__);
&call_exit;
}
$form_data{'page'} = $page; # set it to the untainted & filtered one
#
# error check this, paranoia I know ... just in case regular expr. get
# broken somehow, this is our safety net
if ($form_data{'page'} =~ /\.\.\//) {
print "Content-type: text/html\n\nNo, you cannot go navigating";
print " outside the html directory for pages, that is a security ";
print " risk. Sorry!\n ";
&call_exit;
}
# now un-taint the value of $form_data{'cart_id'}
# also pattern match it, in case the form has 2+ cart_id fields
if ($form_data{'cart_id'} ne "") {
if ($form_data{'cart_id'} =~ /([\w\-\=\+\/]+)\.(\w+)/) {
$form_data{'cart_id'} = "$1.$2";
if ($form_data{'cart_id'} eq ".") {
$form_data{'cart_id'} = "";
}
} else {
$form_data{'cart_id'} = "";
}
}
if ($cookie{'cart_id'} ne "") {
if ($cookie{'cart_id'} =~ /([\w\-\=\+\/]+)\.(\w+)/) {
$cookie{'cart_id'} = "$1.$2";
if ($cookie{'cart_id'} eq ".") {
$cookie{'cart_id'} = "";
}
} else {
$cookie{'cart_id'} = "";
}
}
}
#######################################################################
sub parse_options_to_verify{
local($orig_str) = @_;
local($str) = $orig_str;
local($name) = "";
local($nextname,$stuff,$parta,$partb,$val,@items);
# first group options by their 'name = ' part
while ($str =~ /(name)([ \n\r]*)(=)([ \n\r]*)([\"\'])([^\"\']*)([\"\'])/i) {
$nextname = $6;
$str =~ s/(name)([ \n\r]*)(=)([ \n\r]*)([\"\'])([^\"\']*)([\"\'])/%##%/i;
($stuff,$str) = split(/%##%/,$str,2);
if ($name ne "") {
$items{$name} = $stuff;
}
$name = $nextname;
}
# get the last part, if name was found
if ($name ne "") {
$items{$name} = $str;
}
# for each named option, parse the value associated with it
foreach $name (keys %items) {
$str = $items{$name};
while ($str =~ /(value)([ \n\r]*)(=)([ \n\r]*)([\"\'])([^\"\']*)([\"\'])/i) {
$val = $6;
($parta,$partb) = split(/\|/,$val,2);
$item_opt_verify{$name . "|" . $parta} = $partb;
$str =~ s/(value)([ \n\r]*)(=)([ \n\r]*)([\"\'])([^\"\']*)([\"\'])//i;
}
}
return $orig_str;
}
#######################################################################
sub option_prep {
local ($field,$option_location,$product_id)= @_;
local ($very_first_part,$junk);
$field = &corescript($field,"optpre","$option_location",__FILE__,__LINE__);
$field =~ s/%%PRODUCT_ID%%/$product_id/g;
$field =~ s/%%PRODUCTID%%/$product_id/g;
$field = &corescript($field,"optpost","$option_location",__FILE__,__LINE__);
# DELUXE feature ... take only the part between
--cut here--<\/h3>/i,$field,3);
if ($field eq "") {
$field = $very_first_part;
}
if ($field eq "") {
$field = "(file $option_location not found)";
}
# }
return $field;
}
#######################################################################
# Delete Old Carts. #
#######################################################################
# delete_old_carts is a subroutine which is used to prune
# the carts directory, cleaning out all the old carts
# after some time interval defined in the setup file. It
# takes no argumnetes and is called with the following
# syntax:
#
# &delete_old_carts;
sub check_cart_expiry {
&check_cart_type_file_expiry("$sc_cart_path");
&check_cart_type_file_expiry("$sc_verify_order_path");
}
sub check_cart_type_file_expiry {
local($cart_type_file_path) = @_;
if (-M "$cart_type_file_path" > $sc_number_days_keep_old_carts)
{
if ($cart_type_file_path =~ /cart/i) {
&codehook("delete-cart");
} else {
&codehook("delete-non-cart");
}
unlink("$cart_type_file_path");
}
sub delete_old_carts
{
# The subroutine begins by grabbing a listing of all of
# the client created shoppping carts in the User_carts
# directory.
#
# It then opens the directory and reads the contents using
# grep to grab every file with the extension _cart. Then
# it closes the directory.
#
# If the script has any trouble opening the directory,
# it will output an error message using the
# file_open_error subroutine discussed later. To the
# subroutine, it will pass the name of the file which had
# trouble, as well as the current routine in the script
# having trouble , the filename and the current line
# number.
opendir (USER_CARTS, "$sc_user_carts_directory_path") || &file_open_error("$sc_user_carts_directory_path", "Delete Old Carts", __FILE__, __LINE__);
@carts = grep(/\.[0-9]/,readdir(USER_CARTS)); # must have . followed by digits
closedir (USER_CARTS);
# Now, for every cart in the directory, delete the cart if
# it is older than half a day. The -M file test returns
# the number of days since the file was last modified.
# Since the result is in terms of days, if the value is
# greater than the value of $sc_number_days_keep_old_carts
# set in core.setup.db, we'll delete the file.
foreach $cart (@carts)
{
# code below deletes carts and other files in this directory that have expired
$sc_cart_path = "$sc_user_carts_directory_path/$cart";
$sc_cart_path =~ /([\w\-\=\+\/\.]+)/;
$sc_cart_path = "$1";
$sc_cart_path = "" if ($sc_cart_path eq ".");
$sc_cart_path =~ s/^\/+//; # Get rid of any residual / prefix
&check_cart_expiry;
}
}# end of foreach
}# End of sub delete_old_carts
#######################################################################
# Assign a Shopping Cart. #
#######################################################################
# assign_a_unique_shopping_cart_id is a subroutine used to
# assign a unique cart id to every new clinet. It takes
# no argumnets and is called with the following syntax:
#
# &assign_a_unique_shopping_cart_id;
sub assign_a_unique_shopping_cart_id
{
# First we will check to see if the admin has asked us to
# log all new clients. If so, we will get the current
# date using the get_date subroutine discussed later, open the
# access log file for appending, and print to the access
# log file all of the environment variable values as well
# as the current date and time.
#
# However, we will protect ourselves from multiple,
# simultaneous writes to the access log by using the
# lockfile routine documented at the end of this file,
# passing it the name of a temporary lock file to use.
#
# Remember that there may be multimple simultaneous
# executions of this script because there may be many
# people shopping all at once. It would not do if one
# customer was able to overwrite the information of
# another customer if they accidentally wanted to acccess
# the log file at the same exact time.
if ($sc_shall_i_log_accesses eq "yes")
{
$date = &get_date;
&get_file_lock("$sc_access_log_path.lockfile");
open (ACCESS_LOG, ">>$sc_access_log_path");
# Using the keys function, the script grabs all the
# keys of the %ENV associative array and assigns them as
# elements of @env_keys. It then creates a new row for
# the access log which will be a pipe delimited list of
# the date as well as all the environment variables and
# their values.
$remote_addr = $ENV{'REMOTE_ADDR'};
$request_uri = $ENV{'REQUEST_URI'};
$http_user_agent = $ENV{'HTTP_USER_AGENT'};
if ($ENV{'HTTP_REFERER'} ne "")
{
$http_referer = $ENV{'HTTP_REFERER'};
}
else
{
$http_referer = "possible bookmarks";
}
$remote_host = $ENV{'REMOTE_HOST'};
#$shortdate = `date +"%T"`;
$shortdate = date;
chop ($shortdate);
$unixdate = time;
$new_access = "$form_data{'url'}\|$shortdate\|$request_uri\|$cookie{'visit'}\|$remote_addr\|$http_user_agent\|$http_referer\|$unixdate\|";
# The script then takes off the final pipe, adds the new
# access to the log file, closes the log file and removes
# the lock file.
chop $new_access;
print ACCESS_LOG "$new_access\n";
close (ACCESS_LOG);
&release_file_lock("$sc_access_log_path.lockfile");
}
# Now that the new access is recorded, the script assigns
# the user their own unique shopping cart. To do so,
# it generates a random (rand) 8 digit (100000000)
# integer (int) and then appends to that string the current
# process id ($$). However, the srand function is seeded
# with the time and the current process id in order to
# produce a more random random number. $sc_cart_path is
# also defined now that we have a unique cart id number.
srand (time|$$);
$cart_id = int(rand(10000000));
$cart_id .= ".$$";
$cart_id =~ s/-//g;
$sc_cart_path = "$sc_user_carts_directory_path/${cart_id}_cart";
# However, before we can be absolutely sure that we have
# created a unique cart, the script must check the existing
# list of carts to make sure that there is not one with
# the same value.
#
# It does this by checking to see if a cart with the
# randomly generated ID number already exists in the Carts
# directory. If one does exit (-e), the script grabs
# another random number using the same routine as
# above and checks again.
#
# Using the $cart_count variable, the script executes this
# algorithm three times. If it does not succeede in finding
# a unique cart id number, the script assumes that there is
# something seriously wrong with the randomizing routine
# and exits, warning the user on the web and the admin
# using the update_error_log subroutine discussed later.
$cart_count = 0;
while (-e "$sc_cart_path")
{
if ($cart_count == 3)
{
print "$sc_randomizer_error_message";
&update_error_log("COULD NOT CREATE UNIQUE CART ID", __FILE__, __LINE__);
&call_exit;
}
$cart_id = int(rand(10000000));
$cart_id .= "_$$";
$cart_id =~ s/-//g;
$sc_cart_path = "$sc_user_carts_directory_path/${cart_id}_cart";
$cart_count++;
} # End of while (-e $sc_cart_path)
# Now that we have generated a truly unique id
# number for the new client's cart, the script may go
# ahead and create it in the User_carts sub-directory.
#
# If there is a problem opening the new cart, we'll output
# an error message with the file_open_error subroutine
# discussed later.
&set_sc_cart_path; # there are other paths that must be set as well
&codehook("assign-cart_id");
&SetCookies;
}
#######################################################################
# Output Frontpage. #
#######################################################################
# output_frontpage is used to display the frontpage of the
# store. It takes no argumnets and is accessed with the
# following syntax:
#
# &output_frontpage;
#
# The subroutine simply utilizes the display_page
# subroutine which is discussed later to output the
# frontpage file, the location of which, is defined
# in core.setup.db. display_page takes four arguments:
# the cart path, the routine calling it, the current
# filename and the current line number.
sub output_frontpage {
&codehook("output_frontpage");
&display_page("$sc_store_front_path", "Output Frontpage", __FILE__,__LINE__);
}
#######################################################################
# Add to Shopping Cart #
#######################################################################
# The add_to_the_cart subroutine is used to add items to
# the customer's unique cart. It is called with no
# arguments with the following syntax:
#
# &add_to_the_cart;
sub add_to_the_cart
{
local (@database_rows, @database_fields, @item_ids, @display_fields);
local ($junk,$zzzitem,$temp)="";
local (%item_opt_verify);
&checkReferrer;
if (!($sc_db_lib_was_loaded =~ /yes/i)) {
&require_supporting_libraries (__FILE__, __LINE__, "$sc_db_lib_path");
}
# the script first opens the user's shopping cart with read/write access,
# creating it if for some reason it is not already there. If there is a
# problem opening the file, it will call file_open_error subroutine
# to handle the error reporting.
open (CART, "+>>$sc_cart_path") || &file_open_error("$sc_cart_path", "Add to Shopping Cart", __FILE__, __LINE__);
# The script then retrieves the highest item number of the items already
# in the cart (if any). The item number is an arbitrary number used to
# uniquely identify each item, as described below.
# init highest item number (start at 100)
$highest_item_number = 100;
# make sure we're positioned at top of file
seek (CART, 0, 0);
# loop on cart contents, if any
while ()
{
# get rid of terminating newline
chomp $_;
# split cart row into fields
my @row = split (/\|/, $_);
# get item number of row (last field)
my $item_number_info = pop (@row);
($item_number,$item_modifier) = split(/\*/,$item_number_info,2);
$highest_item_number = $item_number if ($item_number > $highest_item_number);
}
# $highest_item_number is now either the highest item number,
# or 0 if the cart was empty. Position the file pointer to the
# end of the cart, in preparation for appending the new items later.
# position to end of file
seek (CART, 0, 2);
# The script must first figure out what the client has
# ordered.
#
# It begins by using the %form_data associative array
# given to it by cgi-lib.pl. It takes all of the keys
# of the form_data associative array and drops them into
# the @items_ordered array.
#
# Note: An associative array key is like a variable name
# whereas an associative array value is the
# value associated with that variable name. The
# benefit of an associative array is that you can have
# many of these key/value pairs in one array.
# Conveniently enough, you'll notice that input fields on
# HTML forms will have associated NAMES and VALUES
# corresponding to associative array KEYS and VALUES.
#
# Since each of the text boxes in which the client could
# enter quantities were associated with the database id
# number of the item that they accompany, (as defined
# in the display_page routine at the end of this
# script), the HTML should read
#
#
#
# for the item with database id number 1234 and
#
#
#
# for item 5678.
#
# If the client orders 2 of 1234 and 9 of 5678, then
# @incoming_data will be a list of 1234 and 5678 such that
# 1234 is associated with 2 in %form_data associative
# array and 5678 is associated with 9. The script uses
# the keys function to pull out just the keys. Thus,
# @items_ordered would be a list like (1234, 5678, ...).
@items_ordered = keys (%form_data);
# Next it begins going through the list of items ordered
# one by one.
foreach $item (@items_ordered)
{
# However, there are some incoming items that don't need
# to be processed. Specifically, we do not care about cart_id,
# page, keywords, add_to_cart, or whatever incoming
# administrative variables exist because these are all
# values set internally by this script. They will be
# coming in as form data just like the client-defined
# data, and we will need them for other things, just not
# to fill up the user's cart. In order to bypass all of
# these administrartive variables, we use a standard
# method for denoting incoming items. All incoming items
# are prefixed with the tag "item-". When the script sees
# this tag, it knows that it is seeing an item to be added
# to the cart.
#
# Similarly, items which are actually options info are
# denoted with the "option" keyword. We will also accept
# those for further processing.
#
# And of course, we will not need to worry about any items
# which have empty values. If the shopper did not enter a
# quantity, then we won't add it to the cart.
if (($item =~ /^item-/i || $item =~ /^option/i) && $form_data{$item} ne "")
{
# Once the script has determined that the current element
# ($item) of @items_ordered is indeeed a non-admin item,
# it must separate out the items that have been ordered
# from the options which modify those items. If $item
# begins with the keyword "option", which we set
# specifically in the HTML file, the script will add
# (push) that item to the array called @options. However,
# before we make the check, we must strip the "item-"
# keyword off the item so that we have the actual row
# number for comparison.
$item =~ s/^item-//i;
if ($item =~ /^option/i)
{
#Manual price entry
if ($item =~ /^option\|price/i){
$form_data{$item} =~ s/\$//ig;
$form_data{$item} = &format_price("$form_data{$item}");
$form_data{$item} = "Price|"."$form_data{$item}";
}
# end manual price hack
push (@options, $item);
}
# On the other hand, if it is not an option, the script adds
# it to the array @items_ordered_with_options, but adds
# both the item and its value as a single array element.
#
# The value will be a quantity and the item will be
# something like "item-0001|12.98|The letter A" as defined in
# the HTML file. Once we extract the initial "item-"
# tag from the string using regular expressions ($item =~
# s/^item-//i;), the resulting string would be something
# like the following:
#
# 2|0001|12.98|The letter A
#
# where 2 is the quantity.
#
# Firstly, it must be a digit ($form_data{$item} =~ /\D/).
# That is, we do not want the clients trying to enter
# values like "a", "-2", ".5" or "1/2". They might be
# able to play havok on the ordering system and a sneaky
# client may even gain a discount because you were not
# reading the order forms carefully.
#
# Secondly, the script will dissallow any zeros
# ($form_data{$item} == 0). In both cases the client will
# be sent to the subroutine bad_order_note located in
# core_html_lib.pl.
else
{
if (($form_data{"item-$item"} =~ /\D/) || ($form_data{"item-$item"} == 0))
{
&bad_order_note;
}
else
{
$quantity = $form_data{"item-$item"};
push (@items_ordered_with_options, "$quantity\|$item\|");
}
}
# End of if ($item ne "$variable" && $form_data{$item} ne "")
}
#End of foreach $item (@items_ordered)
}
# Now the script goes through the array
# @items_ordered_with_options one item at a time in order
# to modify any item which has had options applied to it.
# Recall that we just built the @options array with all
# the options for all the items ordered. Now the script
# will need to figure out which options in @options belong
# to which items in @items_ordered_with_options.
foreach $item_ordered_with_options (@items_ordered_with_options)
{
&codehook("foreach_item_ordered_top");
# First, clear out a few variables that we are going to
# use for each item.
#
# $options will be used to keep track of all of the
# options selected for any given item.
#
# $option_subtotal will be used to determine the total
# cost of each option.
#
# $option_grand_total will be used to calculate the
# total cost of all ordered options.
#
# $item_grand_total will be used to calculate the total
# cost of the item ordered factoring in quantity and
# options.
$options = "";
$option_subtotal = "";
$option_grand_total = "";
$item_grand_total = "";
# Now split out the $item_ordered_with_options into it's
# fields. Note that we have defined the index location of
# some important fields in core.setup.db Specifically,
# the script must know the index of quantity, item_id and
# item_price within the array. It will need these values
# in particular for further calculations. Also, the
# script will change all occurances of "~qq~" to a double
# quote (") character, "~gt~" to a greater than sign (>)
# and "~lt~" to a less than sign (<). The reason that
# this must be done is so that any double quote, greater
# than, or less than characters used in URLK strings can
# be stuffed safely into the cart and passed as part of
# the NAME argumnet in the "add item" form. Consider the
# following item name which must include an image tag.
#
# /g;
$item_ordered_with_options =~ s/~lt~/\Red
#
# This is the second option modifying item number 0001.
# When displayed in the display cart screen, it will read
# "Red 0.00, and will not affect the cost of the item.
# need to process specific add-to-cart-opt type corescript, if present in
# option file(s)
$field = &corescript($item_corescript,"add-to-cart-opt-" . $option_number,
"$option_location",__FILE__,__LINE__);
($option_name,$option_price,$option_shipping)=split(/\|/,$form_data{$option});
if($option_name) {
if ($sc_use_verified_opt_values =~ /yes/i) {
$temp = $item_opt_verify{$option . '|' . $option_name};
($option_price,$option_shipping) = split(/\|/,$temp,2);
if ($temp ne "") {
$temp = $option_name . '|' . $temp;
} else {
$option_name=""; # erase it, unverifiable
}
} else {
$temp = $form_data{$option};
}
if($option_name) {# still here, either verified or not doing verification
# Keep track of the numbers chosen and their value (3.3c)
$temp =~ s/\|/~/g; # cannot have pipes, change to ~ char
if ($item_option_numbers eq ""){
$item_option_numbers = "${option_number}*$temp";
} else {
$item_option_numbers .= ",${option_number}*$temp";
}
if ((0 + $option_price) == 0) { #price zero, do not display it
$display_option_price = "";
} else { # price non-zero, must format it
$display_option_price = " " . &display_price_nospaces($option_price);
}
#Below displays options in html with comma after each by Lennie
#does not affect email layout.
# This line displays option and price during check out
#$options .= "$option_name$display_option_price, ";
# The below line only displays option name with no dollar amount.
$options .= "$option_name$display_option_price, ";
}
}
# But the script must also calculate the cost changes with
# options. To do so, it will take the current value of
# $option_grand_total and add to it the value of the
# current option. It will then format the result to
# two decimal places using the format_price subroutine
# discussed later and assign the new result to
# $option_grand_total
&codehook("process_cart_options");
$item_shipping = $item_shipping + $option_shipping;
$unformatted_option_grand_total = $option_grand_total + $option_price;
$option_grand_total = &format_price($unformatted_option_grand_total);
# End of if ($option_item_number eq "$item_id_number")
}
# End of foreach $option (@options)
}
# Next, calculate $item_number which the script can use to
# identify a shopping cart item absolutely. This must be done
# so that when we modify and delete from the cart, we will
# know exactly which item to affect. We cannot rely simply
# on the unique database id number because a client may
# purchase two of the same item but with different
# options. Unless there is a separate, unique cart row id
# number, how would the script know which to delete if the
# client asked to delete one of the two. Add 1 to
# $highest_item_number, which was set at the beginning of
# the subroutine.
$item_number = ++$highest_item_number;
# Finally, the script makes the last price calculations
# and appends every ordered item to $cart_row
#
# A completed cart row might look like the following:
# 2|0001|Vowels|15.98|Letter A|Times New Roman 0.00|15.98|161
$unformatted_item_grand_total = $item_price + $option_grand_total;
$item_grand_total = &format_price("$unformatted_item_grand_total");
# now, make the cart value for shipping be the shipping with options
$cart_row[$cart{"shipping"}] = $item_shipping;
$cart_row[$cart{"shipping_calc_flag"}] = "";
# Add the id #s of the options too
$cart_row[$cart{"options_ids"}] = $item_option_numbers;
# need to process generic add-to-cart type corescript, if present in
# option file(s)
$field = &corescript($item_corescript,"add-to-cart",
"$option_location",__FILE__,__LINE__);
&codehook("before_build_cart_row");
$cart_row[$cart{"user1"}] = $item_user1;
$cart_row[$cart{"user2"}] = $item_user2;
$cart_row[$cart{"user3"}] = $item_user3;
foreach $field (@cart_row) {
$cart_row .= "$field\|";
}
$cart_row .= "$options\|$item_grand_total\|$item_number\n";
# End of foreach $item_ordered_with_options.....
&codehook("foreach_item_ordered_end");
}
# When it is done appending all the items to $cart_row,
# the script appends the new items to the end of the
# shopping cart, which was opened at the beginning of the subroutine.
&codehook("before_add_cart_rows");
if (-e "$sc_cart_path")
{
open (CART, ">>$sc_cart_path") || &file_open_error("$sc_cart_path", "Add to Shopping Cart", __FILE__, __LINE__);
&codehook("add_cart_row_to_cart");
print CART "$cart_row";
close (CART);
}
else
{
open (CART, ">$sc_cart_path") || &file_open_error("$sc_cart_path", "Add to Shopping Cart", __FILE__, __LINE__);
&codehook("add_cart_row_to_cart");
print CART "$cart_row";
close (CART);
}
&codehook("after_add_cart_rows");
# Then, the script sends the client back to a previous
# page. There are two pages that the customer can be sent
# of course, the last product page they were on or the
# page which displays the customer's cart. Which page the
# customer is sent depends on the value of
# $sc_should_i_display_cart_after_purchase which is defined
# in core.setup.db If the customer should be sent to
# the display cart page, the script calls
# display_cart_contents, otherwise it calls display_page
# if this is an HTML-based cart or
# create_html_page_from_db if this is a database-based
# cart.
&finish_add_to_the_cart;
}
############################################################
sub finish_add_to_the_cart {
&codehook("finish_add_to_the_cart");
if ($sc_use_html_product_pages eq "yes")
{
if ($sc_should_i_display_cart_after_purchase eq "yes")
{
&display_cart_contents;
}
else
{
&display_page("$sc_html_product_directory_path/$page", "Display Products for Sale");
}
}
else
{
if ($sc_should_i_display_cart_after_purchase eq "yes")
{
&display_cart_contents;
}
elsif ($are_any_query_fields_filled_in =~ /yes/i)
{
$page = "";
&display_products_for_sale;
}
else
{
&create_html_page_from_db;
}
}
}
#######################################################################
# Output Modify Quantity Form #
#######################################################################
# output_modify_quantity_form is the subroutine
# responsible for displaying the form which customers can
# use to modify the quantity of items in their cart. It
# is called with no argumnets with the following syntax:
#
# &output_modify_quantity_form;
sub output_modify_quantity_form
{
# The subroutine begins by outputting the HTML header
# using standard_page_header, adds the modify form using
# display_cart_table and finishes off the HTML page with
# modify_form_footer. All of these subrotuines are
# discussed in core_html_lib.pl
&standard_page_header("Change Quantity");
&display_cart_table("changequantity");
&modify_form_footer;
}
#######################################################################
# Modify Quantity of Items in the Cart #
#######################################################################
# The modify_quantity_of_items_in_cart subroutine is
# responsible for making quantity modifications in the
# customer's cart. It takes no arguments and as called
# with the following syntax:
#
# &modify_quantity_of_items_in_cart;
sub modify_quantity_of_items_in_cart
{
&checkReferrer;
# First, the script gathers the keys as it did for the
# add_to_cart routine previously, checking to make
# sure the customer entered a positive integer (not
# fractional and not less than one).
@incoming_data = keys (%form_data);
foreach $key (@incoming_data)
{
if (($key =~ /[\d]/) && ($form_data{$key} =~ /\D/) &&
(!($form_data{$key} < 0)) && (!($form_data{$key} > 0)))
{
$form_data{$key}="";
}
# Just as the script did in the add to cart routine
# previuosly, it will create an array (@modify_items) of
# valid keys.
unless ($key =~ /[\D]/ && $form_data{$key} =~ /[\D]/)
{
if ($form_data{$key} ne "")
{
push (@modify_items, $key);
}
}
# End of foreach $key (@incoming_data)
}
# Then, the script must open up the client's cart and go
# through it line by line. File open problems are
# handled by file_open_error as usual.
open (CART, "<$sc_cart_path") || &file_open_error("$sc_cart_path", "Modify Quantity of Items in the Cart", __FILE__, __LINE__);
# As the script goes through the cart, it will split each
# row into its database fields placing them as elements in
# @database_row. It will then grab the unique cart row
# number and subsequently replace it in the array.
#
# The script needs this number to check the current line
# against the list of items to be modified. Recall that
# this list will be made up of all the cart items which
# are being modified.
#
# The script also grabs the current quantity of that row.
# Since it is not yet sure if it wants the current
# quantity, it will hold off on adding it back to the
# array. Finally, the script chops the newline character
# off the cart row number.
while ()
{
@database_row = split (/\|/, $_);
$cart_row_number = pop (@database_row);
push (@database_row, $cart_row_number);
$old_quantity = shift (@database_row);
chop $cart_row_number;
# Next, the script checks to see if the item number
# submitted as form data is equal to the number of the
# current database row.
foreach $item (@modify_items)
{
if ($item eq $cart_row_number)
{
# If so, it means that the script must change the quantity
# of this item. It will append this row to the
# $shopper_row variable and begin creating the modified
# row. That is, it will replace the old quantity with the
# quantity submitted by the client ($form_data{$item}).
# Recall that $old_quantity has already been shifted off
# the array.
# if negative value entered, "subtract" from old value
# same if, for example, +6 is entered, add 6 to the current value
if ($form_data{$item} =~ /\-/) {
$form_data{$item} =~ s/\-//g;
$form_data{$item} = 0 - $form_data{$item};
}
if (($form_data{$item} < 0) || ($form_data{$item} =~ /\+/)) {
&codehook("item_quantity_to_be_modified");
$form_data{$item} =~ s/\+//g;
$form_data{$item} = $old_quantity + $form_data{$item};
}
# if invalid qty entered, removes item
$form_data{$item} = 0 + $form_data{$item};
# if negative or zero, then delete the item from the cart
if ($form_data{$item} le 0) {
$shopper_row .= "\|"; #this forces a deletion from CART
} else {
$shopper_row .= "$form_data{$item}\|";
# Now the script adds the rest of the database row to
# $shopper_row and sets two flag variables.
#
# $quantity_modified lets us know that the current row
# has had a quantity modification for each iteration of
# the while loop.
foreach $field (@database_row)
{
$shopper_row .= "$field\|";
}
}
$quantity_modified = "yes";
&codehook("item_quantity_modified");
chop $shopper_row; # Get rid of last pipe symbol but not the
# newline character
# End of if ($item eq $cart_row_number)
}
# End of foreach $item (@modify_items)
}
# If the script gets this far and $quantity_modified has
# not been set to "yes", it knows that the above routine
# was skipped because the item number submitted from the
# form was not equal to the curent database id number.
#
# Thus, it knows that the current row is not having its
# quantity changed and can be added to $shopper_row as is.
# Remember, we want to add the old rows as well as the new
# modified ones.
if ($quantity_modified ne "yes")
{
$shopper_row .= $_;
}
# Now the script clears out the quantity_modified variable
# so that next time around it will have a fresh test.
$quantity_modified = "";
# End of while ()
}
close (CART);
# At this point, the script has gone all the way through
# the cart. It has added all of the items without
# quantity modifications as they were, and has added all
# the items with quantity modifications but made the
# modifications.
#
# The entire cart is contained in the $shopper_row
# variable.
#
# The actual cart still has the old values, however. So
# to change the cart completely the script must overwrite
# the old cart with the new information and send the
# client back to the view cart screen with the
# display_cart_contents subroutine which will be discussed
# later. Notice the use of the write operator (>) instead
# of the append operator (>>).
open (CART, ">$sc_cart_path") || &file_open_error("$sc_cart_path", "Modify Quantity of Items in the Cart", __FILE__, __LINE__);
print CART "$shopper_row";
close (CART);
&codehook("modify_quantity_of_items_in_cart_bot");
&finish_modify_quantity_of_items_in_cart;
# End of if ($form_data{'submit_change_quantity'} ne "")
}
#######################################################################
sub finish_modify_quantity_of_items_in_cart {
&codehook("finish_modify_quantity_of_items_in_cart");
&display_cart_contents;
}
#######################################################################
# Output Delete Item Form #
#######################################################################
# The output_delete_item_form subroutine is responsible
# for displaying the HTML form which the customer can use
# to delete items from their cart. It takes no arguments
# and is called with the following syntax:
#
# &output_delete_item_form;
sub output_delete_item_form
{
# As it did when it printed the modification form, the
# script uses several subroutines in core_html_lib.pl
# to generate the header, body and footer of the delete
# form.
&standard_page_header("Delete Item");
&display_cart_table("delete");
&delete_form_footer;
# End of if ($form_data{'delete_item'} ne "")
}
#######################################################################
# Delete Item From Cart #
#######################################################################
# The job of delete_from_cart is to take a set of items
# submitted by the user for deletion and actually delete
# them from the customer's cart. The subroutine takes no
# arguments and is called with the following syntax:
#
# &delete_from_cart;
sub delete_from_cart
{
&checkReferrer;
# As with the modification routines, the script first
# checks for valid entries. This time though it only needs
# to make sure that it filters out the extra form
# keys rather than make sure that it has a positive
# integer value as well because unlike with a text entry,
# clients have less ability to enter bad values with
# checkbox submit fields.
@incoming_data = keys (%form_data);
foreach $key (@incoming_data)
{
# We still want to make sure that the key is a cart row
# number though and that it has a value associated with
# it. If it is actually an item which the user has asked to
# delete, the script will add it to the delete_items
# array.
unless ($key =~ /[\D]/)
{
if ($form_data{$key} ne "")
{
push (@delete_items, $key);
}
# End of unless ($key =~ /[\D]/...
}
# End of foreach $key (@incoming_data)
}
# Once the script has gone through all the incomming form
# data and collected the list of all items to be deleted,
# it opens up the cart and gets the $cart_row_number,
# $db_id_number, and $old_quantity as it did in the
# modification routines previously.
open (CART, "<$sc_cart_path") || &file_open_error("$sc_cart_path", "Delete Item From Cart", __FILE__, __LINE__);
while ()
{
@database_row = split (/\|/, $_);
$cart_row_number = pop (@database_row);
$db_id_number = pop (@database_row);
push (@database_row, $db_id_number);
push (@database_row, $cart_row_number);
chop $cart_row_number;
$old_quantity = shift (@database_row);
# Unlike modification however, for deletion all we need to
# do is check to see if the current database row matches
# any submitted item for deletion. If it does not match
# the script adds it to $shopper_row. If it is equal,
# it does not. Thus, all the rows will be added to
# $shopper_row except for the ones that should be deleted.
$delete_item = "";
foreach $item (@delete_items)
{
if ($item eq $cart_row_number)
{
$delete_item = "yes";
&codehook("mark_item_for_delete");
}
# End of foreach $item (@add_items)
}
if ($delete_item ne "yes")
{
$shopper_row .= $_;
}
# End of while ()
}
close (CART);
# Then, as it did for modification, the scipt overwrites
# the old cart with the new information and
# sends the client back to the view cart page with the
# display_cart_contents subroutine which will be discussed
# later.
open (CART, ">$sc_cart_path") || &file_open_error("$sc_cart_path", "Delete Item From Cart", __FILE__, __LINE__);
print CART "$shopper_row";
close (CART);
&finish_delete_from_cart;
# End of if ($form_data{'submit_deletion'} ne "")
}
#######################################################################
sub finish_delete_from_cart{
&codehook("finish_delete_from_cart");
&display_cart_contents;
}
#######################################################################
# Display Products for Sale #
#######################################################################
# display_products_for_sale is used to generate
# dynamically the "product pages" that the client will
# want to browse through. There are two cases within it
# however.
#
# Firstly, if the store is an HTML-based store, this
# routine will either display the requested page
# or, in the case of a search, perform a search on all the
# pages in the store for the submitted keyowrd.
#
# Secondly, if this is a database-based store, the script
# will use the create_html_page_from_db to output the
# product page requested or to perform the search on the
# database.
#
# The subroutine takes no arguments and is called with the
# following syntax:
#
# &display_products_for_sale;
sub display_products_for_sale
{
if ($form_data{'keywords'} ne "")
{
open(SEARCH,$sc_search_log_path)||die "$sc_access_log_path doesn't look good";
@lines=;
close(SEARCH);
foreach $link(@lines)
{
chomp($link);
($phrase,$hits)=split(/\|/,$link);
$phrase_array{$phrase}=$hits;
}
{$phrase_used=$form_data{'keywords'}}
$phrase_array{$phrase_used}++;
{@keys=sort {$phrase_array{$a} <=> $phrase_array{$b}} keys(%phrase_array)}
open(SEARCH,">$sc_search_log_path");
foreach $key(@keys)
{
print SEARCH qq~$key|$phrase_array{$key}\n~;
}
close(SEARCH);
}
# The script first determines which type of store this is.
# If it turns out to be an HTML-based store, the script
# will check to see if the current request is a keyword
# search or simply a request to display a page. If it is
# a keyword search, the script will require the html
# search library and use the html_search subroutine with
# in it to perform the search.
if ($sc_use_html_product_pages eq "yes")
{
if ($search_request ne "")
{
&standard_page_header("Search Results");
require "$sc_html_search_routines_library_path";
&html_search;
&html_search_page_footer;
&call_exit;
}
# If the store is HTML-based and there is no current
# keyword however, the script simply displays the page as
# requested with display_page which will be discussed
# shortly.
&display_page("$sc_html_product_directory_path/$page", "Display Products for Sale", __FILE__, __LINE__);
}
# On the other hand, if $sc_use_html_product_pages was set to
# no, it means that the admin wants the script to generate
# HTML product pages on the fly using the format string
# and the raw database rows. The script will do so
# using the create_html_page_from_db subroutine which will
# be discussed next.
else
{
&create_html_page_from_db;
}
}
#######################################################################
# create_html_page_from_db Subroutine #
#######################################################################
# create_html_page_from_db is used to genererate the
# navigational interface for database-base stores. It is
# used to create both product pages and "list of products"
# pages. The subroutine takes no arguments and is called
# with the following syntax:
#
# &create_html_page_from_db;
sub create_html_page_from_db
{
# First, the script defines a few working variables which
# will remain local to this subroutine.
local (@database_rows, @database_fields, @item_ids, @display_fields);
local ($total_row_count, $id_index, $display_index, $found, $product_id);
local ($row, $field, $empty, $option_tag, $option_location, $output);
# Next the script checks to see if there is actually a
# page which must be displayed. If there is a value for
# the page variable incoming as form data, (ie: list of
# product page) the script will simply display that page
# with the display_page subroutine and exit.
if ($page ne "" && $search_request eq "" &&
$form_data{'continue_shopping_button'} eq "")
{
&display_page("$sc_html_product_directory_path/$form_data{'page'}",
"Display Products for Sale", __FILE__, __LINE__);
&call_exit;
}
# If there is no page value, then the script knows that it
# must generate a dynamic product page using the value of
# the product form variable to query the database.
#
# First, the script uses the product_page_header
# subroutine in order to dynamically generate the product
# page header. We'll pass to the subroutine the value of
# the page we have been asked to display so that it can
# display something useful in the area.
#
# The product_page_header subroutine is located in
# core_html_lib.pl and $sc_product_display_title is
# defined in the setup file.
#&product_page_header($sc_product_display_title);
#
#if ($form_data{'add_to_cart_button.x'} ne "" && $sc_shall_i_let_client_know_item_added eq "yes")
#
#{
#print "$sc_item_ordered_message";
#}
# Next the database is querried for rows containing the
# value of the incoming product variable in the correct
# category as defined in core.setup.db The script uses
# the submit_query subroutine in core_db_lib.pl
# passing to it a reference to the list array
# database_rows.
#
# submit_query returns a descriptive status message
# if there was a problem and a total row count
# for diagnosing if the maximum rows returned
# variable was exceeded.
if (!($sc_db_lib_was_loaded =~ /yes/i)) {
&require_supporting_libraries (__FILE__, __LINE__, "$sc_db_lib_path");
}
($status,$total_row_count) = &submit_query(*database_rows);
# Now that the script has the database rows to be
# displayed, it will display them.
#
# Firstly, the script goes through each database row
# contained in @database_rows splitting it into it's
# fields.
#
# For the most part, in order to display the database
# rows, the script will simply need to take each field
# from the database row and substitute it for a %s in the
# format string defined in core.setup.db
#
# However, in the case of options which will modify a
# product, the script must grab the code from an options
# file.
#
# The special way that options are denoted in the database
# are by using the format %%OPTION%%option.html in the
# data file. This string includes two important bits of
# information.
#
# Firstly, it begins with %%OPTION%%. This is a flag
# which will let the script know that it needs to deal
# with this database field as if it were an option. When
# it sees the flag, it will then look to the bit after the
# flag to see which file it should load. Thus, in this
# example, the script would load the file option.html for
# display.
#
# Why go through all the trouble? Well basically, we need
# to create a system which will handle large chunks of
# HTML code within the database that are very likely to be
# similar. If there are options on product pages, it is
# likely that they are going to be repeated fairly
# often. For example, every item in a database might have
# an option like tape, cd or lp. By creating one
# options.html file, we could easily put all the code into
# one shared location and not need to worry about typing
# it in for every single database entry.
# DELUXE version sanity check
if (($form_data{'next'}+$sc_db_max_rows_returned) < 1) {
$form_data{'next'} = 0;
}
$nextCount = $form_data{'next'}+$sc_db_max_rows_returned;
$prevCount = $form_data{'next'}-$sc_db_max_rows_returned;
$minCount = $form_data{'next'};
$maxCount = $form_data{'next'}+$sc_db_max_rows_returned;
if ($maxCount < @database_rows) {
$my_max_count = $maxCount;
} else {
$my_max_count = @database_rows;
}
$num_returned = @database_rows;
$nextHits = $sc_db_max_rows_returned;
$prod_message = &product_message($status,$num_returned,$nextHits);
&product_page_header($sc_product_display_title);
if ($form_data{'add_to_cart_button.x'} ne "" &&
$sc_shall_i_let_client_know_item_added eq "yes") {
print "$sc_item_ordered_message";
}
$last_product_displayed = "no";
# core version 3.2b -- now it is a list of keys, not full rows
foreach $row (@database_rows)
{
$rowCount++;
$prevHits = $sc_db_max_rows_returned;
$nextHits = $sc_db_max_rows_returned;
if ($rowCount > $minCount && $rowCount <= $maxCount)
{
#@database_fields = split (/\|/, $row);
$product_id = $row;
$found = &check_db_with_product_id($product_id,*database_fields);
&codehook("create_html_page_read_db_item");
foreach $field (@database_fields)
{
# DELUXE feature ... if field starts with %%IMG%% then it is an image,
# and we will generate an HTML IMG tag for it
if ($field =~ /^%%IMG%%/i)
{
($empty, $image_tag, $image_location) = split (/%%/, $field);
$field = '';
}
# For every field in every database row, the script simply
# checks to see if it begins (^) with %%OPTION%%. If so,
# it splits out the string into three strings, one
# empty, one equal to OPTION and one equal to the location
# of the option to be used. Then the script resets the
# field to null because it is about to overwrite it.
if ($field =~ /^%%OPTION%%/i)
{
($empty, $option_tag, $option_location) = split (/%%/, $field);
$field = "";
# The option file is then opened and read. Next, every
# line of the option file is appended to the $field
# variable and the file is closed again. Then the
# current product id number is substituted for the
# %%PRODUCT_ID%% flag in the option_prep subroutine and
# and any optpre and optpost corescript is run
open (OPTION_FILE, "<$sc_options_directory_path/$option_location") ||
&file_open_error ("$sc_options_directory_path/$option_location", "Display Products for Sale", __FILE__,__LINE__);
read (OPTION_FILE,$field,99999); # read it in all at once
close (OPTION_FILE);
$field = &option_prep($field,$option_location,$product_id);
# End of if ($field =~ /^%%OPTION%%/)
}
# Now see if we need to load a generic file of some type
if ($field =~ /^%%FILE%%/i)
{
($empty, $option_tag, $option_location) = split (/%%/, $field);
$field = "";
open (OPTION_FILE, "<$sc_generic_directory_path/$option_location");
read (OPTION_FILE,$field,99999); # read it in all at once
close (OPTION_FILE);
$field = &corescript($field,"pre","$option_location",__FILE__,__LINE__);
$cart_id_for_html = &cart_id_for_html;
$field =~ s/%%PRODUCT_ID%%/$database_fields[$sc_db_index_of_product_id]/g;
$field =~ s/%%PRODUCTID%%/$database_fields[$sc_db_index_of_product_id]/g;
$field =~ s/%%URLofImages%%/$URL_of_images_directory/g;
$field =~ s/%%cart_id%%/$cart_id_for_html/g;
$field = &corescript($field,"post","$option_location",__FILE__,__LINE__);
($very_first_part,$field,$junk) =
split(/
--cut here--<\/h3>/i,$field,3);
if ($field eq "") {
$field = $very_first_part;
}
if ($field eq "") {
$field = "(file $option_location not found)";
}
# End of if ($field =~ /^%%FILE%%/)
}
# End of foreach $field (@database_fields)
}
if ($rowCount == (1 + $minCount)) {
$first_product_displayed = "yes";
} else {
$first_product_displayed = "no";
if ($rowCount == $maxCount) {
$last_product_displayed = "yes";
}
}
&create_display_fields(@database_fields);
&displayProductPage;
# End of foreach $row (@database_rows)
}
}
&product_page_footer($status,$total_row_count);
&call_exit;
}
#######################################################################
# display_cart_contents Subroutine #
#######################################################################
# display_cart_contents is used to display the current
# contents of the customer's cart. It takes no arguments
# and is called with the following syntax:
#
# &display_cart_contents;
sub display_cart_contents
{
local ($my_gt,$my_tq,$tmq,$tc,$st) = "";
# The subroutine begins by defining some working variables
# as local to the subroutine.
local (@cart_fields);
local ($field, $cart_id_number, $quantity, $display_number,
$unformatted_subtotal, $subtotal, $unformatted_grand_total,
$grand_total);
# Next, as when we created the modification and deletion
# forms for cart manipulation, we will use the routines in
# core_html_lib.pl to generate the header, body and
# footer of the cart page. However, unlike with the
# modification and deletion forms, we will not need an
# extra table cell for the checkbox or text field. Thus,
# we will not pass anything to display_cart_table. We
# will simply get a table representing the current
# contents of the customer's cart.
$sc_special_page_meta_tags = "\n";
$sc_special_page_meta_tags .= '';
$sc_special_page_meta_tags .= "\n";
$sc_special_page_meta_tags .= '';
$sc_special_page_meta_tags .= "\n";
&standard_page_header("View/Modify Cart");
($my_gt,$my_tq,$tmq,$tc,$st) = &display_cart_table("");
&cart_footer((0+$my_gt),(0+$my_tq));
&call_exit;
# End of sub display_cart_contents
}
#######################################################################
# file_open_error Subroutine #
#######################################################################
# If there is a problem opening a file or a directory, it
# is useful for the script to output some information
# pertaining to what problem has occurred. This
# subroutine is used to generate those error messages.
#
# file_open_error takes four arguments: the file or
# directory which failed, the section in the code in which
# the call was made, the current file name and
# line number, and is called with the following syntax:
#
# &file_open_error("file.name", "ROUTINE", __FILE__,
# __LINE__);
sub file_open_error
{
# The subroutine simply uses the update_error_log
# subroutine discussed later to modify the error log and
# then uses CgiDie in cgi-lib.pl to gracefully exit the
# application with a useful debugging error message sent
# to the browser window.
local ($bad_file, $script_section, $this_file, $line_number) = @_;
&update_error_log("FILE OPEN ERROR-$bad_file", $this_file, $line_number);
open(ERROR, $error_page);
while ()
{
print $_;
}
close (ERROR);
}
#######################################################################
# display_page Subroutine #
#######################################################################
# display_page is used to filter HTML pages through the
# script and display them to the browser window.
#
# display_page takes four arguments: the file or
# directory which failed, the section in the code in which
# the erroneous call was made, the current file name and
# line number, and is called with the following syntax:
#
# &file_open_error("file.name", "ROUTINE", __FILE__,
# __LINE__);
#
# (notice the two special Perl variables __FILE__, which
# equals the current filename, and __LINE__ which equals
# the current line number).
sub display_page
{
local ($page, $routine, $file, $line) = @_;
local($the_file)="";
local($href_fields,$hidden_fields);
$href_fields = &make_href_fields;
$hidden_fields = &make_hidden_fields;
$cart_id_for_html = "%%ZZZ%%";
# the subroutine begins by opening the requested file for
# reading, exiting with file_open_error if there is a
# problem as usual.
open (PAGE, "<$page") || &file_open_error("$page", "$routine", $file, $line);
# It then reads in the file one line at a time.
while ()
{
# Check to see if the add_to_cart_button button
# has been clicked. if so, it means that we have just
# added an item and are returning to the display of the
# product page. In this case, we will sneak in an addition
# confirmation message right after the