#!/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;
}