#!/usr/bin/perl #--------------- # # FreeCheck - a free check printing application released # under the GNU General Public Licene. # # Copyright (C) 2000 Eric Sandeen (sandeen-freecheck@sandeen.net) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # 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. # #--------------- ########################################### # WARNING WARNING WARNING WARNING WARNING # ########################################### # THIS CODE EXECUTES AT LEAST ONE EXTERNAL PROGRAM, BASED ON # STRINGS PASSED IN FROM THE FORM. A FEEBLE EFFORT HAS BEEN # MADE TO SANITIZE THOSE STRINGS, BUT THERE COULD STILL BE # A SECURITY RISK HERE. YOU HAVE BEEN WARNED ########################################## # This script will generate a form that allows users to fill out information to # be printed on checks, and they get back either a PostScript or a PDF document. # Currently, the freecheck script and config files need to be in the same dir as # the CGI. If you want to generate PDFs, you need GhostScript. You also really # need the 6.x series, or the PDFs will look horrible, and checks printed almost # certainly will not be machine readable. # The freecheck executable script, and the freecheck config file # (freecheck.cfg) should be in the same dir as this script. use CGI qw(:standard); # The path to the GhostScript executable, with escaped "/"s $GS = "\/usr\/bin\/gs"; # Parameters to GhostScript to generate PDFs (trailing "-" means STDIN $PDFOptions = "-q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=- -"; # Get the cookie to set the defaults, if it's there... %pairs_hash = cookie('FreeCheck'); # If it's not there, set minimum defaults so the main script won't bonk if (!%pairs_hash) { %pairs_hash = ( "NumPages", "1", "PrintCheckBody", "true", "PrintMICRLine", "true", "CheckNumber", "100"); } # If we have no parameters passed, generate the initial page with default vals # Those defaults might be from a cookie (above) if one has been set. if (!param()) { print header; print start_html('FreeCheck Online'), #"Cookie:",br, #%pairs_hash, h1('FreeCheck'), "A free check printing utility", br, "Version 0.30", br, "Copyright (C) 2000 Eric Sandeen (sandeen-freecheck @ sandeen.net)", hr, "WARNING - unless you're brave, treat this application as a proof-of-concept, rather than a useful utility. I have not had a chance to test this stuff with a bank yet, and I'm also a bit concerned about the accuracy during conversion to PDF. Just don't go paying your rent with this yet, ok? :)", hr, start_form, submit( -name=>"Submit", -label=>" Get my checks! "), h2('Check Information'), h3('Account Holder Information'), textfield( -name=>'Name1', -default=>$pairs_hash{"Name1"}, -maxlength=>50), " Name 1", br, textfield( -name=>'Name2', -default=>$pairs_hash{"Name2"}, -maxlength=>50), " Name 2", br, textfield( -name=>'Address1', -default=>$pairs_hash{"Address1"}, -maxlength=>50), " Address Line 1", br, textfield( -name=>'Address2', -default=>$pairs_hash{"Address2"}, -maxlength=>50), " Address Line 2", br, textfield( -name=>'CityStateZip', -default=>$pairs_hash{"CityStateZip"}, -maxlength=>50), " City, State, Zip", br, textfield( -name=>'PhoneNumber', -default=>$pairs_hash{"PhoneNumber"}, -maxlength=>50), " Phone Number", br, h3('MICR Line Information'), "Pay close attention here - this is where you enter the MICR line at the bottom of your check. For the following symbols, use these characters:", p, hr, img {-src=>'/images/transit.gif'}, " = \"R\"", br, img {-src=>'/images/onus.gif'}, " = \"P\"", br, img {-src=>'/images/dash.gif'}, " = \"-\" (dash, or minus)", p, "For spaces, enter \"S\"", p, "For check numbers, enter a \"C\" for each check digit", p, hr, "Auxiliary On-Us field - Everything to the left of the leftmost ", img {-src=>'/images/transit.gif'}, " symbol", br, em("Don't forget to include trailing spaces (\"S\")!"), br, "This field may not be present on personal checks", br, textfield( -name=>'AuxOnUs', -default=>$pairs_hash{"AuxOnUs"}, -maxlength=>50), " Auxiliary On-Us field", p, "Transit / Routing Field - 9 numbers between, and including, the ", img {-src=>'/images/transit.gif'}, " symbols", br, textfield( -name=>'Routing', -default=>$pairs_hash{"Routing"}, -size=>11, -maxlength=>11), " Routing Field", p, "On-Us field - everything to the right of the rightmost ", img {-src=>'/images/transit.gif'}, " symbol", br, em("Don't forget to include leading spaces (\"S\")!"), br, textfield( -name=>'OnUs', -default=>$pairs_hash{"OnUs"}, -maxlength=>50), " On-Us field", p, textfield( -name=>'Fraction', -default=>$pairs_hash{"Fraction"}, -maxlength=>50), " Fraction (printed at top right of check)", br, h3('Bank Information'), textfield( -name=>'BankName', -default=>$pairs_hash{"BankName"}, -maxlength=>50), " Bank Name", br, textfield( -name=>'BankAddr1', -default=>$pairs_hash{"BankAddr1"}, -maxlength=>50), " Bank Address1", br, textfield( -name=>'BankAddr2', -default=>$pairs_hash{"BankAddr2"}, -maxlength=>50), " Bank Address 2", br, textfield( -name=>'BankCityStateZip', -default=>$pairs_hash{"BankCityStateZip"}, -maxlength=>50), " Bank City, State, Zip", br, h2('Printing Options'), textfield( -name=>'CheckNumber', -default=>$pairs_hash{"CheckNumber"}, -size=>10, -maxlength=>10), " Starting Check Number", br, "Select check style: ", popup_menu( -name=>'CheckStyle', -values=>['Normal','Quicken_Personal'], -default=>$pairs_hash{"CheckStyle"}), br, "Select check blank: ", popup_menu( -name=>'CheckType', -values=>['MVG3001','MVG1000','MVD1001'], -default=>$pairs_hash{"CheckType"}, -labels=>{ 'MVG3001'=>'VersaCheck MVG3001', 'MVG1000'=>'VersaCheck MVG1000', 'MVD1001'=>'VersaCheck MVD1001'}), p, checkbox( -name=>'PrintCheckBody', -checked=>$pairs_hash{"PrintCheckBody"}, -value=>'true', -label=>' Print Check Body'), br, checkbox( -name=>'PrintMICRLine', -checked=>$pairs_hash{"PrintMICRLine"}, -value=>'true', -label=>' Print MICR Line'), br, checkbox( -name=>'Test', -checked=>$pairs_hash{"Test"}, -value=>'true', -label=>' Print voided test checks'), p, "Select Output Format:", br, em("Be sure to de-select \"Fit to Page\" when printing PDFs"), br, em("To view PostScript correctly, you must have the GnuMICR font installed locally"), br, radio_group( -name=>'OutputType', -values=>['PDF', 'PostScript'], -labels=>{'PDF'=>' PDF', 'PostScript'=>' PostScript'}, -default=>$pairs_hash{"OutputType"}, -linebreak=>'true'), br, "Number of Pages to Print: ", textfield( -name=>'NumPages', -default=>$pairs_hash{"NumPages"}, -size=>2, -maxlength=>1), p, "Save information in a cookie?", br, em("Note: if you're security-paranoid, and you've entered real data, this might not be a such a good idea at this point..."), br, radio_group( -name=>'Cookie', -values=>['ClearCookie', 'SetCookie'], -default=>$pairs_hash{"Cookie"}, -labels=>{'ClearCookie'=>' Don\'t set, or clear', 'SetCookie'=>' Set a cookie'}, -linebreak=>'true'), br, submit( -name=>"Submit", -label=>" Get my checks! "), end_form, em("If Netscape wants to save \"freecheck.cgi\" just rename it to \"mychecks.[pdf,ps]\" - I don't know why this happens"), hr; print end_html; } # If Submit button has been pressed , then process the values if (param("Submit")) { # Get a hash of all the fields and their values my @names = param(); $pairs_string = ""; foreach (@names) { $name = $_; $value = param($_); #$pairs_string = $pairs_string . $_ . " $value\n"; $pairs_hash{$name} = $value; } # "Submit" is the only thing we don't want to store delete $pairs_hash{"Submit"}; # Deal with the form elements that didn't go in the hash: $CheckStyle = param("CheckStyle"); $CheckType = param("CheckType"); # For checkboxes, delete them from the hash/cookie if not checked if ( param("PrintMICRLine") ne "true" ) { $MICR = "--nomicr"; delete $pairs_hash{"PrintMICRLine"}; } if ( param("PrintCheckBody") ne "true" ) { $BODY = "--nobody"; delete $pairs_hash{"PrintCheckBody"}; } if ( param("Test") eq "true") { $TEST = "--test"; } else { delete $pairs_hash{"Test"}; } # Turn the hash into a string (this is a bit goofy, I guess...) # We do it as a hash initially to make it easier to fill in the # forms, above. # This is what is passed to the check generation script $pairs_string = ""; $NotDefs="Submit Cookie Test OutputType CheckStyle CheckType"; while ( ($name,$value) = each(%pairs_hash) ) { unless ($NotDefs =~ /${name}/ ) { $pairs_string = $pairs_string . "$name $value\n"; } } # Create the argument string $arguments = "--checkstyle $CheckStyle --checktype $CheckType $MICR $BODY $TEST"; # This is where we should set the next check number to be printed, # if we knew how many checks per page we had... any good way # to do this....? For now, we'll just set things up semi-manually %ChecksPerPage = ("MVG3001", "3", "MVG1000", "1", "MVD1001", "1"); $NextCheckNumber = param("CheckNumber") + param("NumPages") * $ChecksPerPage{param("CheckType")}; $pairs_hash{"CheckNumber"} = $NextCheckNumber; # Sanitize $pairs_string and $arguments # Let's not go spawning any new shells (this is minimal security...) # Also checks for SSI strings # Look for SSI if ($pairs_string =~ /\<\!--\#(.*)\s+(.*)\s?=\s?(.*)--\>/s) { kill_input(); } if ($arguments =~ /\<\!--\#(.*)\s+(.*)\s?=\s?(.*)--\>/s) { kill_input(); } # Look for shell metachars if ($pairs_string =~ /[;><\*`\|]/s) { kill_input(); } if ($arguments =~ /[;><\*`\|]/s) { kill_input(); } if ( param("Cookie") eq "SetCookie" ) { $cookie = cookie( -name=>'FreeCheck', -value=>\%pairs_hash, -expires=>'+6M', -path=>script_name(), -domain=>server_name()); } elsif ( param("Cookie") eq "ClearCookie" ) { $cookie = cookie( -name=>'FreeCheck', -value=>'', -expires=>'+1m', -path=>script_name(), -domain=>server_name()); } # Generate the actual output. # The PDF thing might become an option in the main script # soon... ########################################### # WARNING WARNING WARNING WARNING WARNING # ########################################### # THIS CODE EXECUTES AT LEAST ONE EXTERNAL PROGRAM, BASED ON # STRINGS PASSED IN FROM THE FORM. A FEEBLE EFFORT HAS BEEN # MADE TO SANITIZE THOSE STRINGS, BUT THERE COULD STILL BE # A SECURITY RISK HERE. YOU HAVE BEEN WARNED if (param("OutputType") eq "PDF") { $PDFConvert = "\| $GS $PDFOptions"; } #print (`.\/freecheck --cgi \"$pairs_string\" $arguments \| $GS $PDFConvert`); # This is just the postscript result, or the error: $Result = `.\/freecheck --cgi \"$pairs_string\" $arguments`; if (length($Result) < 500 ) { # Anything this short is an error... print header; print start_html("We encountered an error..."); print h1("There are some errors on your form:"); # HTML-ify the result ( \n to
) $Result =~ s/\n/
/gsm; print $Result; print br; print "Press the Back button on your browser to fix them..."; print p; print em("If you select \"Print voided test checks\" then MICR consistency checking will not be performed"); print end_html; exit; } # Otherwise, generate the apropriate header... # And send the data if (param("OutputType") eq "PDF") { print header( -type=>'application/pdf', -attachment=>'mychecks.pdf', -cookie=>$cookie); # This is bad... running the script a 2nd time... must be a better # way. Like open(PDF, "|$GS $PDFConvert #open (PDF, "| $PDFConvert"); #print PDF $Result; #close(PDF); print (`.\/freecheck --cgi \"$pairs_string\" $arguments \| $GS $PDFOptions`); } else { print header( -type=>'application/postscript', -attachment=>'mychecks.ps', -cookie=>$cookie); print $Result; } exit; } sub kill_input { print header; print start_html("Problem with those strings..."); print "You seem to have some shell metacharacters in your "; print "entered strings. Sorry, you can't do that..."; print p; print "Please get those funky things out of your form, and try again"; print p; print "You can hit the back button to go back to your form."; print end_html; exit; }