#!/usr/bin/perl use strict; use Image::Magick; use IO::File; use CGI; use DB_File; use Fcntl ':flock'; # import LOCK_* constants my $q = new CGI(); my $base_directory = "/usr/local/web_customers/www.0xdeadbeef.com/images/"; my $filename; my $scale; my $width; my $height; my $aspect; my $old_width; my $old_height; my $ext; my $image; my $info_cache; my %info_cache; my $info_cache_fd; my $cached_size; $filename = $q->param("filename"); $scale = $q->param("scale"); $width = $q->param("width"); $height = $q->param("height"); $aspect = $q->param("aspect"); if (!$filename) { print_error("Sorry, you need to include a filename."); } if (($filename =~ /^\//) || ($filename =~ /\.\./)) { print_error("You don't have a get out of jail free card."); } ($ext) = $filename =~ /\.(...)$/; if ($ext != 'jpg' && $ext != 'gif' && $ext != 'png') { print_error("unsupported type\n"); } $filename = $base_directory . $filename; if (! -f $filename) { print_error("Can't read that file."); } if ((!$scale || $scale == 1) && !$width && !$height) { # just dump the file to stdout dump_file($filename, $ext); exit 0; } # check to see if that info is in our info cache $info_cache = tie %info_cache, "DB_File", "/home/blizzard/cache/info_cache", O_RDWR|O_CREAT, 0666, $DB_HASH or die("Can't open database."); $info_cache_fd = $info_cache->fd; open(INFO_CACHE_FH, "+<&=$info_cache_fd") || die ("failed to dup info cache fd"); flock(INFO_CACHE_FH, LOCK_EX) || die("failed to lock info cache fd"); if ($info_cache->get($filename, $cached_size) == 1) { print STDERR "not found in info cache\n"; # it wasn't found so cache it $image = new Image::Magick; $image->Read($filename); $old_width = $image->Get('width'); $old_height = $image->Get('height'); $info_cache->put($filename, $old_width."x".$old_height); } else { print STDERR "found in info cache\n"; ($old_width, $old_height) = split("x", $cached_size); } print STDERR "old size is $old_width $old_height\n"; flock(INFO_CACHE_FH, LOCK_UN); undef $info_cache; untie %info_cache; close(INFO_CACHE_FH); # try to scale first if ($scale) { if ($scale > 3 || $scale < 0.1) { print_error("Sorry, can't scale to $scale"); } $width = int($old_width * $scale); $height = int($old_height * $scale); } elsif ($aspect) { if ($width) { $height = int($old_height * ($width / $old_width)); } else { $width = int($old_width * ($height / $old_height)); } } else { if (!$width) { $width = $old_width; } if (!$height) { $height = $old_height; } } # now that we know the height and width, try and look it up in the cache my $cache; my %cache; my $cache_fd; my $cache_name; my $cache_value; # we know what our cache key would look like $cache_name = $filename.$width."x".$height; print STDERR "cache name is $cache_name\n"; $cache = tie %cache, "DB_File", "/home/blizzard/cache/cache", O_RDWR|O_CREAT, 0666, $DB_HASH or die("Can't open database."); $cache_fd = $cache->fd; open(CACHE_FH, "+<&=$cache_fd") || die("failed to dup info cache fd"); flock(CACHE_FH, LOCK_EX) || die("failed to lock cache fd"); # blast it out of the cache if ($cache->get($cache_name, $cache_value) != 1) { print STDERR "got out of cache! $cache_name\n"; dump_file ("/home/blizzard/cache/$cache_value"); } else { print STDERR "not in the cache\n"; my $name = get_next_file(); # in case it wasn't opened above... if (!$image) { $image = new Image::Magick; $image->Read($filename); } $image->Scale(width=>$width, height=>$height); $image->Set(magick=>'jpg'); $image->Write("/home/blizzard/cache/$name"); dump_file ("/home/blizzard/cache/$name"); $cache->put($cache_name, $name); } flock(CACHE_FH, LOCK_UN); undef $cache; untie %cache; close(CACHE_FH); print STDERR "done\n"; sub print_error { my $message = shift; print "Content-type: text/plain\n\n"; print $message; exit 1; } sub dump_file { my $filename = shift; my $ext = shift; my $buf; my $fh = new IO::File; $fh->open($filename) || print_error("Failed to open file"); dump_header($ext); while (read($fh, $buf, 1024)) { print $buf; } } sub dump_header { my $ext = shift; if ($ext == 'jpg') { print "Content-type: image/jpeg\n\n"; } elsif ($ext == 'gif') { print "Content-type: image/gif\n\n"; } elsif ($ext == 'png') { print "Content-type: image/png\n\n"; } else { print_error("unsupported media type\n"); } } sub get_next_file { my $i; for ($i=0; $i < 255; $i++) { if ( ! -f "/home/blizzard/cache/$i") { return $i; } } die("Can't create more than 255 files!\n"); }