#!/usr/bin/perl -T ## ## Agora.cgi 4.0 $versions{'agora.cgi'} = "4.0" . $ENV{"AGORAWRAP"}; $versions{'perl'} = "$]"; $versions{'OSNAME'} = "$^O"; $versions{'server'} = $ENV{'SERVER_SOFTWARE'} if $ENV{'SERVER_SOFTWARE'}; #$versions{'cookie'} = $ENV{'HTTP_COOKIE'} if $ENV{'HTTP_COOKIE'}; #print "Content-type: text/html;\n\n"; ## ## Version history is available at... ## http://www.agoracgi.com/ ## ## Agora.cgi is based on Selena Sol's freeware 'Web Store' ## available at http://www.extropia.com with many modifications ## made independently by Carey Internet Services before splitting ## off and becoming this package known as agora.cgi. The package ## distributed here is Copyright 1999-2000 by Steven P. Kneizys of ## Agoracgi.com 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. ## ## There are several add-on modest-cost modules planned, see the ## http://www.agoracgi.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{"AGORAWRAP"}="*"; print `./wrap_agc.o`; &call_exit; } $time = time; $main_program_running = "yes"; push(@INC,"./library/additions"); &require_supporting_libraries (__FILE__, __LINE__, "./admin_files/agora_user_lib.pl", "./library/cgi-lib.pl"); &read_and_parse_form_data; &require_supporting_libraries (__FILE__, __LINE__, "./library/agora.setup.db"); &codehook("after_loading_setup_db"); &require_supporting_libraries (__FILE__, __LINE__, "./admin_files/$sc_gateway_name-user_lib.pl", "$sc_html_setup_file_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 = sort(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"; $search_request = ""; if (!(-f "$sc_html_product_directory_path/$page")){ $page = ""; $form_data{'product'} = "."; # show everything $are_any_query_fields_filled_in = "yes"; } else { $form_data{'page'} = $page; } } &codehook("special_navigation"); if (&form_check('display_cart')) { &load_order_lib; &display_cart_contents; &call_exit; } if (&form_check('add_to_cart_button')) { &load_order_lib; &add_to_the_cart; &call_exit; } elsif (&form_check('modify_cart_button')) { &load_order_lib; &display_cart_contents; &call_exit; } elsif (&form_check('change_quantity_button')) { &load_order_lib; &output_modify_quantity_form; &call_exit; } elsif (&form_check('submit_change_quantity_button')) { &load_order_lib; &modify_quantity_of_items_in_cart; &call_exit; } elsif (&form_check('delete_item_button')) { &load_order_lib; &output_delete_item_form; &call_exit; } elsif (&form_check('submit_deletion_button')) { &load_order_lib; &delete_from_cart; &call_exit; } elsif (&form_check('order_form_button')) { &load_order_lib; &display_order_form; &call_exit; } elsif (&form_check('clear_order_form_button')) { &load_order_lib; &clear_verify_file; &codehook("display_cleared_order_form"); &display_order_form; &call_exit; } elsif (&form_check('submit_order_form_button')) { &load_order_lib; if ($sc_test_repeat) { &display_order_form; } else { &process_order_form; } &call_exit; } elsif (($page ne "" || $search_request ne "" || &form_check('continue_shopping_button') || $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"); $sc_processing_order="no"; # If we got here, then just output the front page &output_frontpage; &call_exit; ######################################################################### # load the order library # ######################################################################### sub load_order_lib{ &require_supporting_libraries ( __FILE__, __LINE__, "$sc_order_lib_path"); } ######################################################################### # check if a form_data button has been selected # ######################################################################### sub form_check { if ($SUB_form_check eq '') { $ZCODE=q` $SUB_form_check = '1'; sub SUB_form_check { local ($name) = @_; local ($name2) = $name . ".x"; if (($form_data{$name} ne "") || ($form_data{$name2} ne "")) { return 1; } else { return ""; } } `;eval($ZCODE);if ($@ ne ''){&zcode_error($ZCODE,$@,__FILE__,__LINE__);} } &SUB_form_check; } ####################################################################### # 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 { if ($SUB_serve_picture eq '') { $ZCODE=q` $SUB_serve_picture = '1'; sub 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 agora # 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); } } } `;eval($ZCODE);if ($@ ne ''){&zcode_error($ZCODE,$@,__FILE__,__LINE__);} } &SUB_serve_picture; } ####################################################################### # 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"; foreach $junk (sort(keys(%versions))) { print "\n"; } print "
$junk $versions{$junk}
\n"; $junk .= `grep -h "versions{'" ./custom/* |grep "}="`; $junk .= `grep -h "versions{'" ./custom/* |grep "} ="`; $junk .= `grep -h "versions{'" ./protected/* |grep "}="`; $junk .= `grep -h "versions{'" ./protected/* |grep "} ="`; $junk .= `grep -h "versions{'" ./protected/custom/* |grep "}="`; $junk .= `grep -h "versions{'" ./protected/custom/* |grep "} ="`; $junk .= `grep -h "versions{'" ./library/* |grep "}="`; $junk .= `grep -h "versions{'" ./library/* |grep "} ="`; $junk =~s/\n/ /g; $junk =~ /([\w\-\=\+\/\;\{\}\'\ \.\"\$]+)/; $junk = $1; while ($junk ne "") { $result = $lib; ($junk1,$key,$junk) = split(/\'/,$junk,3); ($junk1,$val,$junk) = split(/\"/,$junk,3); if ($versions{$key} eq "") { $versions{$key} = $val; } ($junk1,$junk) = split(/versions/,$junk,2); } # if ($@ eq "") { print "

info and Versions of loaded and unloaded libraries:
\n"; print "\n"; foreach $junk (sort(keys(%versions))) { print "\n"; } print "
$junk $versions{$junk}
\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 "" ) && ($form_data{'relay'} 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{'ofn'})) { $form_data{'order_form'} = $form_data{'ofn'}; } 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 # agora.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{ if ($SUB_parse_options_to_verify eq '') { $ZCODE=q` $SUB_parse_options_to_verify = '1'; sub 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; } `;eval($ZCODE);if ($@ ne ''){&zcode_error($ZCODE,$@,__FILE__,__LINE__);} } &SUB_parse_options_to_verify; } ####################################################################### sub option_prep { if ($SUB_option_prep eq '') { $ZCODE=q` $SUB_option_prep = '1'; sub SUB_option_prep { local ($field,$option_location,$product_id)= @_; local ($very_first_part,$junk); $field = &agorascript($field,"optpre","$option_location",__FILE__,__LINE__); $field =~ s/%%PRODUCT_ID%%/$product_id/g; $field =~ s/%%PRODUCTID%%/$product_id/g; $field = &agorascript($field,"optpost","$option_location",__FILE__,__LINE__); # DELUXE feature ... take only the part between

--cut here--

# tokens # if ($chop =~ /yes/i) { ($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)"; } # } return $field; } `;eval($ZCODE);if ($@ ne ''){&zcode_error($ZCODE,$@,__FILE__,__LINE__);} } &SUB_option_prep; } ####################################################################### # 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 agora.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") { &log_access_to_store; } # 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; &codehook("assign-cart_id-modifier"); $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 == 4) { 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; &codehook("assign-cart_id-modifier"); $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; } ####################################################################### # Log Access to Store # ####################################################################### sub log_access_to_store { $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"`; # time #$shortdate = `date +"%D %T"`; # date and time $shortdate = &get_date_short; chomp ($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"); } ####################################################################### # 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 agora.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__); } ############################################################ sub finish_add_to_the_cart { &codehook("finish_add_to_the_cart"); if (($sc_use_html_product_pages eq "yes") || (($sc_use_html_product_pages eq "maybe") && ($page ne ""))) { 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; } } } ####################################################################### # 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 { # 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") || (($sc_use_html_product_pages eq "maybe") && ($page ne ""))) { if (($search_request ne "") && ($sc_use_html_product_pages eq "yes")){ &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 { local ($body_html,$prod_message,$status,$total_row_count); # First thing, need to check 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 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. # #if ($page ne "" && $search_request eq "" && # $form_data{'continue_shopping_button'} eq "") if (($page ne "" ) && (!($sc_use_html_product_pages eq "no"))) { &display_page("$sc_html_product_directory_path/$form_data{'page'}", "Display Products for Sale", __FILE__, __LINE__); &call_exit; } # 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 # agora_html_lib.pl and $sc_product_display_title is # defined in the setup file. ($body_html,$prod_message,$status,$total_row_count) = &create_html_page_from_db_body; &product_page_header($sc_product_display_title,$prod_message); print $body_html; &product_page_footer($prod_message); &call_exit; } ####################################################################### sub create_html_page_from_db_body { # First, the script defines a few working variables which # will remain local to this subroutine. local ($my_output,$prod_message); 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 database is querried for rows containing the # value of the incoming product variable in the correct # category as defined in agora.setup.db The script uses # the submit_query subroutine in agora_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 agora.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); if ($form_data{'add_to_cart_button.x'} ne "" && $sc_shall_i_let_client_know_item_added eq "yes") { $my_output .= "$sc_item_ordered_message"; } $last_product_displayed = "no"; # Agora 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, $junk) = split (/%%/, $field, 4); $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 agorascript is run { open (XX_FILE, "$sc_options_directory_path/$option_location") || &file_open_error ("$sc_options_directory_path/$option_location", "Display Products for Sale", __FILE__,__LINE__); local $/=undef; $field=; close (XX_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"); local $/=undef; $field=; close (OPTION_FILE); } $field = &agorascript($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 = &agorascript($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); $my_output .= &prep_displayProductPage(&get_sc_ppinc_info); # End of foreach $row (@database_rows) } } return ($my_output,$prod_message,$status,$total_row_count); } ####################################################################### # 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
tag line. if (($form_data{'add_to_cart_button'} ne "") && ($sc_allow_sneak_in_message =~ /yes/i) && ($sc_shall_i_let_client_know_item_added =~ /yes/i)) { if ($_ =~ / # # # 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". if (($form_data{'add_to_cart_button'} ne "" )&& ($sc_shall_i_let_client_know_item_added =~ /yes/i)) { $item_ordered_message = $sc_item_ordered_msg_token; } $the_file = &agorascript($the_file,"pre","$page",__FILE__,__LINE__); $the_file =~ s/%%item_ordered_msg%%/$item_ordered_message/ig; $the_file =~ s/%%CartID%%/%%cart_id%%/g;# must prevent double substitution $the_file =~ s/%%cartID%%/%%cart_id%%/ig; $the_file =~ s/cart_id=%%cart_id%%/cart_id=/ig; $the_file =~ s/cart_id=/cart_id=$cart_id_for_html/ig; $the_file =~ s/%%cart_id%%/$cart_id_for_html/ig; $the_file =~ s/%%page%%/$form_data{'page'}/ig; $the_file =~ s/%%date%%/$date/ig; $the_file =~ s/%%agoracgi_ver%%/$versions{'agora.cgi'}/ig; $the_file =~ s/%%URLofImages%%/$URL_of_images_directory/ig; $the_file =~ s/%%scriptURL%%/$sc_main_script_url/ig; $the_file =~ s/%%sc_order_script_url%%/$sc_order_script_url/ig; $the_file =~ s/%%StepOneURL%%/$sc_stepone_order_script_url/ig; $the_file =~ s/%%href_fields%%/$href_fields/ig; $the_file =~ s/%%make_hidden_fields%%/$hidden_fields/ig; $the_file =~ s/%%ppinc%%/$form_data{'ppinc'}/ig; $the_file =~ s/%%maxp%%/$form_data{'maxp'}/ig; $the_file =~ s/%%product%%/$form_data{'product'}/ig; $the_file =~ s/%%p_id%%/$form_data{'p_id'}/ig; $the_file =~ s/%%keywords%%/$keywords/ig; $the_file =~ s/%%next%%/$form_data{'next'}/ig; $the_file =~ s/%%exact_match%%/$form_data{'exact_match'}/ig; $the_file =~ s/%%exact_case%%/$form_data{'exact_case'}/ig; while ($the_file =~ /(%%eval)([^%]+)(%%)/i) { $arg = $2; $myans = eval($arg); if ($@ ne ""){ $myans = "%% Eval Error on: $arg %%";} $the_file =~ s/(%%eval)([^%]+)(%%)/$myans/i; } while ($the_file =~ /%%ZZZ%%/) { $cart_id_for_html = &cart_id_for_html; $the_file =~ s/%%ZZZ%%/$cart_id_for_html/; } $the_file = &agorascript($the_file,"post","$page",__FILE__,__LINE__); $the_file = &agorascript($the_file,"","$page",__FILE__,__LINE__); # Very Last thing, load headers and footers # These routines already have substitutions, agorascript, etc, and # are stand-alones, so do not need to make any additional changes to them while ($the_file =~ /%%StoreHeader%%/i) { $my_text = &GetStoreHeader; $the_file =~ s/%%StoreHeader%%/$my_text/i; } while ($the_file =~ /%%StoreFooter%%/i) { $my_text = &GetStoreFooter; $the_file =~ s/%%StoreFooter%%/$my_text/i; } return $the_file; } `;eval($ZCODE);if ($@ ne ''){&zcode_error($ZCODE,$@,__FILE__,__LINE__);} } &SUB_script_and_substitute; } ################################################################# # update_error_log Subroutine # ################################################################# # update_error_log is used to append to the error log if # there has been a process executing this script and/or # email the admin. # # The subroutine takes three arguments, the type of error, # the current filename and current line number and is # called with the following syntax: # # &update_error_log("WARNING", __FILE__, __LINE__); sub update_error_log { if ($SUB_update_error_log eq '') { eval q` $SUB_update_error_log = '1'; sub SUB_update_error_log{ # The subroutine begins by assigning the incoming # argumnets to local variables and defining some other # local variables to use during its work. # # $type_of_error will be a text string explaining what # kind of error is being logged. # # $file_name is the current filename of this script. # # $line_number is the line number on which the error # occurred. Note that it is essential that the line # number, stored in __LINE__ be passed through all levels # of subroutines so that the line number value will truly # represent the line number of the error and not the # line number of some subroutine for error handling. local ($type_of_error, $file_name, $line_number) = @_; local ($log_entry, $email_body, $variable, @env_vars); # The list of the HTTP environment variables are culled # into the @env_vars list array and get_date is used to # assign the current date to $date @env_vars = keys(%ENV); $date = &get_date; # Now, if the admin has instructed the script to log # errors by setting $sc_shall_i_log_errors in # agora.setup.db, the script will create an error log # entry. if ($sc_debug_mode eq "yes") { if ($sc_header_printed ne 1) { if ($sc_browser_header eq "") { $sc_browser_header = "Content/type: text/html;\n\n"; } print $sc_browser_header; } local($browser_text) = $type_of_error; $browser_text =~ s/\|/\\n/g; print '
' . "\n
";
print "ERROR:$browser_text
", "FILE: $file_name
", "LINE: $line_number
\n"; print '
' . "\n"; } if ($sc_shall_i_log_errors eq "yes") { # First, the new log entry row is created as a pipe # delimited list beginning with the error type, filename, # line number and current date. $log_entry = "$type_of_error\|FILE=$file_name\|LINE=$line_number\|"; $log_entry .= "DATE=$date\|"; # Then the error log file is opened securely by using the # lock file routines in get_file_lock discussed later. &get_file_lock("$sc_error_log_path.lockfile"); open (ERROR_LOG, ">>$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 agora.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") } } `; if ($@ ne '') { print "Content-type: text/html;\n\nBAD update_error_log routine!\n"; } } &SUB_update_error_log; } ################################################################# # 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 { local (@days, @months); local ($connector) = ' at '; @days = ('Sunday','Monday','Tuesday','Wednesday','Thursday', 'Friday', 'Saturday'); @months = ('January','February','March','April','May','June','July', 'August','September','October','November','December'); return &get_date_engine; } sub get_date_short { local (@days, @months); local ($connector) = ' '; @days = ('Sun','Mon','Tue','Wed','Thu', 'Fri', 'Sat'); @months = ('Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'); return &get_date_engine; } sub get_date_engine { # The subroutine begins by defining some local working # variables local ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst,$date); # 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. 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" . $connector . "$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 # agora.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); # set to 2 decimal places ... SPK 1/26/2000 $price = &format_price($price); if ($sc_money_symbol_placement eq "front") { $format_price = "$sc_money_symbol$sc_money_symbol_spaces$price"; } else { $format_price = "$price$sc_money_symbol_spaces$sc_money_symbol"; } return $format_price; } sub display_price_nospaces { local ($price) = @_; local ($holdme) = $sc_money_symbol_spaces; $sc_money_symbol_spaces=''; $price = &display_price($price); $sc_money_symbol_spaces = $holdme; return $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); local ($exit_get_file_lock)=""; &codehook("get_file_lock"); if ($exit_get_file_lock ne "") {return;} $endtime = 55; # was 20 originally $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 ($lock_file)"); # 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) = @_; local ($exit_release_file_lock)=""; &codehook("release_file_lock"); if ($exit_release_file_lock ne "") {return;} # 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 SetCookies { local(@test); $cookie{'cart_id'} = "$cart_id"; # Set the domain to be correct for your domain $domain = $sc_domain_name_for_cookie; # now, if there is only a two-parter domain name, add a leading period. @test = split(/\./,$domain); #if ($test[2] eq '') { $domain = '.' . $domain;} $secureDomain = $sc_secure_domain_name_for_cookie; @test = split(/\./,$secureDomain); #if ($test[2] eq '') { $secureDomain = '.' . $secureDomain;} # The path to your 'store' directory $path = $sc_path_for_cookie; $securePath = $sc_secure_path_for_cookie; # Leave this as is. $secure = ""; # Cookie will expire in 24 hours times the number of cookie days $now = time; # Second in twenty four hours $twenty_four_hours = "86400"; $cookie_hours = $sc_cookie_days * $twenty_four_hours; $expiration = $now+$cookie_hours;#number of days until cookie expires &codehook("about_to_set_cookie"); if(!$form_data{'secure'}){ &set_cookie($expiration,$domain,$path,$secure); } else { &set_cookie($expiration,$secureDomain,$securePath,$secure); } } ############################################################ sub checkReferrer { # BEGIN REFERRING SITE VALIDATION and REPEATED PAGE LOADING local ($referringDomain, $acceptedDomain); local ($test_repeat,$raw_text); $referringDomain = $ENV{'HTTP_REFERER'}; $acceptedDomain = $sc_domain_name_for_cookie; $referringDomain =~ s/\?.*//g; $referringDomain =~ s/http:\/\///g; $referringDomain =~ s/https:\/\///g; $referringDomain =~ s/\/.*//g; $referringDomain =~ s/\/agora.cgi//g; if ($referringDomain =~ "^w*\.") { $referringDomain =~ s/^w*\.//i; } if ($acceptedDomain =~ "^w*\.") { $acceptedDomain =~ s/^w*\.//i; } $test_repeat = 0; if ($sc_test_for_store_cart_change_repeats) { $test_repeat = $sc_test_repeat; } if (($referringDomain ne $acceptedDomain) || ($test_repeat)) { if ($test_repeat) { if ($sc_repeat_fake_it =~ /yes/i) { &repeat_fake_it; } else { $special_message = $messages{'chkref_01'}; &display_cart_contents; } } elsif ($cart_id == $cookie{'cart_id'}) { # okay to show the stuff ... $special_message = $messages{'chkref_02'}; &display_cart_contents; } else { print "$acceptedDomain is the accepted referrer.
"; print "$referringDomain is not a valid referrer
"; print $messages{'chkref_03'}; } &call_exit; } # END REFERRING SITE VALIDATION } ############################################################ sub repeat_fake_it { if ($form_data{'add_to_cart_button.x'} ne "") { &finish_add_to_the_cart; &call_exit; } elsif ($form_data{'submit_change_quantity_button.x'} ne "") { &finish_modify_quantity_of_items_in_cart; &call_exit; } elsif ($form_data{'submit_deletion_button.x'} ne "") { &finish_delete_from_cart; &call_exit; } else { $special_message = $messages{'chkref_01'}; &display_cart_contents; } } ############################################################ sub set_sc_cart_path { local($raw_text)=""; # untaint cart_id ... plus set the original form data variable just in # case somebody mistakenly uses it later $cart_id =~ /([\w\-\=\+\/]+)\.(\w+)/; $cart_id = "$1.$2"; $form_data{'cart_id'} = $cart_id; # have already untainted $cart_id, this should be all we need to do $base = "$sc_user_carts_directory_path/"; $sc_cart_path = "$base${cart_id}_cart"; $sc_capture_path = "$base${cart_id}_CAPTURE"; $sc_verify_order_path = "$base${cart_id}_VERIFY"; $sc_trans_rec_path = "$base${cart_id}_TRANSLOG"; $cart_id_for_html = "$cart_id*" . &make_random_chars; # deprecated! &check_cart_expiry; $sc_test_repeat = 0; if (-f $sc_trans_rec_path) { open(TRANS_FILE, "<$sc_trans_rec_path") || &file_open_error( "$sc_trans_rec_path", "Reading TRANS LOG", __FILE__,__LINE__); local $/=undef; $raw_text = ; close(TRANS_FILE); } else { $raw_text=""; } if (!($raw_text =~ /$sc_unique_cart_modifier/)){ open (TRANS_FILE, ">>$sc_trans_rec_path") || &file_open_error( "$sc_trans_rec_path", "Trans Repeat Write", __FILE__,__LINE__); if ($sc_unique_cart_modifier ne "") { print TRANS_FILE "$sc_unique_cart_modifier\n"; } close(TRANS_FILE); } else { $sc_test_repeat = 1; } &codehook("set_sc_cart_path_bot"); return; } ####################################################################### sub cart_id_for_html{ # nice and simple ... append random chars as tag return "$cart_id*" . &make_random_chars; } ####################################################################### sub zcode_error { local ($ZCODE,$at,$file,$line)=@_; local ($xx)="-" x 60; $ZCODE =~ s/\n/\|/g; $at =~ s/\n/\|/g; &update_error_log("zcode compilation error: |$at|$ZCODE|$xx", $file,$line); &call_exit; } ####################################################################### # For running codehooks at various places ####################################################################### sub codehook{ local($hookname)=@_; local($codehook,$err_code,@hooklist); if ($codehooks{$hookname} ne "") { @hooklist = split(/\|/,$codehooks{$hookname}); foreach $codehook (@hooklist) { eval("&$codehook;"); $err_code = $@; if ($err_code ne "") { #script died, error of some kind &update_error_log("code-hook $hookname $codehook $err_code","",""); } } } } ####################################################################### # For adding codehooks to the list for later execution ####################################################################### sub add_codehook{ local($hookname,$sub_name)=@_; local($codehook,$err_code,@hooklist); if ($sub_name eq "") { return;} @hooklist = split(/\|/,$codehooks{$hookname}); foreach $codehook (@hooklist) { if ($codehook eq $sub_name) { # already on the list, no need to add return; } } if ($codehooks{$hookname} eq "") { $codehooks{$hookname} = $sub_name; } else { $codehooks{$hookname} .= "|" . $sub_name; } } ####################################################################### sub replace_codehook{# replace ALL hooks with the value provided local($hookname,$sub_name)=@_; $codehooks{$hookname} = $sub_name; } ####################################################################### sub my_die { local ($msg) = @_; if ($sc_in_throes_of_death eq "yes") {die $msg;} $sc_in_throes_of_death="yes"; &call_exit; die $msg; } ####################################################################### # For cleanup purposes such as closing files, removing locks, etc. ####################################################################### sub call_exit { codehook("cleanup_before_exit"); if ($sc_in_throes_of_death ne "yes") { exit; } } # End of agora.cgi