#!/usr/bin/perl use warnings; use CGI::Carp qw/fatalsToBrowser/; use CGI qw(:standard); use MD5; # Manual add item hack here (auction payments) # Format price hack here # Add item name to "thank you your selection added" message # hack here - sub add_to_the_cart # Quantity break discount hack entry here and subroutine # in commerce_order_lib.pl # added switch for ordered message for trollbeads wishlist # trollbeads list #set the default first choice for options that we will disallow $default_first_choice = "Please Choose..."; ## This script is FREE but if you'd like to help us develop ## more free stuff, your always welcome to send a donation! ## ## Carey Internet Services ## 28 Route 6A ## Sandwich, MA 02563 ## ## Commerce.cgi 2.0 Beta 2 ## ## Version history is available at... ## http://www.careyinternet.com/main/commerce/version.html ## ## Commerce.cgi is based on Selena Sol's freeware 'Web Store' ## available at http://www.extropia.com. Modifications made ## independently by Carey Internet Services. The entire package as ## distributed here is Copyright 1999-2000 by Carey Internet Services ## and is distributed free of charge consistent with the GNU General ## Public License Version 2 dated June 1991. ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License ## Version 2 as published by the Free Software Foundation. ## ## 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. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## Set this to "yes" for card validation, or "no" for none: $validate_card_cc="no"; $| = 1; ## Hack for external cgi scripts # This is a list of the scripts that you want to be able to call from commerce.cgi # If they are in the same folder as commerce.cgi then you can use ./ otherwise you # should just use the full Unix path to the file. $ENV{"PATH"} = ""; @execute_cgi = ("./page.pl?page=classes"); ## end external cgi $time = time; ## Stop and record hackers if ($ENV{'REQUEST_URI'} =~ /\.\./i) { $ipnum = $ENV{'REMOTE_ADDR'}; @digits = split (/\./, $ipnum); $address = pack ("C4", @digits); $host = gethostbyaddr ($address, 2); $date = &get_date; open (HACK_LOG, "+>>./log_files/hack.log"); print HACK_LOG "$ENV{'REMOTE_ADDR'}\|$ENV{'REMOTE_PORT'}\|$host\|$date\|$ENV{'REQUEST_URI'}\n"; close HACK_LOG; print "Content-type: text/html\n\n"; print qq~ Hack Attempt

 

Hack attempt logged and will be reported to your ISP!

IP: $ENV{'REMOTE_ADDR'}
Remote port: $ENV{'REMOTE_PORT'}
Host: $host


 

~; exit; } ## End hack stop &require_supporting_libraries (__FILE__, __LINE__, "./admin_files/commerce_user_lib.pl"); &require_supporting_libraries (__FILE__, __LINE__, "./admin_files/$sc_gateway_name-user_lib.pl"); &require_supporting_libraries (__FILE__, __LINE__, "./library/commerce.setup.db"); &require_supporting_libraries (__FILE__, __LINE__, "./library/commerce_order_lib.pl"); &require_supporting_libraries (__FILE__, __LINE__, "$sc_cgi_lib_path", "$sc_html_setup_file_path", "$sc_mail_lib_path", "$sc_cookie_lib", "$sc_commerce_subs_path", "$sc_process_order_lib_path"); # to disallow categories require "./disallow-lib.pl"; &read_and_parse_form_data; &require_supporting_libraries (__FILE__, __LINE__, "./library/se_advanced.pl"); &get_cookie; &require_supporting_libraries (__FILE__, __LINE__, "./library/special_library.pl"); $page = $form_data{'page'}; $page =~ /([\w\-\=\+\/]+)\.(\w+)/; $page = "$1.$2"; $page = "" if ($page eq "."); $page =~ s/^\/+//; # Get rid of any residual / prefix $search_request = $form_data{'search_request_button'}; $cart_id = $form_data{'cart_id'}; &error_check_form_data; if ($cookie{'cart_id'} eq "" && $form_data{'cart_id'} eq "") { &delete_old_carts; &assign_a_unique_shopping_cart_id; } if ($form_data{'cart_id'} eq "") { $cart_id = $cookie{'cart_id'}; $sc_cart_path = "$sc_user_carts_directory_path/${cart_id}.cart"; $sc_cart_path =~ /([\w\-\=\+\/]+)\.(\w+)/; $sc_cart_path = "$1.$2"; $sc_cart_path = "" if ($sc_cart_path eq "."); $sc_cart_path =~ s/^\/+//; # Get rid of any residual / prefix } else { $cart_id = $form_data{'cart_id'}; $sc_cart_path = "$sc_user_carts_directory_path/${cart_id}.cart"; $sc_cart_path =~ /([\w\-\=\+\/]+)\.(\w+)/; $sc_cart_path = "$1.$2"; $sc_cart_path = "" if ($sc_cart_path eq "."); $sc_cart_path =~ s/^\/+//; # Get rid of any residual / prefix } # new 07-10-10 Chris Costa if ($cookie{'cart_id'} eq "" && ($form_data{'cart_id'} || $cart_id)) { &SetCookies; } #Chris Costa 03-2012 to allow mobile site template use # if ($form_data{'mobile'} eq "yes"){ # &delete_cookie("ismobile"); # &set_custom_cookie("ismobile","yes"); # $sc_store_template_file = $sc_mobile_template_file; # }elsif ($form_data{'mobile'} eq "no"){ # &delete_cookie("ismobile"); # &set_custom_cookie("ismobile","no"); # $sc_store_template_file = $sc_store_template_file; # }else{ # no override on mobile, run the subroutine &checkmobilebrowser; # } sub checkmobilebrowser{ if ($cookie{'ismobile'} eq "yes"){ $sc_store_template_file = $sc_mobile_template_file; }elsif ($cookie{'ismobile'} eq "no"){ $sc_store_template_file = $sc_store_template_file;# do nothing here }else{ # no mobile cookie set yet # check if on a mobile device - subroutine is found in commerce html lib.pl my $checkmobile = &isMobile; # 1 is yes, 0 is no if ($checkmobile eq "1") { $sc_store_template_file = $sc_mobile_template_file; # &set_custom_cookie("ismobile","yes"); }else{ # &set_custom_cookie("ismobile","no"); } } # end mobile cookie code } # end sub checkmobilebrowser print "Content-type: text/html\n\n"; # Make sure that the cart_id starts with a digit. if (substr($cart_id, 0, 1) =~ /\D/) { print "Invalid Cart ID!\n"; exit; } $are_any_query_fields_filled_in = "no"; 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 ($form_data{'add_to_cart_button.x'} ne "") { # fix for new_arrivals if (($form_data{product} eq "") && ($sc_show_new_arrivals eq "yes") && (!$form_data{pid}) && (!$form_data{keywords})){ $form_data{product} = "New_Arrivals"; } &add_to_the_cart; exit; } elsif ($form_data{'modify_cart_button.x'} ne "") { &display_cart_contents; exit; } elsif ($form_data{'change_quantity_button.x'} ne "") { &output_modify_quantity_form; exit; } elsif ($form_data{'submit_change_quantity_button.x'} ne "") { &modify_quantity_of_items_in_cart; exit; } elsif ($form_data{'delete_item_button.x'} ne "") { &output_delete_item_form; exit; } elsif ($form_data{'submit_deletion_button.x'} ne "") { &delete_from_cart; exit; } elsif ($form_data{'order_form_button.x'} ne "") { &require_supporting_libraries (__FILE__, __LINE__, "$sc_order_lib_path"); &display_order_form; exit; } elsif ($form_data{'submit_order_form_button'} ne "") { &require_supporting_libraries (__FILE__, __LINE__, "$sc_order_lib_path"); &process_order_form; exit; } elsif (($page ne "" || $form_data{'search_request_button'} ne "" || $form_data{'continue_shopping_button'} || $are_any_query_fields_filled_in =~ /yes/i) && ($form_data{'return_to_frontpage_button'} eq "")) { &display_products_for_sale; exit; } # Authorize.net elsif ($form_data{'x_response_code'}) { # You can uncomment the section below and set the URL if # you want to validate the Authorize.net referring URL # #if ($ENV{'HTTP_REFERER'} ne 'https://secure.authorize.net/gateway/transact.dll') #{ #print "Refering Site Authentication Failed!"; #exit; #} $cart_id = $form_data{'x_cust_id'}; $sc_cart_path = "$sc_user_carts_directory_path/$cart_id.cart"; $sc_cart_path =~ /([\w\-\=\+\/]+)\.(\w+)/; $sc_cart_path = "$1.$2"; $sc_cart_path = "" if ($sc_cart_path eq "."); $sc_cart_path =~ s/^\/+//; # Get rid of any residual / prefix &processOrder; exit; } # iTransact elsif ($form_data{'authcode'}) { # You can uncomment the section below and set the URL if # you want to validate the iTransact referring URL # #if ($ENV{'HTTP_REFERER'} ne 'https://secure.itransact.com/cgi-bin/mas/split.cgi') #{ #print "Refering Site Authentication Failed!"; #exit; #} $cart_id = $form_data{'p5'}; $sc_cart_path = "$sc_user_carts_directory_path/$cart_id.cart"; $sc_cart_path =~ /([\w\-\=\+\/]+)\.(\w+)/; $sc_cart_path = "$1.$2"; $sc_cart_path = "" if ($sc_cart_path eq "."); $sc_cart_path =~ s/^\/+//; # Get rid of any residual / prefix &processOrder; exit; } # Linkpoint elsif ($form_data{'approval_code'}) { # You can uncomment the section below and set the URL if # you want to validate the Linkpoint referring URL # #if ($ENV{'HTTP_REFERER'} ne 'https://secure.linkpt.net/cgi-bin/hlppay') #{ #print "Refering Site Authentication Failed!"; #exit; #} $cart_id = $form_data{'custnum'}; $sc_cart_path = "$sc_user_carts_directory_path/$cart_id.cart"; $sc_cart_path =~ /([\w\-\=\+\/]+)\.(\w+)/; $sc_cart_path = "$1.$2"; $sc_cart_path = "" if ($sc_cart_path eq "."); $sc_cart_path =~ s/^\/+//; # Get rid of any residual / prefix &processOrder; exit; } # Offline elsif ($form_data{'process_order'}) { # You can uncomment the section below and set the URL if # you want to validate the referring URL # #if ($ENV{'HTTP_REFERER'} ne 'https://secure.your_domain.com/cgi-bin/store/commerce.cgi') #{ #print "Refering Site Authentication Failed!"; #exit; #} $cart_id = $form_data{'cart_id'}; $sc_cart_path = "$sc_user_carts_directory_path/$cart_id.cart"; $sc_cart_path =~ /([\w\-\=\+\/]+)\.(\w+)/; $sc_cart_path = "$1.$2"; $sc_cart_path = "" if ($sc_cart_path eq "."); $sc_cart_path =~ s/^\/+//; # Get rid of any residual / prefix &processOrder; exit; } ## to display html invoice elsif(($form_data{'show'} eq "html_invoice") && ($create_html_invoice eq "yes")){ #UT invoice $html_invoice_num = $form_data{'INVOICE'}; $html_invoice_num =~ /^([-\@\w\D]+)$/; $html_invoice_num = "$1"; $html_invoice_num =~ s/^\/+//; # Get rid of any residual / prefix &show_invoice; exit; } # To add functionality to Calendar elsif ($form_data{'action'} eq "showheader"){ &StoreHeader; exit; }elsif($form_data{'action'} eq "showfooter"){ &StoreFooter; exit; } elsif ($form_data{'action'} eq "calendar"){ my $caldisp; # Removed below - next line causing error and resource violation # use LWP::Simple; # Convert form data # my $linkadd = ''; # foreach my $key(keys %form_data){ # $linkadd .= "&$key=$form_data{$key}"; # } # $linkadd =~ s/^&//; # $linkadd = "?".$linkadd if($linkadd); # my $cjls = "http://www.wildaboutbeads.com/cgi-bin/calendar/calendar.cgi$linkadd"; # $cjls =~ s/ //g; # $caldisp = get("$cjls"); # if ($caldisp eq ''){ $caldisp = "
Calendar Currently Unavailable - Please Check Back Soon!"; # $caldisp .= "
$!
$cjls
"; # } &StoreHeader; print $caldisp; &StoreFooter; exit; } else { &output_frontpage; 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 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 ($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") { require "$require_file"; } # If not, the scripts sends back an error message that # will help the admin isolate the problem with the script. else { print "I am sorry but I was unable to require $require_file at line $line in $file. Would you please make sure that you have the path correct and that the permissions are set so that I have read access? Thank you."; 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 { &ReadParse(*form_data); } ####################################################################### # 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 { foreach $file_extension (@acceptable_file_extensions_to_display) { if ($page =~ /$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 # commerce.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 "$sc_page_load_security_warning"; &update_error_log("PAGE LOAD WARNING", __FILE__, __LINE__); exit; } } ####################################################################### # 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 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)); 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 commerce.setup.db, we'll delete the file. foreach $cart (@carts) { if (-M "$sc_user_carts_directory_path/$cart" > $sc_number_days_keep_old_carts) { $sc_cart_path = "$sc_user_carts_directory_path/$cart"; $sc_cart_path =~ /([\w\-\=\+\/]+)\.(\w+)/; $sc_cart_path = "$1.$2"; $sc_cart_path = "" if ($sc_cart_path eq "."); $sc_cart_path =~ s/^\/+//; # Get rid of any residual /prefix unlink("$sc_cart_path"); print "$sc_cart_path"; } }# 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__); 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. &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 commerce.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 { &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 { #print "
ADDING To CART

"; ## Added by Chris Costa, # if this is a multiple_add # we need to loop through all the items # if the checkbox is checked $form_data{check-item-$item} eq "add" # leave it alone, or else # we need to undefine the item before going to the add to cart routine if ($form_data{multiple_add} eq "yes"){ $sc_should_i_display_cart_after_purchase = "yes"; #$sc_item_ordered_message =''; @check_ordered = keys (%form_data); # Next it begins going through the list of items ordered # one by one. foreach $check_item (@check_ordered) { #if it's an item, check for the checkbox or undefine it if ($check_item =~ /^item-/i){ my $checkboxname = "check-$check_item"; unless($form_data{$checkboxname} eq "add"){ undef ($form_data{$check_item}); } # end unless ($form_data{$checkboxname} eq "add"){ } # end if ($check_item =~ /^item-/i){ } # end foreach $check_item } # end if multiple add #&checkReferrer; # 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 = pop (@row); $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) { ### Use this to disallow items: # check for disallowed categories &check_disallow; ### use this hack to add the items name to the selection added ### message. Also edit the message in commerce.setup.db if ($item =~ /^item-/i){ ###this is a product heading, split it and return the item name @cjn = split(/\|/ , $item); $cjprodname = $cjn[3]; #trollbeads list if ($cjn[1] =~ /^Trollbeads/i){ $sc_item_ordered_message = $sc_item_wishlist_message; $sc_item_ordered_message =~ s/%%catname%%/Trollbeads/ig; } unless($form_data{multiple_add} eq "yes"){ $sc_item_ordered_message =~ s/%%cjprodname%%/$cjprodname/ig; } else {# end unless multiple items $sc_item_ordered_message =~ s/\"%%cjprodname%%\"//ig; }# end unless multiple items } # 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 fo 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. # MD5 Check if (($item =~ /^item-/i) && (!$item =~ /^item-79/i)){ print "\n\n\n\n"; $CONTROL = &make_hash($item); if ($CONTROL ne $form_data{'CONTROL'}){ &bogus; exit; } } # end MD5 #original below 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){ $form_data{$item} =~ s/\"//ig; # Manual add item hack here $cjref = "79"; # the ref num in manager for the auction payments $cjoptprice = "2"; # the option number of the price in the option file if ($item eq "option|1|$cjref"){ $form_data{$item} =~ s/\$//ig; $form_data{$item} = "Item # "."$form_data{$item}"; } elsif ($item eq "option|$cjoptprice|$cjref"){ $form_data{$item} =~ s/\$//ig; $form_data{$item} = "Price|"."$form_data{$item}"; } # End make price hack #Manual price entry hack by Chris Costa elsif ($item =~ /^option\|price/i){ $form_data{$item} =~ s/\$//ig; $form_data{$item} =~ s/[a-zA-Z]//ig; $form_data{$item} = &format_price("$form_data{$item}"); $optchprice = $form_data{$item}; #$form_data{$item} = "Price|"."$form_data{$item}"; undef $form_data{$item}; } # end manual price hack # Manual Option Label Hack by Chris Costa else { # Check if [1] spot has words my (@cwiopt) = split (/\|/, $item); if ($cwiopt[1] =~ /\D/){ $cwiopt[1] =~ s/_/ /g; $form_data{$item} = "$cwiopt[1]- "."$form_data{$item}"; } } # Added by Chris Costa to prevent user forgetting to choose options # Variable $default_first_choice = "Please Choose..."; is set at top of this file if ($form_data{$item} =~ /^$default_first_choice/){ &bad_option_note; } # end if user forgot to choose and default first choice was left chosen push (@options, $item) if ($form_data{$item}); } # end if option # 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 # commerce_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) { # 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. #for adding multiple items, must add this next line undef (@option_array); undef (@option_array); $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 commerce.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 sscreen, it will read # "Red 0.00, and will not affect the cost of the item. ($option_name, $option_price) = split (/\|/,$form_data{$option}); if($option_name) { # added next line to sort options by number if ($option_number =~ /\D/){ $cwoptnum ++; $option_array[$cwoptnum] = "$option_name $option_price
"; }else{ $option_array[$option_number] = "$option_name $option_price
"; } #$options .= "$option_name $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 $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. # added to sort options foreach $option (@option_array){ $options .= $option; } $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"); foreach $field (@cart_row) { $cart_row .= "$field\|"; } $cart_row .= "$options\|$item_grand_total\|$item_number\n"; # End of foreach $item_ordered_with_options..... } # 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. if (-e "$sc_cart_path") { open (CART, ">>$sc_cart_path") || &file_open_error("$sc_cart_path", "Add to Shopping Cart", __FILE__, __LINE__); print CART "$cart_row"; close (CART); } else { open (CART, ">$sc_cart_path") || &file_open_error("$sc_cart_path", "Add to Shopping Cart", __FILE__, __LINE__); print CART "$cart_row"; close (CART); } # 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 commerce.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. 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; # add new subroutine here if you want to do something else after # adding to the cart } # elsif ($are_any_query_fields_filled_in =~ /yes/i) elsif (($are_any_query_fields_filled_in =~ /yes/i) || ($form_data{'keywords'})) { $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 commerce_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/)) { &update_error_log("BAD QUANTITY CHANGE", __FILE__, __LINE__); &bad_order_note("change_quantity_button"); } # 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 ($form_data{$item} gt 0) { # 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. $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. # Quantity break pricing hack by Chris Costa if ($sc_use_quantity_break_pricing eq "yes"){ $pri = &format_price($database_row[2]); $prio = &format_price($database_row[7]); $item_price_now = &check_quantity_break($pri,$form_data{$item},$database_row[0]); $item_price_now_formatted = &format_price($item_price_now); if ($database_row[2] ne $item_price_now){ if ($pri ne $prio){ $database_row[2] = $item_price_now; $database_row[7] = $database_row[7] - $pri + $item_price_now_formatted; } else { $database_row[2] =$item_price_now; $database_row[7] =$item_price_now_formatted; } } } # end quantity break hack foreach $field (@database_row) { #quantity break hack was here $shopper_row .= "$field\|"; } $quantity_modified = "yes"; chop $shopper_row; # Get rid of last pipe symbol but not the # newline character } else { $quantity_modified = "yes"; } # 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); &display_cart_contents; # End of if ($form_data{'submit_change_quantity'} ne "") } ####################################################################### # 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 commerce_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"; } # 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); &display_cart_contents; # End of if ($form_data{'submit_deletion'} ne "") } ####################################################################### # 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 { ## for search logging: if (($form_data{'keywords'} ne "")&&(!$form_data{'no_log'})){ open(SEARCH,"./data_files/search.log")||die "search 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{$b} <=> $phrase_array{$a}} keys(%phrase_array)} open(SEARCH,">./data_files/search.log"); foreach $key(@keys) { print SEARCH qq~$key|$phrase_array{$key}\n~; } close(SEARCH); } #end search logging # 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 ($form_data{'search_request_button'} ne "") { &standard_page_header("Search Results"); require "$sc_html_search_routines_library_path"; &html_search; &html_search_page_footer; 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); 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 "" && $form_data{'search_request_button'} eq "" && $form_data{'continue_shopping_button'} eq "") { &display_page("$sc_html_product_directory_path/$page", "Display Products for Sale", __FILE__, __LINE__); 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. # # Next the database is querried for rows containing the # value of the incoming product variable in the correct # category as defined in commerce.setup.db The script uses # the submit_query subroutine in commerce_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); if (($form_data{'pid'}) || (!$database_rows[1])){ my @cjdispname = split(/\|/,$database_rows[0]); my $cjdispname = $cjdispname[3]; $display_name_title = $cjdispname; } #above section and this switched by Chris Costa # now 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 # commerce_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"; } # 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 commerce.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. $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; ## could insert code right here to calculate the $warn_message for page index nav ## at top of page. ## this is before any products are printed to the screen, but after ## the header and after the require information of $status and $total_row_count ## are available. Store the page nav in a variable, print it here and then again ## before the footer Use an if statement to determine if it's even needed. ## problem -- uses $rowCount variable, which is incremented below ## Add page navigation to top of page by Chris Costa if ($status =~ /max.*row.*exceed.*/i){ $prevHits = $sc_db_max_rows_returned; $nextHits = $sc_db_max_rows_returned; $just_get_nav = "yes"; $rowCount = $rowCount + $#database_rows + 1; my ($page_index_nav) = &product_page_footer($status,$total_row_count); $rowCount = $rowCount - $#database_rows - 1; $just_get_nav = "no"; print qq~ $page_index_nav ~; } ## end page index nav to top hack # insert code to start table for new tile # do not shrink tile if there is only one returned item #commented below line to allow more than one result for pid search # if ((!$database_rows[1]) && (!$form_data{'pid'})){ if (!$database_rows[1]){ $sc_shrink_tile = "no"; } #commented below line to allow more than one result for pid search # if (($sc_shrink_tile eq "yes") && (!$form_data{'pid'})){ if ($sc_shrink_tile eq "yes"){ print ""; print ""; $tile_keep_count = "1"; } # end new tile code foreach $row (@database_rows) { # insert code to start table for new tile # if (($sc_shrink_tile eq "yes") && (!$form_data{'pid'})){ if ($sc_shrink_tile eq "yes"){ if ($rowCount > $minCount && $rowCount <= $maxCount){ if ($tile_keep_count == $tile_display_rows){ print "<\/td><\/tr>"; $tile_keep_count = "1"; } else { print "<\/td>"; $tile_keep_count++; } } } # end new tile code here $rowCount++; $prevHits = $sc_db_max_rows_returned; $nextHits = $sc_db_max_rows_returned; if ($rowCount > $minCount && $rowCount <= $maxCount) { @database_fields = split (/\|/, $row); foreach $field (@database_fields) { # 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%%/) { ($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. However, the # current product id number is substituted for the # %%PRODUCT_ID%% flag open (OPTION_FILE, "<$sc_options_directory_path/$option_location") || &file_open_error ("$sc_options_directory_path/$option_location", "Display Products for Sale", __FILE__,__LINE__); while () { s/%%PRODUCT_ID%%/$database_fields[$sc_db_index_of_product_id]/g; $field .= $_; } close (OPTION_FILE); # End of if ($field =~ /^%%OPTION%%/) } # End of foreach $field (@database_fields) } # Finally, the database fields (including the option field # which has been recreated) are stuffed into the format # string, $sc_product_display_row and the entire formatted # string is printed to the browser along with the footer. # # First, however, we must format the fields correctly. # Initially, @display_fields is created which contains the # values of every field to be displayed, including a # formatted price field. @display_fields = (); @temp_fields = @database_fields; foreach $display_index (@sc_db_index_for_display) { if ($display_index == $sc_db_index_of_price) { $temp_fields[$sc_db_index_of_price] = &display_price($temp_fields[$sc_db_index_of_price]); } push(@display_fields, $temp_fields[$display_index]); } # Then, the elements of the NAME field are created so that # customers will be able to specify an item to purchase. # We are careful to substitute double quote marks ("), and # greater and less than signs (>,<) for the tags ~qq~, # ~gt~, and ~lt~. The reason that this must be done is so # that any double quote, greater than, or less than # characters used in URL 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. # # /~gt~/g; $database_fields[$id_index] =~ s/\$topcat_html # ~; # } # end top categories for pid only $sc_product_display_row = &displayProductPage; # End of foreach $row (@database_rows) } } # code to close new tile #commented below line to allow more than one result for pid search # if (($sc_shrink_tile eq "yes") && (!$form_data{'pid'})){ if ($sc_shrink_tile eq "yes"){ print "<\/td><\/tr>"; } # end new tile code ################################## if ($form_data{'productbefore'}){ $form_data{'product'} = $form_data{'productbefore'}; } # fix for removal of new_arrivals category name to allow bottom page nav to work in that category ################################## &product_page_footer($status,$total_row_count); print < ENDOFTEXT 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 { # 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 # commerce_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. &standard_page_header("View/Modify"); &display_cart_table(""); &cart_footer; 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 { #####showcase grabber # &grab_showcase; ####end showcase grabber (subs at end of script) local ($page, $routine, $file, $line) = @_; # the subroutine begins by opening the requested file for # reading, exiting with file_open_error if there is a # problem as usual. my $newbies = &newbies; ## add random text & pics my $rotatead = &rotatead; if ($sc_header_for_pages eq "yes"){ &StoreHeader; } print "$clickthroughheader" if ($clickthroughheader); open (PAGE, "<$page") || &file_open_error("$page", "$routine", $file, $line); # It then reads in the file one line at a time. However, # on every line it looks for special tag sequences which # it knows it must modify in order to maintain the state # information necessary for the workings of this script. # Specifically, every form must include a page and a # cart_id value and every url hyperlink must have a # cart_id value added to it. # # Raw administratively pre-designed HTML pages must # include the follwoing tag lines if they are to filter # properly and pass along this necesary state information. # # All forms must include two hidden field lines with the # "tags" tobe substituted for imbedded as follows: # # # # # When the script reads in these lines, it will see the # tags "%%cart_id%%" and"%%page%%" and substitute them for # the actual page and cart_id values which came in as form # data. # # Similarly it might see the following URL reference: # # # # In this case, it will see the cartid= tag and # substitute in the correct and complete # "cartid=some_number". while () { # Check if we are pretending to display static pages: $_ = &checkstatic("$_"); s/%%showcase%%/$showcase/g; s/%%showcase2%%/$showcase2/g; s/%%showcase3%%/$showcase3/g; s/%%mainnotice%%/$mainnotice/g; s/%%cart_id%%/$cart_id/g; s/%%page%%/$form_data{'page'}/g; s/%%date%%/$date/g; s/%%URLofImages%%/$URL_of_images_directory/g; # display table of new products - sub newbie in html_lib.pl s/%%newbies%%/$newbies/g; # display rotating ad for items - sub rotatead in html_lib.pl s/%%rotatead%%/$rotatead/g; # Next, it checks 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
tag line. if ($form_data{'add_to_cart_button'} ne "" && $sc_shall_i_let_client_know_item_added eq "yes"){ if ($_ =~ />$sc_error_log_path") || &CgiDie ("The Error Log could not be opened"); # Now, the script adds to the log entry row, the values # associated with all of the HTTP environment variables # and prints the whole row to the log file which it then # closes and opens for use by other instances of this # script by removing the lock file. foreach $variable (@env_vars) { $log_entry .= "$ENV{$variable}\|"; } print ERROR_LOG "$log_entry\n"; close (ERROR_LOG); &release_file_lock("$sc_error_log_path.lockfile"); # End of if ($sc_shall_i_log_errors eq "yes") } # Next, the script checks to see if the admin has # instructed it to also send an email error notification # to the admin by setting the $sc_shall_i_email_if_error # in commerce.setup.db # # If so, it prepares an email with the same info contained # in the log file row and mails it to the admin using the # send_mail routine in mail-lib.pl. Note that a common # sourse of email errors lies in the admin not setting the # correct path for sendmail in mail-lib.pl on line 42. # Make sure that you set this variable there if you are # not receiving your mail and you are using the sendmail # version of the mail-lib package. if ($sc_shall_i_email_if_error eq "yes") { $email_body = "$type_of_error\n\n"; $email_body .= "FILE = $file_name\n"; $email_body .= "LINE = $line_number\n"; $email_body .= "DATE=$date\|"; foreach $variable (@env_vars) { $email_body .= "$variable = $ENV{$variable}\n"; } &send_mail("$sc_admin_email", "$sc_admin_email", "Web Store Error", "$email_body"); # End of if ($sc_shall_i_email_if_error eq "yes") } } ################################################################# # get_date Subroutine # ################################################################# # get_date is used to get the current date and time and # format it into a readable form. The subroutine takes no # arguments and is called with the following syntax: # # $date = &get_date; # # It will return the value of the current date, so you # must assign it to a variable in the calling routine if # you are going to use the value. sub get_date { # The subroutine begins by defining some local working # variables local ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst,$date); local (@days, @months); @days = ('Sunday','Monday','Tuesday','Wednesday','Thursday', 'Friday','Saturday'); @months = ('January','February','March','April','May','June','July', 'August','September','October','November','December'); # Next, it uses the localtime command to get the current # time, from the value returned by the time # command, splitting it into variables. ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); # Then the script formats the variables and assign them to # the final $date variable. Note that $sc_current_century # is defined in commerce.setup.db Since the 20th centruy # is really 1900-1999, we'll need to subtract 1 from this # value in order to format the year correctly. if ($hour < 10) { $hour = "0$hour"; } if ($min < 10) { $min = "0$min"; } if ($sec < 10) { $sec = "0$sec"; } $year += 1900; $date = "$days[$wday], $months[$mon] $mday, $year at $hour\:$min\:$sec"; return $date; } ################################################################# # display_price Subroutine # ################################################################# # display_price is used to format the price string so that # the store can take into account differing methods for # displaying prices. For example, some countries use # "$xxx.yyy". Others may use "xx.yy UNIT". This # subroutine will use the $sc_money_symbol_placement and # the $sc_money_symbol variables defined in # commerce.setup.db to format the entire price string for # display. The subroutine takes one argument, the price # to be formatted, and is called with the following # syntax: # # $price = &display_price(xx.yy); # # Where xx.yy is some number like 23.99. # # Note that the main routine calling this subroutine must # prepare a variable for the returned formatted price to # be assigned to. sub display_price { local ($price) = @_; local ($format_price); if ($sc_money_symbol_placement eq "front"){ ## Format price hack #This hack only displays the \$ symbol if there is a price for that item: if ($price > 0){ # Use this next line to display price regardless $format_price = "$sc_money_symbol $price"; }else{ $format_price = " "; } ## End hack } else { $format_price = "$price $sc_money_symbol"; } return $format_price; } ####################################################################### # get_file_lock # ####################################################################### # get_file_lock is a subroutine used to create a lockfile. # Lockfiles are used to make sure that no more than one # instance of the script can modify a file at one time. A # lock file is vital to the integrity of your data. # Imagine what would happen if two or three people # were using the same script to modify a shared file (like # the error log) and each accessed the file at the same # time. At best, the data entered by some of the users # would be lost. Worse, the conflicting demands could # possibly result in the corruption of the file. # # Thus, it is crucial to provide a way to monitor and # control access to the file. This is the goal of the # lock file routines. When an instance of this script # tries to access a shared file, it must first check for # the existence of a lock file by using the file lock # checks in get_file_lock. # # If get_file_lock determines that there is an existing # lock file, it instructs the instance that called it to # wait until the lock file disappears. The script then # waits and checks back after some time interval. If the # lock file still remains, it continues to wait until some # point at which the admin has given it permissios to just # overwrite the file because some other error must have # occurred. # # If, on the other hand, the lock file has dissappeared, # the script asks get_file_lock to create a new lock file # and then goes ahead and edits the file. # # The subroutine takes one argumnet, the name to use for # the lock file and is called with the following syntax: # # &get_file_lock("file.name"); sub get_file_lock { local ($lock_file) = @_; local ($endtime); $endtime = 20; $endtime = time + $endtime; # We set endtime to wait 20 seconds. If the lockfile has # not been removed by then, there must be some other # problem with the file system. Perhaps an instance of # the script crashed and never could delete the lock file. while (-e $lock_file && time < $endtime) { sleep(1); } open(LOCK_FILE, ">$lock_file") || &CgiDie ("I could not open the lockfile - check your permission settings"); # Note: If flock is available on your system, feel free to # use it. flock is an even safer method of locking your # file because it locks it at the system level. The above # routine is "pretty good" and it will server for most # systems. But if youare lucky enough to have a server # with flock routines built in, go ahead and uncomment # the next line and comment the one above. # flock(LOCK_FILE, 2); # 2 exclusively locks the file } ####################################################################### # release_file_lock # ####################################################################### # release_file_lock is the partner of get_file_lock. When # an instance of this script is done using the file it # needs to manipulate, it calls release_file_lock to # delete the lock file that it put in place so that other # instances of the script can get to the shared file. It # takes one argument, the name of the lock file, and is # called with the following syntax: # # &release_file_lock("file.name"); sub release_file_lock { local ($lock_file) = @_; # flock(LOCK_FILE, 8); # 8 unlocks the file # As we mentioned in the discussion of get_file_lock, # flock is a superior file locking system. If your system # has it, go ahead and use it instead of the hand rolled # version here. Uncomment the above line and comment the # two that follow. close(LOCK_FILE); unlink($lock_file); } ####################################################################### # format_price # ####################################################################### # format_price is used to format prices to two decimal # places. It takes one argumnet, the price to be formatted # and is called with the following syntax: # # $price =&format_price(xxx.yyyyy); # # Notice that the main calling routine must assign the # returned formatted price to some variable for its own # use. # # Also notice that this routine takes a value even if it # is longer than two decimal places and formats it with # rounding. Thus, you can utilize price calculations such # as 12.99 * 7.985 (where 7.985 might be some tax value. sub format_price { # The incoming price is set to a local variables and a few # wroking local variables are defined. local ($unformatted_price) = @_; local ($formatted_price); # The script then uses the rounding method in EXCEL. If # the 3rd decimal place is > 4, then we round the 2nd # decimal place up 1. Otherwise, we leave the number # alone. Notice that we will use the substr function to # pull off the last value in the three decimal place # number and compare it using the EXCEL logic. # # Basically, the routine uses the rounding rules of # sprintf. # The unformatted_price is rounded to # to two decimal places and returned to the calling # routine. $formatted_price = sprintf ("%.2f", $unformatted_price); return $formatted_price; } ############################################################ # # subroutine: format_text_field # Usage: # $formatted_value = # &format_text_field($value, [$width]); # # Parameters: # $value = text value to format. # $width = optional field width. Defaults to 25. # # This routine takes the value and appends enough # spaces so that the field width is 25 spaces. # in order to justify the fields that are stored # eventually in the $text_of_cart. # # Output: # The formatted value # ############################################################ sub format_text_field { local($value, $width) = @_; $width = 25 if (!$width); # Very simple. We return the value in # $value plus a string of 25 spaces which # has been truncated by the length of # the $value string. # # This results in a left justified # field of width = 25. # return ($value . (" " x ($width - length($value)))); #End of format_text_field } ############################################################ sub checkReferrer { # no longer used: return; # DEFINE THE ERROR IF REFERRER IS BLOCKED $referrer_error = qq~

ERROR - Referrer variable not present!

You may be behind a firewall or filter.
If you are accessing this store from work, try from home.
-or-
Try temporarily disabling your firewall or lowering your browser security settings while shopping.

Why is this happening?
The security features of this store require some information for validation.  One of those pieces of information is called an environmental variable - the "referrer" variable.  This tells our program where information came from.  We check this information to ensure that your form post comes from where we think it should (our web site, not someplace else).  Some firewalls or filters may block these environmental variables from passing, thus not allowing our site to ensure the information is from a reliable, accurate source.  You may temporarily disable your firewall to check out, or check the documentation for your software. 

In Norton Personal Firewall, for instance, you can go to settings, Privacy Control > Custom Level and uncheck "Enable Browser Privacy".  The Norton documentation defines "Enable Browser Privacy as - "Prevents a Web site from retrieving your email address or the address of the last Web site visited."  If we cannot determine the last web site you were visiting was our web site, we cannot be sure that the information you are passing during shopping came from our own site and contains accurate information for your order.  Without this validation our process cannot continue.  To alleviate this problem you can adjust your firewall settings to allow browser referrer info, doing so will not pose a security threat to your system.  Please email us with any questions about this process.

More information may be found on the website for the company that produces your firewall or filter, please consult their support pages and search for information on "how to pass referrer information" or "how to pass referer information" (spelled both ways across the web).

Click Here to return to the main page.

~; # BEGIN REFERRING SITE VALIDATION local ($referringDomain, $acceptedDomain); $referringDomain = $ENV{'HTTP_REFERER'}; $acceptedDomain = $sc_domain_name_for_cookie; $referringDomain =~ s/\?.*//g; $referringDomain =~ s/http:\/\///g; $referringDomain =~ s/\/.*//g; $referringDomain =~ s/\/commerce.cgi//g; if ($referringDomain =~ "^w*\.") { $referringDomain =~ s/^w*\.//i; } if ($acceptedDomain =~ "^w*\.") { $acceptedDomain =~ s/^w*\.//i; } if ($referringDomain ne $acceptedDomain) { if ($referringDomain eq ""){ print $referrer_error; exit; } print "$acceptedDomain is the accepted referrer.
"; print "$referringDomain is not a valid referrer
"; print "Refering Site Authentication Failed!"; exit; } # END REFERRING SITE VALIDATION } # end sub checkref ############################################################ # MD5 Hash Creation ############################################################ sub make_hash{ my @secrets = @_; my $secret = "mizzxxxthisthangupbabyboyzz"; my $addmess = "thisthangizzevenharderbaby"; my $secretpart = ""; foreach $secretpart (@secrets){ $secret .= $secretpart; } my $md5 = new MD5; my $hash = MD5->hexhash(MD5->hexhash ($secret), $addmess); return $hash; } #################################### # Error Message for not passing MD5 Validation #################################### sub bogus{ my $bogus_error = qq~

ERROR - Data Contamination!

Why is this happening?
The security features of this store require some information for validation.  Pieces of information from each process are combined and encrypted.  When you add, delete, modify, or checkout items in your cart we validate this information.  It appears that some information in your last process was tampered with.

What can I do?
If you did not tamper with the information in this shopping cart (often called hacking), and you believe this to be an error, you may attempt to use the shopping links and try again.  

If this does not solve the problem, please send us an email and let us know what happened.  The administrators of the site have already been sent a notice that this error occurred.  We will promptly look into the matter and truly appreciate feedback on the performance of this web site.  Providing you with a safe and enjoyable shopping experience is our priority.  Please reference our contact information and feel free to give us a call.  We may be able to help you recover your order and continue.

Please note
If you are trying to hack this store, your IP and other relevant information have just been recorded.  We prosecute to the fullest extent and tougher and tougher hacking laws are being implemented around the world.

Click Here to return to the main page or use the page links to continue shopping.

~; my $ertime = scalar(localtime); my $text_of_email = qq~ NOTE TO ADMIN - MD5 ERROR RECEIVED IN ONLINE STORE Time of error: $ertime Cart ID: $cart_id ENV variables: ~; foreach my $en (keys %ENV){ $text_of_email .= "$en - $ENV{$en}\n"; } &send_mail($sc_admin_email, $sc_admin_email, "MD5 ERROR IN ONLINE STORE - NOTIFY ADMIN", "$text_of_email"); &StoreHeader; print $bogus_error; &StoreFooter; exit; } # end sub bogus