Sometimes there are things, which can bring everyone to the rage accumulation. For example if traffic of the server is exceeded because of hotlinking and it's measured at least in hundreds of megabytes. | So, for me this is only a story about someone else because I'm always using the following script, if the traffic of the server is not unlimited. I've used Perl to convert or resize images, because according my measurements Perl was doing this at least twice as fast as PHP and more qualitatively as PHP (this concerns anyhow PHP4). Only the one weakness can prevent you from using this script - it works with HTTP_REFERER server variable and since some browsers, firewalls or proxies for any reasons don't sending this field to the webserver, you can punish innocent people, who want only to see your images. But the advantages are evident - you can place your images out of the web space and show them only if they has been called from your web site. Lets go on.
As usually we begining a Perl script with the line
PERL:
and at one stroke checking if the file was hotlinked
PERL:
#reading referer
# reading the server name
$detect = $ENV{'SERVER_NAME'};
# this replaces the www. prefix from the domain name
$detect =~ s/www\.//ig;
# check if the referer string contains the domain name
# however an empty referer means for us the same as
# a referer not from our site
if($ENV{'HTTP_REFERER'} = ~/$detect/){
print "Content-type: text/html\n\n";
print "Your IP: ".$ENV{'REMOTE_ADDR'}."";
print "Referer: ".$ENV{'HTTP_REFERER'}."";
print "Hotlink is forbidden!";
print "Please visit our <a href=\"http://www.$detect\">WebPage</a> to view this file.";
die;
}
This check with regexp is here not very perfect, because a hotlinker could use the domain name in the path to files which could show us a fictituous accuracy. Think about how to make this check better. Which was my idea - we could split a referer string with the domain name and than analyse the parts.
So, if the check passed, we can go further
PERL:
# read the query string and parse it
$buffer = $ENV{'QUERY_STRING'};
# in our example in the list are:
# @data[0] - foldername
# @data[1] - filename
# @data[2] - image width or height
@data = split(/&/, $buffer);
# set the server path to images
$ftp = "/home/user/images";
So, some words about the query string format. For simpler parsing and more security I'm using the simple format where all variables are predefined by their placement in the query string. For example instead of var1=1&var2=2&var3=3 I'm using 1&2&3. Depends on your directory structure you may want to pass some path to files so the query string will be look as fotlder&filename&size where the size is height or width of the image. With complete URL it will be like http://www.domain.com/cgi-bin/image.pl?folder1&filename&330. For more security you could define a hash with names of folders with images and pass only the index of according folder, but I consider it's most likely a question of the server security that should not alarm us. Some other thing is, that we should hide the file extensions.
Now include required Perl libs
PERL:
use CGI ':standard';
use GD;
and build the server path to the image:
PERL:
$image_file = $ftp."/".@data[0]."/".@data[1].".jpg";
If you want to use full images filenames you might send them as they are in the query string. For this example I'm using only the jpeg images and than can hide their extensions. So now we will proceed with the image
PERL:
if ( (-e $image_file) && (-r $image_file) ) {
# if the filename exists and is readable
# read the image in
$im = GD::Image->newFromJpeg($image_file);
# read image width and hight
($width, $height) = $im->getBounds();
# with this you can easily resize an image
# here I assume the @data[2] is the new image width
$newwidth = @data[2];
$newheight = $height/($width/@data[2]);
$outim = new GD::Image($newwidth, $newheight);
$outim->copyResized($im, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
} else {
# if the filename was not found or is not readable
$outim = new GD::Image(120, 120);
$black = $outim->colorAllocate(0,0,0);
$blue = $outim->colorAllocate(0,0,255);
$outim->colorAllocate(0,0,0);
$outim->interlaced('true');
$outim->rectangle(0, 0, 119, 119, $black);
$outim->string(gdGiantFont, 20, 50, "No image!", $blue);
}
So here is the @data[2] new image width and it is firmly during the image height is variable and is changed proportionally to the width. You could make it turned around on on some other way.
PERL:
# send image to the browser
binmode STDOUT;
print "Content-type: image/jpeg\n\n";
print $outim->jpeg();
exit;
So with this script you can make a simple gallery producing the thumbs and big images hotlink protected. The next step here were to learn the script to cache resized images for the server load reducing.
Now we have finished. You can place the script in your cgi-bin and use it. Don't forget to make it executable.