As you have probably already noticed, I recently implemented a random banner to be displayed on the main page of my website. By clicking refresh a few times you can see it working. Slowly but surely I will add more banners to my collection. It's an elegant way of enabling such a feature, so I thought that it'd be nice to share it with anyone interested.
Basically I create and then keep a bunch of 905x170 pictures which I save in the images directory. These can be randomly displayed in the banner div#homepage-image
which is inserted using the /cgi-bin/rand-banner.pl
random banner generator script.
This is made possible by including the following lines in the css file:
/* custom.css */ ... /* Banner is randomly generated */ #homepage-image { background-image: url('/cgi-bin/rand-banner.pl'); } ...
This script is run automatically every day by including the following line in the crontab:
@daily /www/kiffingish.com/cgi-bin/rand-banner.pl crontab
For those interested, I have also included the perl script which does all of the magic. It should be pretty straight-forward what this script does, which shows you the power of perl.
#!/usr/bin/perl use strict; use warnings; # rand-banner.pl [crontab] : # If called with 'crontab' just create the file list of banners, # otherwise return the binary contents of a random banner immage # for displaying on a web page. use CGI; my $cgi = new CGI; # Directories based from root dir. my $root_dir = "/path/to/root/dir"; my $cgi_dir = "$root_dir/cgi-bin"; my $doc_dir = "$root_dir/docs"; my $img_dir = "$doc_dir/images"; my $banners_lst = "banners.lst"; # Coversion file ext to http img/type. my %ext2typ = ( bmp => 'bmp', gif => 'gif', jpeg => 'jpeg', jpg => 'jpeg', png => 'png', tif => 'tiff', tiff => 'tiff', ); # Only interested in the following file types. my $img_filter = join(',', keys %ext2typ); # If called with 'crontab' or banners.lst does not exist, collect # list of banners from 905x170 files in images directory. my $crontab = (defined($ARGV[0]) && ($ARGV[0] eq 'crontab')); if ($crontab || (! -f $banners_lst)) { # See: http://www.imagemagick.org/script/identify.php my $output = `identify $img_dir/*.{$img_filter} 2>/dev/null |grep ' 905x170 ' | sed 's/\\[.*//' | sed 's/^.*\\///'`; open my $fh, ">", $banners_lst or die "Cannot open file '$banners_lst' for writing ($!)"; print $fh $output; close $fh; } # All done if called from crontab. exit if $crontab; # Grab the latest banner list. open my $fh, "<", $banners_lst or die "Cannot open file '$banners_lst' for reading ($!)"; my @banners = <$fh>; close $fh; # Take a random item from the list. my $banner = $banners[rand @banners]; chomp($banner); # Define banner image filepath. $banner =~ /^.*\.(.*)$/; my $ext = $1; my $img = "$img_dir/$banner"; # Get the binary contents of the image file. open (IMAGE, $img) or die "Cannot open image file '$img' for reading ($!)"; my $size = -s $img; my $data; read IMAGE, $data, $size; close (IMAGE); # Return image data to the caller. Be sure to disable caching so # that each new request returns a new banner image. print "Content-type: image/$ext2typ{$ext}\n"; print "Cache-Control: max-age=0, no-cache, no-store, must-revalidate\n"; print "Pragma: no-cache\n"; print "Expires: Wed, 11 Jan 1984 05:00:00 GMT\n"; print "\n"; print $data;
This might seem like alot of work for something very simple, but the challenge was worth it for me. I kind like this little added feature to my website. Have fun using it.
You are using the 3-argument form of open(), which is good, but you missed one spot. Now I'm pretty sure there's no file named "foo | some-dangerous-command" in your banner dir :-)
Thanks for the heads up. That's a good point you make, could be a potential loophole for tainted attacks. However, since I have complete control over the files in that directory, I shouldn't have any problems.
I object to line 39. Calling 'grep' and 'sed' from Perl is an abomination in the eyes of God ;-> And the "$img_dir/*.{$img_filter}" syntax depends on your shell being bash or zsh, but is not compatible with some other shells, like dash.
Besides, http://search.cpan.org/~jcristy/PerlMagick-6.86/Magick.pm is here so we don't need to call ImageMagick binaries - in this particular case, Ping() method can be used to get the dimensions of a particular image file, while the list of candidate files in a directory can be obtained by using readdir() or, probably more easily, glob().
Yes, I am a purist. And a nitpicker.
You are right about making the sed and grep system calls rather than perl. To be honest, I was just too lazy trying to figure it out in perl when I could hack out the linux call in a tenth of the time.
Nothing wrong with being a purist, I like that. In that regard, by being (too) lazy and sparking reactions like these, I did achieve part of the point I was trying to make.
I'll leave it as an exercise for someone else to come up with a a more proper solution, thanks.
Feedback is always good, no one is ever too old to stop learning.