From dd3124b11ff6657ed161b74d3bedf907da733b8a Mon Sep 17 00:00:00 2001 From: Tony Cook Date: Sat, 26 Mar 2005 14:06:22 +0000 Subject: [PATCH] CGI samples --- samples/samp-form.cgi | 209 +++++++++++++++++++++++++++++++++++++++++ samples/samp-image.cgi | 67 +++++++++++++ 2 files changed, 276 insertions(+) create mode 100755 samples/samp-form.cgi create mode 100755 samples/samp-image.cgi diff --git a/samples/samp-form.cgi b/samples/samp-form.cgi new file mode 100755 index 00000000..93368bc8 --- /dev/null +++ b/samples/samp-form.cgi @@ -0,0 +1,209 @@ +#!/usr/bin/perl -w +use strict; +use CGI; +use HTML::Entities; + +my $cgi = CGI->new; + +# get our parameter, make sure it's defined to avoid +my $color = $cgi->param('color'); + +# Imager allows a number of different color specs, but keep this +# simple, only accept simple RRGGBB hex colors +my %errors; + +if (defined $color && $color !~ /^[0-9a-f]{6}$/i) { + $errors{color} = "Color must be hex RRGGBB"; +} + +# validated, make it defined to avoid warnings in the HTML generation +defined $color or $color = ''; + +# print the content type header and the start of out HTML +print "Content-Type: text/html\n\n", < + + Sample HTML and Image generation with Imager + + +
+HTML + +# link to the image if we got a good color +# START LINK GENERATION (see the POD) +if ($color && !keys %errors) { + # since color only contains word characters it doesn't need to be + # escaped, in most cases you'd load URI::Escape and call uri_escape() on it + print < +HTML +} +# END LINK GENERATION + +# finish off the page +# one reason template systems are handy... +my $color_encoded = encode_entities($color); +my $color_msg_encoded = encode_entities($errors{color} || ''); + +print <Color: +$color_msg_encoded

+ + + +HTML + +=head1 NAME + +samp-form.cgi - demonstrates interaction of HTML generation with image generation + +=head1 SYNOPSIS + + /cgi-bin/samp-form.cgi?color=RRGGBB + +=head1 DESCRIPTION + +This is the HTML side of a sample for Imager that demonstrates +generating an image linked from a HTML form. + +See samp-image.cgi for the image generation side of this sample. + +One common mistake seen in generating images is attempting to generate +the image inline, for example: + + # DON'T DO THIS, IT'S WRONG + my $img = Imager->new(...); + ... draw on the image ... + print ''; + +This sample code demonstrates one of the possible correct ways to +generate an image linked from a HTML page. + +This has the limitation that some processing is done twice, for +example, the validation of the parameters, but it's good when the same +image will never be generated again. + +The basic approach is to have one program generate the HTML which +links to a second program that generates the image. + +This sample is only intended to demonstrate embedding a generated +image in a page, it's missing some best practice: + +=over + +=item * + +a templating system, like HTML::Mason, or Template::Toolkit should be +used to generate the HTML, so that the HTML can be maintained +separately from the code. Such a system should also be able to HTML +or URI escape values embedded in the page to avoid the separate code +used above. + +=item * + +a more complex system would probably do some validation as part of +business rules, in a module. + +=back + +=head1 ANOTHER APPROACH + +A different way of doing this is to have the HTML generation script +write the images to a directory under the web server document root, +for example, the code from C<# START LINK GENERATION> to C<# END LINK +# GENERATION> in samp-form.cgi would be replaced with something like: + + if ($color && !keys %errors) { + # make a fairly unique filename + # in this case we could also use: + # my $filename = lc($color) . ".jpg"; + # but that's not a general solution + use Time::HiRes; + my $filename = time . $$ . ".jpg"; + my $image_path = $docroot . "/images/dynamic/" . $filename; + my $image_url = "/images/dynamic/" . $filename; + + my $im = Imager->new(xsize=>40, ysize=>40); + $im->box(filled=>1, color=>$color); + + $im->write(file=>$image_path) + or die "Cannot write to $image_path:", $im->errstr, "\n"; + + print < + HTML + } + +This has the advantage that you aren't handling a second potentially +expensive CGI request to generate the image, but it means you need +some mechanism to manage the files (for example, a cron job to delete +old files), and you need to make some directory under the document +root writeable by the user that your web server runs CGI programs as, +which may be a security concern. + +Also, if you're generating large numbers of large images, you may end +up using significant disk space. + +=head1 SECURITY + +It's important to remember that any value supplied by the user can be +abused by the user, in this example there's only one parameter, the +color of the sample image, but in a real application the values +supplied coule include font filenames, URLs, image filename and so on. +It's important that these are validated and in some cases limited to +prevent a user from using your program to obtain access or deny access +to things they shouldn't be able to. + +For example of limiting a parameter, you might have a select like: + + + + +and then build a font filename with: + + my $fontname = $cgi->param('font'); + my $fontfile=$fontpath . $fontname; + +but watch out when the user manually supplies font with a value like +C<../../../some_file_that_crashes_freetype>. + +So limit the values and validate them: + + + +and code like: + + my $fontname = $cgi->param('font'); + $fontname =~ /^\w+$/ or $fontname = 'arial'; # use a default if invalid + -e $fontpath . $fontname . ".ttf" or $fontname = 'arial'; + my $fontfile = $fontpath . $fontname . '.ttf'; + +or use a lookup table: + + my %fonts = ( + arial => "arial.ttf", + arialb => "arialb.ttf", + xfont_helv => "x11/helv.pfb", + ); + ... + + my $fontname = $cgi->param('font'); + exists $fonts{$fontname} or $fontname = 'arial'; + my $fontfile = $fontpath . $fonts{$fontname}; + +Remember that with perl your code isn't in a sandbox, it's up to you +to prevent shooting yourself in the foot. + +=head1 AUTHOR + +Tony Cook + +=cut diff --git a/samples/samp-image.cgi b/samples/samp-image.cgi new file mode 100755 index 00000000..320dd4dd --- /dev/null +++ b/samples/samp-image.cgi @@ -0,0 +1,67 @@ +#!/usr/bin/perl -w +use strict; +use CGI; +use Imager; + +my $cgi = CGI->new; + +my $color = $cgi->param('color'); + +defined $color or $color = ''; + +# Imager allows a number of different color specs, but keep this +# simple, only accept simple RRGGBB hex colors +my %errors; + +# note that you need to perform validation here as well as in +# the form script, since the user can view image and manipulate the +# URL (or even fetch the URL using LWP and modify the request in any way +# they like. +# Since we're producing an image, we don't have a mechanism to +# report errors (unless we choose to draw text on an image), so +# just product a default image. +if (!defined $color || $color !~ /^[0-9a-f]{6}$/i) { + $color = '000000'; +} + +my $im = Imager->new(xsize=>40, ysize=>40); +$im->box(filled=>1, color=>$color); + +# this will force the flushing of the headers, otherwise the server (and +# your web browser) may see the image data before the content type. +++$|; + +print "Content-Type: image/jpeg\n\n"; + +# use binmode to prevent LF expanding to CRLF on windows +binmode STDOUT; + +# we have to supply the type of output since we haven't supplied a +# filename +$im->write(fd=>fileno(STDOUT), type=>'jpeg') + or die "Cannot write to stdout: ", $im->errstr; + +=head1 NAME + +samp-image.cgi - demonstrates interaction of HTML generation with image generation + +=head1 SYNOPSIS + + /cgi-bin/samp-image.cgi?color=RRGGBB + +=head1 DESCRIPTION + +This is the image generation program that samp-form.cgi uses to +generate the image. + +See samp-form.cgi for more detail. + +=head1 REVISION + +$Revision$ + +=head1 AUTHOR + +Tony Cook + +=cut -- 2.39.5