"Fossies" - the Fresh Open Source Software Archive

Member "Apache-Gallery-1.0.2/lib/Apache/Gallery.pm" (8 Jun 2011, 58965 Bytes) of package /linux/www/old/Apache-Gallery-1.0.2.tar.gz:


The requested HTML page contains a <FORM> tag that is unusable on "Fossies" in "automatic" (rendered) mode so that page is shown as HTML source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 package Apache::Gallery;
    2 
    3 # $Author: mil $ $Rev: 335 $
    4 # $Date: 2011-06-08 20:47:46 +0200 (Wed, 08 Jun 2011) $
    5 
    6 use strict;
    7 
    8 use vars qw($VERSION);
    9 
   10 $VERSION = "1.0.2";
   11 
   12 BEGIN {
   13 
   14     if (exists($ENV{MOD_PERL_API_VERSION})
   15         and ($ENV{MOD_PERL_API_VERSION}==2)) {
   16         require mod_perl2;
   17         if ($mod_perl::VERSION >= 1.99 && $mod_perl::VERSION < 2.0) {
   18             die "mod_perl 2.0.0 or later is now required";
   19         }
   20         require Apache2::ServerRec;
   21         require Apache2::RequestRec;
   22         require Apache2::Log;
   23         require APR::Table;
   24         require Apache2::RequestIO;
   25         require Apache2::SubRequest;
   26         require Apache2::Const;
   27     
   28         Apache2::Const->import(-compile => 'OK','DECLINED','FORBIDDEN','NOT_FOUND','HTTP_NOT_MODIFIED');
   29 
   30         $::MP2 = 1;
   31     } else {
   32         require mod_perl;
   33 
   34         require Apache;
   35         require Apache::Constants;
   36         require Apache::Request;
   37     
   38         Apache::Constants->import('OK','DECLINED','FORBIDDEN','NOT_FOUND');
   39         $::MP2 = 0;
   40     }
   41 }
   42 
   43 use Image::Info qw(image_info);
   44 use Image::Size qw(imgsize);
   45 use Image::Imlib2;
   46 use Text::Template;
   47 use File::stat;
   48 use File::Spec;
   49 use POSIX qw(floor);
   50 use URI::Escape;
   51 use CGI;
   52 use CGI::Cookie;
   53 use Encode;
   54 use HTTP::Date;
   55 use Digest::MD5 qw(md5_base64);
   56 
   57 use Data::Dumper;
   58 
   59 # Regexp for escaping URI's
   60 my $escape_rule = "^A-Za-z0-9\-_.!~*'()\/";
   61 my $memoized;
   62 
   63 sub handler {
   64 
   65     my $r = shift or Apache2::RequestUtil->request();
   66 
   67     unless (($r->method eq 'HEAD') or ($r->method eq 'GET')) {
   68         return $::MP2 ? Apache2::Const::DECLINED() : Apache::Constants::DECLINED();
   69     }
   70 
   71     if ((not $memoized) and ($r->dir_config('GalleryMemoize'))) {
   72         require Memoize;
   73         Memoize::memoize('get_imageinfo');
   74         $memoized=1;
   75     }
   76 
   77     $r->headers_out->{"X-Powered-By"} = "apachegallery.dk $VERSION - Hest design!";
   78     $r->headers_out->{"X-Gallery-Version"} = '$Rev: 335 $ $Date: 2011-06-08 20:47:46 +0200 (Wed, 08 Jun 2011) $';
   79 
   80     my $filename = $r->filename;
   81     $filename =~ s/\/$//;
   82     my $topdir = $filename;
   83 
   84     my $media_rss_enabled = $r->dir_config('GalleryEnableMediaRss');
   85 
   86     # Just return the http headers if the client requested that
   87     if ($r->header_only) {
   88 
   89         if (!$::MP2) {
   90             $r->send_http_header;
   91         }
   92 
   93         if (-f $filename or -d $filename) {
   94             return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
   95         }
   96         else {
   97             return $::MP2 ? Apache2::Const::NOT_FOUND() : Apache::Constants::NOT_FOUND();
   98         }
   99     }
  100 
  101     my $cgi = new CGI;
  102 
  103     # Handle selected images
  104     if ($cgi->param('selection')) {
  105         my @selected = $cgi->param('selection');
  106         my $content = join "<br />\n",@selected;
  107         $r->content_type('text/html');
  108         $r->headers_out->{'Content-Length'} = length($content);
  109 
  110         if (!$::MP2) {
  111             $r->send_http_header;
  112         }
  113 
  114         $r->print($content);
  115         return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
  116     }
  117     
  118     # Selectmode providing checkboxes beside all thumbnails
  119     my $select_mode = $cgi->param('select');
  120     
  121     # Let Apache serve icons without us modifying the request
  122     if ($r->uri =~ m/^\/icons/i) {
  123         return $::MP2 ? Apache2::Const::DECLINED() : Apache::Constants::DECLINED();
  124     }
  125     # Lookup the file in the cache and scale the image if the cached
  126     # image does not exist
  127     if ($r->uri =~ m/\.cache\//i) {
  128 
  129         my $filename = $r->filename().$r->path_info();
  130         $filename =~ s/\.cache//;
  131 
  132         $filename =~ m/\/(\d+)x(\d+)\-/;
  133         my $image_width = $1;
  134         my $image_height = $2;
  135 
  136         $filename =~ s/\/(\d+)x(\d+)\-//;
  137 
  138         my ($width, $height, $type) = imgsize($filename);
  139 
  140         my $imageinfo = get_imageinfo($r, $filename, $type, $width, $height);
  141     
  142         my $cached = scale_picture($r, $filename, $image_width, $image_height, $imageinfo);
  143 
  144         my $file = cache_dir($r, 0);
  145         $file =~ s/\.cache//;
  146 
  147         my $subr = $r->lookup_file($file);
  148         $r->content_type($subr->content_type());
  149 
  150         if ($::MP2) {
  151             my $fileinfo = stat($file);
  152 
  153             my $nonce = md5_base64($fileinfo->ino.$fileinfo->mtime);
  154             if ($r->headers_in->{"If-None-Match"} eq $nonce) {
  155                 return Apache2::Const::HTTP_NOT_MODIFIED();
  156             }
  157 
  158             if ($r->headers_in->{"If-Modified-Since"} && str2time($r->headers_in->{"If-Modified-Since"}) < $fileinfo->mtime) {
  159                 return Apache2::Const::HTTP_NOT_MODIFIED();
  160             }
  161 
  162             $r->headers_out->{"Content-Length"} = $fileinfo->size; 
  163             $r->headers_out->{"Last-Modified-Date"} = time2str($fileinfo->mtime); 
  164             $r->headers_out->{"ETag"} = $nonce;
  165             $r->sendfile($file);
  166             return Apache2::Const::OK();
  167         }
  168         else {
  169             $r->path_info('');
  170             $r->filename($file);
  171             return Apache::Constants::DECLINED();
  172         }
  173         
  174     }
  175 
  176     my $uri = $r->uri;
  177     $uri =~ s/\/$//;
  178 
  179     unless (-f $filename or -d $filename) {
  180         show_error($r, 404, "404!", "No such file or directory: ".uri_escape($r->uri, $escape_rule));
  181         return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
  182     }
  183 
  184     my $doc_pattern = $r->dir_config('GalleryDocFile');
  185     unless ($doc_pattern) {
  186         $doc_pattern = '\.(mpe?g|avi|mov|asf|wmv|doc|mp3|ogg|pdf|rtf|wav|dlt|txt|html?|csv|eps)$'
  187     }
  188     my $img_pattern = $r->dir_config('GalleryImgFile');
  189     unless ($img_pattern) {
  190         $img_pattern = '\.(jpe?g|png|tiff?|ppm)$'
  191     }
  192 
  193     # Let Apache serve files we don't know how to handle anyway
  194     if (-f $filename && $filename !~ m/$img_pattern/i) {
  195         return $::MP2 ? Apache2::Const::DECLINED() : Apache::Constants::DECLINED();
  196     }
  197 
  198     if (-d $filename) {
  199 
  200         unless (-d cache_dir($r, 0)) {
  201             unless (create_cache($r, cache_dir($r, 0))) {
  202                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
  203             }
  204         }
  205 
  206         my $tpl_dir = $r->dir_config('GalleryTemplateDir');
  207 
  208         # Instead of reading the templates every single time
  209         # we need them, create a hash of template names and
  210         # the associated Text::Template objects.
  211         my %templates = create_templates({layout       => "$tpl_dir/layout.tpl",
  212                           index        => "$tpl_dir/index.tpl",
  213                           directory    => "$tpl_dir/directory.tpl",
  214                           picture      => "$tpl_dir/picture.tpl",
  215                           file         => "$tpl_dir/file.tpl",
  216                           comment      => "$tpl_dir/dircomment.tpl",
  217                           nocomment    => "$tpl_dir/nodircomment.tpl",
  218                           rss          => "$tpl_dir/rss.tpl",
  219                           rss_item     => "$tpl_dir/rss_item.tpl",
  220                           navdirectory => "$tpl_dir/navdirectory.tpl",
  221                          });
  222 
  223 
  224 
  225 
  226         my %tpl_vars;
  227 
  228         $tpl_vars{TITLE} = "Index of: $uri";
  229 
  230         if ($media_rss_enabled) {
  231             # Put the RSS feed on all directory listings
  232             $tpl_vars{META} = '<link rel="alternate" href="?rss=1" type="application/rss+xml" title="" id="gallery" />';
  233         }
  234 
  235         unless (opendir (DIR, $filename)) {
  236             show_error ($r, 500, $!, "Unable to access directory $filename: $!");
  237             return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
  238         }
  239 
  240         $tpl_vars{MENU} = generate_menu($r);
  241 
  242         $tpl_vars{FORM_BEGIN} = $select_mode?'<form method="post">':'';
  243         $tpl_vars{FORM_END}   = $select_mode?'<input type="submit" name="Get list" value="Get list"></form>':'';
  244 
  245         # Read, sort, and filter files
  246         my @files = grep { !/^\./ && -f "$filename/$_" } readdir (DIR);
  247 
  248         @files=gallerysort($r, @files);
  249 
  250         my @downloadable_files;
  251 
  252         if (@files) {
  253             # Remove unwanted files from list
  254             my @new_files = ();
  255             foreach my $picture (@files) {
  256 
  257                 my $file = $topdir."/".$picture;
  258 
  259                 if ($file =~ /$img_pattern/i) {
  260                     push (@new_files, $picture);
  261                 }
  262 
  263                 if ($file =~ /$doc_pattern/i) {
  264                     push (@downloadable_files, $picture);
  265                 }
  266 
  267             }
  268             @files = @new_files;
  269         }
  270 
  271         # Read and sort directories
  272         rewinddir (DIR);
  273         my @directories = grep { !/^\./ && -d "$filename/$_" } readdir (DIR);
  274         my $dirsortby;
  275         if (defined($r->dir_config('GalleryDirSortBy'))) {
  276             $dirsortby=$r->dir_config('GalleryDirSortBy');
  277         } else {
  278             $dirsortby=$r->dir_config('GallerySortBy');
  279         }
  280         if ($dirsortby && $dirsortby =~ m/^(size|atime|mtime|ctime)$/) {
  281             @directories = map(/^\d+ (.*)/, sort map(stat("$filename/$_")->$dirsortby()." $_", @directories));
  282         } else {
  283             @directories = sort @directories;
  284         }
  285 
  286         closedir(DIR);
  287 
  288 
  289         # Combine directories and files to one listing
  290         my @listing;
  291         push (@listing, @directories);
  292         push (@listing, @files);
  293         push (@listing, @downloadable_files);
  294         
  295         if (@listing) {
  296 
  297             my $filelist;
  298 
  299             my $file_counter = 0;
  300             my $start_at = 1;
  301             my $max_files = $r->dir_config('GalleryMaxThumbnailsPerPage');
  302 
  303             if (defined($cgi->param('start'))) {
  304                 $start_at = $cgi->param('start');
  305                 if ($start_at < 1) {
  306                     $start_at = 1;
  307                 }
  308             }
  309 
  310             my $browse_links = "";
  311             if (defined($max_files)) {
  312             
  313                 for (my $i=1; $i<=scalar(@listing); $i++) {
  314 
  315                     my $from = $i;
  316 
  317                     my $to = $i+$max_files-1;
  318                     if ($to > scalar(@listing)) {
  319                         $to = scalar(@listing);
  320                     }
  321 
  322                     if ($start_at < $from || $start_at > $to) {
  323                         $browse_links .= "<a href=\"?start=$from\">$from - ".$to."</a> ";
  324                     }
  325                     else {
  326                         $browse_links .= "$from - $to ";
  327                     }
  328 
  329                     $i+=$max_files-1;
  330 
  331                 }
  332 
  333             }
  334 
  335             $tpl_vars{BROWSELINKS} = $browse_links;
  336 
  337             DIRLOOP:
  338             foreach my $file (@listing) {
  339 
  340                 $file_counter++;
  341 
  342                 if ($file_counter < $start_at) {
  343                     next;
  344                 }
  345 
  346                 if (defined($max_files) && $file_counter > $max_files+$start_at-1) {
  347                     last DIRLOOP;
  348                 }
  349 
  350                 my $thumbfilename = $topdir."/".$file;
  351 
  352                 my $fileurl = $uri."/".$file;
  353 
  354                 # Debian bug #619625 <http://bugs.debian.org/619625>
  355                 if (-d $thumbfilename && ! -e $thumbfilename . ".ignore") {
  356                     my $dirtitle = '';
  357                     if (-e $thumbfilename . ".folder") {
  358                         $dirtitle = get_filecontent($thumbfilename . ".folder");
  359                     }
  360 
  361                     $dirtitle = $dirtitle ? $dirtitle : $file;
  362                     $dirtitle =~ s/_/ /g if $r->dir_config('GalleryUnderscoresToSpaces');
  363 
  364                     $tpl_vars{FILES} .=
  365                          $templates{directory}->fill_in(HASH=> {FILEURL => uri_escape($fileurl, $escape_rule),
  366                                             FILE    => $dirtitle,
  367                                            }
  368                                        );
  369 
  370                 }
  371                 # Debian bug #619625 <http://bugs.debian.org/619625>
  372                 elsif (-f $thumbfilename && $thumbfilename =~ /$doc_pattern/i && $thumbfilename !~ /$img_pattern/i && ! -e $thumbfilename . ".ignore") {
  373                     my $type = lc($1);
  374                     my $stat = stat($thumbfilename);
  375                     my $size = $stat->size;
  376                     my $filetype;
  377 
  378                     if ($thumbfilename =~ m/\.(mpe?g|avi|mov|asf|wmv)$/i) {
  379                         $filetype = "video-$type";
  380                     } elsif ($thumbfilename =~ m/\.(txt|html?)$/i) {
  381                         $filetype = "text-$type";
  382                     } elsif ($thumbfilename =~ m/\.(mp3|ogg|wav)$/i) {
  383                         $filetype = "sound-$type";
  384                     } elsif ($thumbfilename =~ m/$doc_pattern/i) {
  385                         $filetype = "application-$type";
  386                     } else {
  387                         $filetype = "unknown";
  388                     }
  389 
  390                     # Debian bug #348724 <http://bugs.debian.org/348724>
  391                     # not images
  392                     my $filetitle = $file;
  393                     $filetitle =~ s/_/ /g if $r->dir_config('GalleryUnderscoresToSpaces');
  394 
  395                     $tpl_vars{FILES} .=
  396                          $templates{file}->fill_in(HASH => {%tpl_vars,
  397                                         FILEURL => uri_escape($fileurl, $escape_rule),
  398                                         ALT => "Size: $size Bytes",
  399                                         FILE => $filetitle,
  400                                         TYPE => $type,
  401                                         FILETYPE => $filetype,
  402                                            }
  403                                       );
  404                 }
  405                 # Debian bug #619625 <http://bugs.debian.org/619625>
  406                 elsif (-f $thumbfilename && ! -e $thumbfilename . ".ignore") {
  407 
  408                     my ($width, $height, $type) = imgsize($thumbfilename);
  409                     next if $type eq 'Data stream is not a known image file format';
  410 
  411                     my @filetypes = qw(JPG TIF PNG PPM GIF);
  412 
  413                     next unless (grep $type eq $_, @filetypes);
  414                     my ($thumbnailwidth, $thumbnailheight) = get_thumbnailsize($r, $width, $height);    
  415                     my $imageinfo = get_imageinfo($r, $thumbfilename, $type, $width, $height);
  416                     my $cached = get_scaled_picture_name($thumbfilename, $thumbnailwidth, $thumbnailheight);
  417 
  418                     my $rotate = readfile_getnum($r, $imageinfo, $thumbfilename.".rotate");
  419 
  420                     # Debian bug #348724 <http://bugs.debian.org/348724>
  421                     # HTML <img> tag, alt attribute
  422                     my $filetitle = $file;
  423                     $filetitle =~ s/_/ /g if $r->dir_config('GalleryUnderscoresToSpaces');
  424 
  425                     my %file_vars = (FILEURL => uri_escape($fileurl, $escape_rule),
  426                              FILE    => $filetitle,
  427                              DATE    => $imageinfo->{DateTimeOriginal} ? $imageinfo->{DateTimeOriginal} : '', # should this really be a stat of the file instead of ''?
  428                              SRC     => uri_escape($uri."/.cache/$cached", $escape_rule),
  429                              HEIGHT => (grep($rotate==$_, (1, 3)) ? $thumbnailwidth : $thumbnailheight),
  430                              WIDTH => (grep($rotate==$_, (1, 3)) ? $thumbnailheight : $thumbnailwidth),
  431                              SELECT  => $select_mode?'<input type="checkbox" name="selection" value="'.$file.'">&nbsp;&nbsp;':'',);
  432                     $tpl_vars{FILES} .= $templates{picture}->fill_in(HASH => {%tpl_vars,
  433                                                  %file_vars,
  434                                                 },
  435                                                );
  436 
  437                     if ($media_rss_enabled) {
  438                         my ($content_image_width, undef, $content_image_height) = get_image_display_size($cgi, $r, $width, $height);
  439                         my %item_vars = ( 
  440                             THUMBNAIL => uri_escape($uri."/.cache/$cached", $escape_rule),
  441                             LINK      => uri_escape($fileurl, $escape_rule),
  442                             TITLE     => $file,
  443                             CONTENT   => uri_escape($uri."/.cache/".$content_image_width."x".$content_image_height."-".$file, $escape_rule)
  444                         );
  445                         $tpl_vars{ITEMS} .= $templates{rss_item}->fill_in(HASH => { 
  446                             %item_vars
  447                         });
  448                     }
  449                 }
  450             }
  451         }
  452         else {
  453             $tpl_vars{FILES} = "No files found";
  454             $tpl_vars{BROWSELINKS} = "";
  455         }
  456 
  457         # Generate prev and next directory menu items
  458         $filename =~ m/(.*)\/.*?$/;
  459         my $parent_filename = $1;
  460 
  461         $r->document_root =~ m/(.*)\/$/;
  462         my $root_path = $1;
  463         print STDERR "$filename vs $root_path\n";
  464         if ($filename ne $root_path) {
  465             unless (opendir (PARENT_DIR, $parent_filename)) {
  466                 show_error ($r, 500, $!, "Unable to access parent directory $parent_filename: $!");
  467                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
  468             }
  469     
  470             # Debian bug #619625 <http://bugs.debian.org/619625>
  471             my @neighbour_directories = grep { !/^\./ && -d "$parent_filename/$_" && ! -e "$parent_filename/$_" . ".ignore" } readdir (PARENT_DIR);
  472             my $dirsortby;
  473             if (defined($r->dir_config('GalleryDirSortBy'))) {
  474                 $dirsortby=$r->dir_config('GalleryDirSortBy');
  475             } else {
  476                 $dirsortby=$r->dir_config('GallerySortBy');
  477             }
  478             if ($dirsortby && $dirsortby =~ m/^(size|atime|mtime|ctime)$/) {
  479                 @neighbour_directories = map(/^\d+ (.*)/, sort map(stat("$parent_filename/$_")->$dirsortby()." $_", @neighbour_directories));
  480             } else {
  481                 @neighbour_directories = sort @neighbour_directories;
  482             }
  483 
  484             closedir(PARENT_DIR);
  485 
  486             my $neightbour_counter = 0;
  487             foreach my $neighbour_directory (@neighbour_directories) {
  488                 if ($parent_filename.'/'.$neighbour_directory eq $filename) {
  489                     if ($neightbour_counter > 0) {
  490                         print STDERR "prev directory is " .$neighbour_directories[$neightbour_counter-1] ."\n";
  491                         my $linktext = $neighbour_directories[$neightbour_counter-1];
  492                         if (-e $parent_filename.'/'.$neighbour_directories[$neightbour_counter-1] . ".folder") {
  493                             $linktext = get_filecontent($parent_filename.'/'.$neighbour_directories[$neightbour_counter-1] . ".folder");
  494                         }
  495                         my %info = (
  496                         URL => "../".$neighbour_directories[$neightbour_counter-1],
  497                         LINK_NAME => "<<< $linktext",
  498                         DIR_FILES => "",
  499                         );
  500                         $tpl_vars{PREV_DIR_FILES} = $templates{navdirectory}->fill_in(HASH=> {%info});
  501                         print STDERR $tpl_vars{PREV_DIR_FILES} ."\n";
  502 
  503                     }
  504                     if ($neightbour_counter < scalar @neighbour_directories - 1) {
  505                         my $linktext = $neighbour_directories[$neightbour_counter+1];
  506                         if (-e $parent_filename.'/'.$neighbour_directories[$neightbour_counter+1] . ".folder") {
  507                             $linktext = get_filecontent($parent_filename.'/'.$neighbour_directories[$neightbour_counter+1] . ".folder");
  508                         }
  509                         my %info = (
  510                         URL => "../".$neighbour_directories[$neightbour_counter+1],
  511                         LINK_NAME => "$linktext >>>",
  512                         DIR_FILES => "",
  513                         );
  514                         $tpl_vars{NEXT_DIR_FILES} = $templates{navdirectory}->fill_in(HASH=> {%info});
  515                         print STDERR "next directory is " .$neighbour_directories[$neightbour_counter+1] ."\n";
  516                     }
  517                 }
  518                 $neightbour_counter++;
  519             }
  520         }
  521 
  522         if (-f $topdir . '.comment') {
  523             my $comment_ref = get_comment($topdir . '.comment');
  524             my %comment_vars;
  525             $comment_vars{COMMENT} = $comment_ref->{COMMENT} . '<br />' if $comment_ref->{COMMENT};
  526             $comment_vars{TITLE} = $comment_ref->{TITLE} if $comment_ref->{TITLE};
  527             $tpl_vars{DIRCOMMENT} = $templates{comment}->fill_in(HASH => \%comment_vars);
  528             $tpl_vars{TITLE} = $comment_ref->{TITLE} if $comment_ref->{TITLE};
  529         } else {
  530             $tpl_vars{DIRCOMMENT} = $templates{nocomment}->fill_in(HASH=>\%tpl_vars);
  531         }
  532 
  533         if ($cgi->param('rss')) {
  534             $tpl_vars{MAIN} = $templates{rss}->fill_in(HASH => \%tpl_vars);
  535             $r->content_type('application/rss+xml');
  536         } else {
  537             $tpl_vars{MAIN} = $templates{index}->fill_in(HASH => \%tpl_vars);
  538             $tpl_vars{MAIN} = $templates{layout}->fill_in(HASH => \%tpl_vars);
  539             $r->content_type('text/html');
  540         }
  541 
  542         $r->headers_out->{'Content-Length'} = length($tpl_vars{MAIN});
  543 
  544         if (!$::MP2) {
  545             $r->send_http_header;
  546         }
  547 
  548         $r->print($tpl_vars{MAIN});
  549         return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
  550 
  551     }
  552     else {
  553 
  554         # original size
  555         if (defined($ENV{QUERY_STRING}) && $ENV{QUERY_STRING} eq 'orig') {
  556             if ($r->dir_config('GalleryAllowOriginal') ? 1 : 0) {
  557                 $r->filename($filename);
  558                 return $::MP2 ? Apache2::Const::DECLINED() : Apache::Constants::DECLINED();
  559             } else {
  560                 return $::MP2 ? Apache2::Const::FORBIDDEN() : Apache::Constants::FORBIDDEN();
  561             }
  562         }
  563     
  564         # Create cache dir if not existing
  565         my @tmp = split (/\//, $filename);
  566         my $picfilename = pop @tmp;
  567         my $path = (join "/", @tmp)."/";
  568         my $cache_path = cache_dir($r, 1);
  569 
  570         unless (-d $cache_path) {
  571             unless (create_cache($r, $cache_path)) {
  572                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
  573             }
  574         }
  575 
  576         my ($orig_width, $orig_height, $type) = imgsize($filename);
  577 
  578         my $imageinfo = get_imageinfo($r, $filename, $type, $orig_width, $orig_height);
  579 
  580         my ($image_width, $width, $height, $original_size) = get_image_display_size($cgi, $r, $orig_width, $orig_height);
  581 
  582         my $cached = get_scaled_picture_name($filename, $image_width, $height);
  583         
  584         my $tpl_dir = $r->dir_config('GalleryTemplateDir');
  585 
  586         my %templates = create_templates({layout         => "$tpl_dir/layout.tpl",
  587                           picture        => "$tpl_dir/showpicture.tpl",
  588                           navpicture     => "$tpl_dir/navpicture.tpl",
  589                           info           => "$tpl_dir/info.tpl",
  590                           scale          => "$tpl_dir/scale.tpl",
  591                           scaleactive    => "$tpl_dir/scaleactive.tpl",
  592                           orig           => "$tpl_dir/orig.tpl",
  593                           refresh        => "$tpl_dir/refresh.tpl",
  594                           interval       => "$tpl_dir/interval.tpl",
  595                           intervalactive => "$tpl_dir/intervalactive.tpl",
  596                           slideshowisoff => "$tpl_dir/slideshowisoff.tpl",
  597                           slideshowoff   => "$tpl_dir/slideshowoff.tpl",
  598                           pictureinfo    => "$tpl_dir/pictureinfo.tpl",
  599                           nopictureinfo  => "$tpl_dir/nopictureinfo.tpl",
  600                          });
  601 
  602         my %tpl_vars;
  603 
  604         my $resolution = (($image_width > $orig_width) && ($height > $orig_height)) ? 
  605             "$orig_width x $orig_height" : "$image_width x $height";
  606 
  607         $tpl_vars{TITLE} = "Viewing ".$r->uri()." at $image_width x $height";
  608         $tpl_vars{META} = " ";
  609         $tpl_vars{RESOLUTION} = $resolution;
  610         $tpl_vars{MENU} = generate_menu($r);
  611         $tpl_vars{SRC} = uri_escape(".cache/$cached", $escape_rule);
  612         $tpl_vars{URI} = $r->uri();
  613     
  614         my $exif_mode = $r->dir_config('GalleryEXIFMode');
  615         unless ($exif_mode) {
  616             $exif_mode = 'namevalue';
  617         }
  618 
  619         unless (opendir(DATADIR, $path)) {
  620             show_error($r, 500, "Unable to access directory", "Unable to access directory $path");
  621             return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
  622         }
  623         my @pictures = grep { /$img_pattern/i && ! -e "$path/$_" . ".ignore" } readdir (DATADIR);
  624         closedir(DATADIR);
  625         @pictures = gallerysort($r, @pictures);
  626 
  627         $tpl_vars{TOTAL} = scalar @pictures;
  628 
  629         my $prevpicture;
  630         my $nextpicture;
  631     
  632         for (my $i=0; $i <= $#pictures; $i++) {
  633             if ($pictures[$i] eq $picfilename) {
  634 
  635                 $tpl_vars{NUMBER} = $i+1;
  636 
  637                 $prevpicture = $pictures[$i-1];
  638                 my $displayprev = ($i>0 ? 1 : 0);
  639 
  640                 if ($r->dir_config("GalleryWrapNavigation")) {
  641                     $prevpicture = $pictures[$i>0 ? $i-1 : $#pictures];
  642                     $displayprev = 1;
  643                 }
  644                 if ($prevpicture and $displayprev) {
  645                     my ($orig_width, $orig_height, $type) = imgsize($path.$prevpicture);
  646                     my ($thumbnailwidth, $thumbnailheight) = get_thumbnailsize($r, $orig_width, $orig_height);  
  647                     my $imageinfo = get_imageinfo($r, $path.$prevpicture, $type, $orig_width, $orig_height);
  648                     my $cached = get_scaled_picture_name($path.$prevpicture, $thumbnailwidth, $thumbnailheight);
  649                     my %nav_vars;
  650                     $nav_vars{URL}       = uri_escape($prevpicture, $escape_rule);
  651                     $nav_vars{FILENAME}  = $prevpicture;
  652                     $nav_vars{WIDTH}     = $width;
  653                     $nav_vars{PICTURE}   = uri_escape(".cache/$cached", $escape_rule);
  654                     $nav_vars{DIRECTION} = "&laquo; <u>p</u>rev";
  655                     $nav_vars{ACCESSKEY} = "P";
  656                     $tpl_vars{BACK} = $templates{navpicture}->fill_in(HASH => \%nav_vars);
  657                 }
  658                 else {
  659                     $tpl_vars{BACK} = "&nbsp;";
  660                 }
  661 
  662                 $nextpicture = $pictures[$i+1];
  663                 if ($r->dir_config("GalleryWrapNavigation")) {
  664                     $nextpicture = $pictures[$i == $#pictures ? 0 : $i+1];
  665                 }   
  666 
  667                 if ($nextpicture) {
  668                     my ($orig_width, $orig_height, $type) = imgsize($path.$nextpicture);
  669                     my ($thumbnailwidth, $thumbnailheight) = get_thumbnailsize($r, $orig_width, $orig_height);  
  670                     my $imageinfo = get_imageinfo($r, $path.$nextpicture, $type, $thumbnailwidth, $thumbnailheight);
  671                     my $cached = get_scaled_picture_name($path.$nextpicture, $thumbnailwidth, $thumbnailheight);
  672                     my %nav_vars;
  673                     $nav_vars{URL}       = uri_escape($nextpicture, $escape_rule);
  674                     $nav_vars{FILENAME}  = $nextpicture;
  675                     $nav_vars{WIDTH}     = $width;
  676                     $nav_vars{PICTURE}   = uri_escape(".cache/$cached", $escape_rule);
  677                     $nav_vars{DIRECTION} = "<u>n</u>ext &raquo;";
  678                     $nav_vars{ACCESSKEY} = "N";
  679 
  680                     $tpl_vars{NEXT} = $templates{navpicture}->fill_in(HASH => \%nav_vars);
  681                     $tpl_vars{NEXTURL}   = uri_escape($nextpicture, $escape_rule);
  682                 }
  683                 else {
  684                     $tpl_vars{NEXT} = "&nbsp;";
  685                     $tpl_vars{NEXTURL}   = '#';
  686                 }
  687             }
  688         }
  689 
  690         my $foundcomment = 0;
  691         if (-f $path . '/' . $picfilename . '.comment') {
  692             my $comment_ref = get_comment($path . '/' . $picfilename . '.comment');
  693             $foundcomment = 1;
  694             $tpl_vars{COMMENT} = $comment_ref->{COMMENT} . '<br />' if $comment_ref->{COMMENT};
  695             $tpl_vars{TITLE} = $comment_ref->{TITLE} if $comment_ref->{TITLE};
  696         } elsif ($r->dir_config('GalleryCommentExifKey')) {
  697             my $comment = decode("utf8", $imageinfo->{$r->dir_config('GalleryCommentExifKey')});
  698             $tpl_vars{COMMENT} = encode("iso-8859-1", $comment);
  699         } else {
  700             $tpl_vars{COMMENT} = '';
  701         }
  702 
  703         my @infos = split /, /, $r->dir_config('GalleryInfo') ? $r->dir_config('GalleryInfo') : 'Picture Taken => DateTimeOriginal, Flash => Flash';
  704         my $foundinfo = 0;
  705         my $exifvalues;
  706         foreach (@infos) {
  707     
  708             my ($human_key, $exif_key) = (split " => ")[0,1];
  709             my $value = $imageinfo->{$human_key};
  710             if (defined($value)) {
  711 
  712                 $foundinfo = 1;
  713 
  714                 if ($exif_mode eq 'namevalue') {
  715                     my %info_vars;
  716                     $info_vars{KEY} = $human_key;
  717                     $info_vars{VALUE} = $value;
  718                     $tpl_vars{INFO} .=  $templates{info}->fill_in(HASH => \%info_vars);
  719                 }
  720 
  721                 if ($exif_mode eq 'variables') {
  722                     $tpl_vars{"EXIF_".uc($exif_key)} = $value;
  723                 }
  724 
  725                 if ($exif_mode eq 'values') {
  726                     $exifvalues .= "| ".$value." ";
  727                 }
  728 
  729             } 
  730 
  731         }
  732 
  733         if ($exif_mode eq 'values') {
  734             if (defined($exifvalues)) {
  735                 $tpl_vars{EXIFVALUES} = $exifvalues;
  736             }
  737             else {
  738                 $tpl_vars{EXIFVALUES} = "";
  739             }
  740         }
  741 
  742         if ($foundcomment and !$foundinfo) {
  743             $tpl_vars{INFO} = "";
  744         }
  745 
  746         if ($exif_mode ne 'namevalue') {
  747             $tpl_vars{INFO} = "";
  748         }
  749 
  750         if ($exif_mode eq 'namevalue' && $foundinfo or $foundcomment) {
  751 
  752             $tpl_vars{PICTUREINFO} = $templates{pictureinfo}->fill_in(HASH => \%tpl_vars);
  753 
  754             unless (defined($exifvalues)) {
  755                 $tpl_vars{EXIFVALUES} = "";
  756             }
  757 
  758         }
  759         else {
  760             $tpl_vars{PICTUREINFO} = $templates{nopictureinfo}->fill_in(HASH => \%tpl_vars);
  761         }
  762 
  763         # Fill in sizes and determine if any are smaller than the
  764         # actual image. If they are, $scaleable=1
  765         my $scaleable = 0;
  766         my @sizes = split (/ /, $r->dir_config('GallerySizes') ? $r->dir_config('GallerySizes') : '640 800 1024 1600');
  767         foreach my $size (@sizes) {
  768             if ($size<=$original_size) {
  769                 my %sizes_vars;
  770                 $sizes_vars{IMAGEURI} = uri_escape($r->uri(), $escape_rule);
  771                 $sizes_vars{SIZE}     = $size;
  772                 $sizes_vars{WIDTH}    = $size;
  773                 if ($width == $size) {
  774                     $tpl_vars{SIZES} .= $templates{scaleactive}->fill_in(HASH => \%sizes_vars);
  775                 }
  776                 else {
  777                     $tpl_vars{SIZES} .= $templates{scale}->fill_in(HASH => \%sizes_vars);
  778                 }
  779                 $scaleable = 1;
  780             }
  781         }
  782 
  783         unless ($scaleable) {
  784             my %sizes_vars;
  785             $sizes_vars{IMAGEURI} = uri_escape($r->uri(), $escape_rule);
  786             $sizes_vars{SIZE}     = $original_size;
  787             $sizes_vars{WIDTH}    = $original_size;
  788             $tpl_vars{SIZES} .= $templates{scaleactive}->fill_in(HASH => \%sizes_vars);
  789         }
  790 
  791         $tpl_vars{IMAGEURI} = uri_escape($r->uri(), $escape_rule);
  792 
  793         if ($r->dir_config('GalleryAllowOriginal')) {
  794             $tpl_vars{SIZES} .= $templates{orig}->fill_in(HASH => \%tpl_vars);
  795         }
  796 
  797         my @slideshow_intervals = split (/ /, $r->dir_config('GallerySlideshowIntervals') ? $r->dir_config('GallerySlideshowIntervals') : '3 5 10 15 30');
  798         foreach my $interval (@slideshow_intervals) {
  799 
  800             my %slideshow_vars;
  801             $slideshow_vars{IMAGEURI} = uri_escape($r->uri(), $escape_rule);
  802             $slideshow_vars{SECONDS} = $interval;
  803             $slideshow_vars{WIDTH} = ($width > $height ? $width : $height);
  804 
  805             if ($cgi->param('slideshow') && $cgi->param('slideshow') == $interval and $nextpicture) {
  806                 $tpl_vars{SLIDESHOW} .= $templates{intervalactive}->fill_in(HASH => \%slideshow_vars);
  807             }
  808             else {
  809 
  810                 $tpl_vars{SLIDESHOW} .= $templates{interval}->fill_in(HASH => \%slideshow_vars);
  811 
  812             }
  813         }
  814 
  815         if ($cgi->param('slideshow') and $nextpicture) {
  816 
  817             $tpl_vars{SLIDESHOW} .= $templates{slideshowoff}->fill_in(HASH => \%tpl_vars);
  818 
  819             unless ((grep $cgi->param('slideshow') == $_, @slideshow_intervals)) {
  820                 show_error($r, 200, "Invalid interval", "Invalid slideshow interval choosen");
  821                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
  822             }
  823 
  824             $tpl_vars{URL} = uri_escape($nextpicture, $escape_rule);
  825             $tpl_vars{WIDTH} = ($width > $height ? $width : $height);
  826             $tpl_vars{INTERVAL} = $cgi->param('slideshow');
  827             $tpl_vars{META} .=  $templates{refresh}->fill_in(HASH => \%tpl_vars);
  828 
  829         }
  830         else {
  831             $tpl_vars{SLIDESHOW} .=  $templates{slideshowisoff}->fill_in(HASH => \%tpl_vars);
  832         }
  833 
  834         $tpl_vars{MAIN} = $templates{picture}->fill_in(HASH => \%tpl_vars);
  835         $tpl_vars{MAIN} = $templates{layout}->fill_in(HASH => \%tpl_vars);
  836 
  837         $r->content_type('text/html');
  838         $r->headers_out->{'Content-Length'} = length($tpl_vars{MAIN});
  839 
  840         if (!$::MP2) {
  841             $r->send_http_header;
  842         }
  843 
  844         $r->print($tpl_vars{MAIN});
  845         return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
  846 
  847     }
  848 
  849 }
  850 
  851 sub cache_dir {
  852 
  853     my ($r, $strip_filename) = @_;
  854 
  855     my $cache_root;
  856 
  857     unless ($r->dir_config('GalleryCacheDir')) {
  858 
  859         $cache_root = '/var/cache/www/';
  860         if ($r->server->is_virtual) {
  861             $cache_root = File::Spec->catdir($cache_root, $r->server->server_hostname);
  862         } else {
  863             $cache_root = File::Spec->catdir($cache_root, $r->location);
  864         }
  865 
  866     } else {
  867 
  868         $cache_root = $r->dir_config('GalleryCacheDir');
  869 
  870     }
  871 
  872     # If the uri contains .cache we need to remove it
  873     my $uri = $r->uri;
  874     $uri =~ s/\.cache//;
  875 
  876     my (undef, $dirs, $filename) = File::Spec->splitpath($uri);
  877     # We don't need a volume as this is a relative path
  878 
  879     if ($strip_filename) {
  880         return(File::Spec->canonpath(File::Spec->catdir($cache_root, $dirs)));
  881     } else {
  882         return(File::Spec->canonpath(File::Spec->catfile($cache_root, $dirs, $filename)));
  883     }
  884 }
  885 
  886 sub create_cache {
  887 
  888     my ($r, $path) = @_;
  889 
  890         unless (mkdirhier ($path)) {
  891             show_error($r, 500, $!, "Unable to create cache directory in $path: $!");
  892             return 0;
  893         }
  894 
  895     return 1;
  896 }
  897 
  898 sub mkdirhier {
  899 
  900     my $dir = shift;
  901 
  902     unless (-d $dir) {
  903 
  904         unless (mkdir($dir, 0755)) {
  905             my $parent = $dir;
  906             $parent =~ s/\/[^\/]*$//;
  907 
  908             mkdirhier($parent);
  909 
  910             mkdir($dir, 0755);
  911         }
  912     }
  913 }
  914 
  915 sub get_scaled_picture_name {
  916 
  917     my ($fullpath, $width, $height) = @_;
  918 
  919     my (undef, undef, $type) = imgsize($fullpath);
  920 
  921     my @dirs = split(/\//, $fullpath);
  922     my $filename = pop(@dirs);
  923     my $newfilename;
  924 
  925     if (grep $type eq $_, qw(PPM TIF GIF)) {
  926         $newfilename = $width."x".$height."-".$filename;
  927         # needs to be configurable
  928         $newfilename =~ s/\.(\w+)$/-$1\.jpg/;
  929     } else {
  930         $newfilename = $width."x".$height."-".$filename;
  931     }
  932 
  933     return $newfilename;
  934     
  935 }
  936 
  937 sub scale_picture {
  938 
  939     my ($r, $fullpath, $width, $height, $imageinfo) = @_;
  940 
  941     my @dirs = split(/\//, $fullpath);
  942     my $filename = pop(@dirs);
  943 
  944     my ($orig_width, $orig_height, $type) = imgsize($fullpath);
  945 
  946     my $cache = cache_dir($r, 1);
  947 
  948     my $newfilename = get_scaled_picture_name($fullpath, $width, $height);
  949 
  950     if (($width > $orig_width) && ($height > $orig_height)) {
  951         # Run it through the resize code anyway to get watermarks
  952         $width = $orig_width;
  953         $height = $orig_height;
  954     }
  955 
  956     my ($thumbnailwidth, $thumbnailheight) = get_thumbnailsize($r, $orig_width, $orig_height);
  957 
  958     # Do we want to generate a new file in the cache?
  959     my $scale = 1;
  960 
  961     if (-f $cache."/".$newfilename) {   
  962         $scale = 0;
  963 
  964         # Check to see if the image has changed
  965         my $filestat = stat($fullpath);
  966         my $cachestat = stat($cache."/".$newfilename);
  967         if ($filestat->mtime >= $cachestat->mtime) {
  968             $scale = 1;
  969         }   
  970 
  971         # Check to see if the .rotate file has been added or changed
  972         if (-f $fullpath . ".rotate") {
  973             my $rotatestat = stat($fullpath . ".rotate");
  974             if ($rotatestat->mtime > $cachestat->mtime) {
  975                 $scale = 1;
  976             }   
  977         }       
  978         # Check to see if the copyrightimage has been added or changed
  979         if ($r->dir_config('GalleryCopyrightImage') && -f $r->dir_config('GalleryCopyrightImage')) {
  980             unless ($width == $thumbnailwidth or $width == $thumbnailheight) {
  981                 my $copyrightstat = stat($r->dir_config('GalleryCopyrightImage'));
  982                 if ($copyrightstat->mtime > $cachestat->mtime) {
  983                     $scale = 1;
  984                 }   
  985             }
  986         }   
  987 
  988     }   
  989 
  990     if ($scale) {
  991 
  992         my $newpath = $cache."/".$newfilename;
  993         my $rotate = readfile_getnum($r, $imageinfo, $fullpath . ".rotate");
  994         my $quality = $r->dir_config('GalleryQuality');
  995 
  996         if ($width == $thumbnailwidth or $width == $thumbnailheight) {
  997 
  998             resizepicture($r, $fullpath, $newpath, $width, $height, $rotate, '', '', '', '', '', '');
  999 
 1000         } else {
 1001 
 1002             resizepicture($r, $fullpath, $newpath, $width, $height, $rotate, 
 1003                 ($r->dir_config('GalleryCopyrightImage') ? $r->dir_config('GalleryCopyrightImage') : ''), 
 1004                 ($r->dir_config('GalleryTTFDir') ? $r->dir_config('GalleryTTFDir') : ''), 
 1005                 ($r->dir_config('GalleryCopyrightText') ? $r->dir_config('GalleryCopyrightText') : ''), 
 1006                 ($r->dir_config('GalleryCopyrightColor') ? $r->dir_config('GalleryCopyrightColor') : ''), 
 1007                 ($r->dir_config('GalleryTTFFile') ? $r->dir_config('GalleryTTFFile') : ''), 
 1008                 ($r->dir_config('GalleryTTFSize') ?  $r->dir_config('GalleryTTFSize') : ''),
 1009                 ($r->dir_config('GalleryCopyrightBackgroundColor') ?  $r->dir_config('GalleryCopyrightBackgroundColor') : ''),
 1010                 $quality);
 1011 
 1012         }
 1013     }
 1014 
 1015     return $newfilename;
 1016 
 1017 }
 1018 
 1019 sub get_thumbnailsize {
 1020     my ($r, $orig_width, $orig_height) = @_;
 1021 
 1022     my $gallerythumbnailsize=$r->dir_config('GalleryThumbnailSize');
 1023 
 1024     if (defined($gallerythumbnailsize)) {
 1025         warn("Invalid setting for GalleryThumbnailSize") unless
 1026             $gallerythumbnailsize =~ /^\s*\d+\s*x\s*\d+\s*$/i;
 1027     }
 1028 
 1029     my ($thumbnailwidth, $thumbnailheight) = split(/x/i, ($gallerythumbnailsize) ?  $gallerythumbnailsize : "100x75");
 1030 
 1031     my $width = $thumbnailwidth;
 1032     my $height = $thumbnailheight;
 1033 
 1034     # If the image is rotated, flip everything around.
 1035     if (defined $r->dir_config('GalleryThumbnailSizeLS')
 1036     and $r->dir_config('GalleryThumbnailSizeLS') eq '1'
 1037     and $orig_width < $orig_height) {
 1038         
 1039         $width = $thumbnailheight;
 1040         $height = $thumbnailwidth;
 1041     }
 1042 
 1043     my $scale = ($orig_width ? $width/$orig_width : 1);
 1044 
 1045     if ($orig_height) {
 1046         if ($orig_height * $scale > $thumbnailheight) {
 1047             $scale = $height/$orig_height;
 1048             $width = $orig_width * $scale;
 1049         }
 1050     }
 1051 
 1052     $height = $orig_height * $scale;
 1053 
 1054     $height = floor($height);
 1055     $width  = floor($width);
 1056 
 1057     return ($width, $height);
 1058 }
 1059 
 1060 sub get_image_display_size {
 1061     my ($cgi, $r, $orig_width, $orig_height) = @_;
 1062 
 1063     my $width = $orig_width;
 1064 
 1065     my $original_size=$orig_height;
 1066     if ($orig_width>$orig_height) {
 1067         $original_size=$orig_width;
 1068     }
 1069 
 1070     # Check if the selected width is allowed
 1071     my @sizes = split (/ /, $r->dir_config('GallerySizes') ? $r->dir_config('GallerySizes') : '640 800 1024 1600');
 1072 
 1073     my %cookies = fetch CGI::Cookie;
 1074 
 1075     if ($cgi->param('width')) {
 1076         unless ((grep $cgi->param('width') == $_, @sizes) or ($cgi->param('width') == $original_size)) {
 1077             show_error($r, 200, "Invalid width", "The specified width is invalid");
 1078             return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
 1079         }
 1080 
 1081         $width = $cgi->param('width');
 1082         my $cookie = new CGI::Cookie(-name => 'GallerySize', -value => $width, -expires => '+6M');
 1083         $r->headers_out->{'Set-Cookie'} = $cookie;
 1084 
 1085     } elsif ($cookies{'GallerySize'} && (grep $cookies{'GallerySize'}->value == $_, @sizes)) {
 1086 
 1087         $width = $cookies{'GallerySize'}->value;
 1088 
 1089     } else {
 1090         $width = $sizes[0];
 1091     }   
 1092 
 1093     my $scale;
 1094     my $image_width;
 1095     if ($orig_width<$orig_height) {
 1096         $scale = ($orig_height ? $width/$orig_height: 1);
 1097         $image_width=$width*$orig_width/$orig_height;
 1098     }
 1099     else {
 1100         $scale = ($orig_width ? $width/$orig_width : 1);
 1101         $image_width = $width;
 1102     }
 1103 
 1104     my $height = $orig_height * $scale;
 1105 
 1106     $image_width = floor($image_width);
 1107     $width       = floor($width);
 1108     $height      = floor($height);
 1109 
 1110     return ($image_width, $width, $height, $original_size);
 1111 }
 1112 
 1113 sub get_imageinfo {
 1114     my ($r, $file, $type, $width, $height) = @_;
 1115     my $imageinfo = {};
 1116     if ($type eq 'Data stream is not a known image file format') {
 1117         # should never be reached, this is supposed to be handled outside of here
 1118         log_error("Something was fishy with the type of the file $file\n");
 1119     } else { 
 1120 
 1121         # Some files, like TIFF, PNG, GIF do not have EXIF info 
 1122         # embedded but use .thm files instead.
 1123         $imageinfo = get_imageinfo_from_thm_file($file, $width, $height);
 1124 
 1125         # If there is no .thm file and our file is a JPEG file we try to extract the EXIf
 1126         # info using Image::Info
 1127         unless (defined($imageinfo) && (grep $type eq $_, qw(JPG))) {
 1128             # Only for files that natively keep the EXIF info in the same file
 1129             $imageinfo = image_info($file);
 1130         }
 1131     }
 1132 
 1133     unless (defined($imageinfo->{width}) and defined($imageinfo->{height})) {
 1134         $imageinfo->{width} = $width;
 1135         $imageinfo->{height} = $height;
 1136     }
 1137 
 1138     my @infos = split /, /, $r->dir_config('GalleryInfo') ? $r->dir_config('GalleryInfo') : 'Picture Taken => DateTimeOriginal, Flash => Flash';
 1139     foreach (@infos) {
 1140         
 1141         my ($human_key, $exif_key) = (split " => ")[0,1];
 1142         if (defined($exif_key) && defined($imageinfo->{$exif_key})) {
 1143             my $value = "";
 1144             if (ref($imageinfo->{$exif_key}) eq 'Image::TIFF::Rational') { 
 1145                 $value = $imageinfo->{$exif_key}->as_string;
 1146             } 
 1147             elsif (ref($imageinfo->{$exif_key}) eq 'ARRAY') {
 1148                 foreach my $element (@{$imageinfo->{$exif_key}}) {
 1149                     if (ref($element) eq 'ARRAY') {
 1150                         foreach (@{$element}) {
 1151                             $value .= $_ . ' ';
 1152                         }
 1153                     } 
 1154                     elsif (ref($element) eq 'HASH') {
 1155                         $value .= "<br />{ ";
 1156                         foreach (sort keys %{$element}) {
 1157                             $value .= "$_ = " . $element->{$_} . ' ';
 1158                         }
 1159                         $value .= "} ";
 1160                     } 
 1161                     else {
 1162                         $value .= $element;
 1163                     }
 1164                     $value .= ' ';
 1165                 }
 1166             } 
 1167             else {
 1168                 my $exif_value = $imageinfo->{$exif_key};
 1169                 if ($human_key eq 'Flash' && $exif_value =~ m/\d/) {
 1170                     my %flashmodes = (
 1171                         "0"  => "No",
 1172                         "1"  => "Yes",
 1173                         "9"  => "Yes",
 1174                         "16" => "No (Compulsory) Should be External Flash",
 1175                         "17" => "Yes (External)",
 1176                         "24" => "No",
 1177                         "25" => "Yes (Auto)",
 1178                         "73" => "Yes (Compulsory, Red Eye Reducing)",
 1179                         "89" => "Yes (Auto, Red Eye Reducing)"
 1180                     );
 1181                     $exif_value = defined $flashmodes{$exif_value} ? $flashmodes{$exif_value} : 'unknown flash mode';
 1182                 }
 1183                 $value = $exif_value;
 1184             }
 1185             if ($exif_key eq 'MeteringMode') {
 1186                 my $exif_value = $imageinfo->{$exif_key};
 1187                 if ($exif_value =~ /^\d+$/) {
 1188                     my %meteringmodes = (
 1189                         '0' => 'unknown',
 1190                         '1' => 'Average',
 1191                         '2' => 'CenterWeightedAverage',
 1192                         '3' => 'Spot',
 1193                         '4' => 'MultiSpot',
 1194                         '5' => 'Pattern',
 1195                         '6' => 'Partial',
 1196                         '255' => 'Other'
 1197                     );
 1198                     $exif_value = defined $meteringmodes{$exif_value} ? $meteringmodes{$exif_value} : 'unknown metering mode';
 1199                 }
 1200                 $value = $exif_value;
 1201                 
 1202             }
 1203             if ($exif_key eq 'LightSource') {
 1204                 my $exif_value = $imageinfo->{$exif_key};
 1205                 if ($exif_value =~ /^\d+$/) {
 1206                     my %lightsources = (
 1207                         '0' => 'unknown',
 1208                         '1' => 'Daylight',
 1209                         '2' => 'Fluorescent',
 1210                         '3' => 'Tungsten (incandescent light)',
 1211                         '4' => 'Flash',
 1212                         '9' => 'Fine weather',
 1213                         '10' => 'Cloudy weather',
 1214                         '11' => 'Shade',
 1215                         '12' => 'Daylight fluorescent',
 1216                         '13' => 'Day white fluorescent',
 1217                         '14' => 'Cool white fluorescent',
 1218                         '15' => 'White fluorescent',
 1219                         '17' => 'Standard light A',
 1220                         '18' => 'Standard light B',
 1221                         '19' => 'Standard light C',
 1222                         '20' => 'D55',
 1223                         '21' => 'D65',
 1224                         '22' => 'D75',
 1225                         '23' => 'D50',
 1226                         '24' => 'ISO studio tungsten',
 1227                         '255' => 'other light source'
 1228                     );
 1229                     $exif_value = defined $lightsources{$exif_value} ? $lightsources{$exif_value} : 'unknown light source';
 1230                 }
 1231                 $value = $exif_value;
 1232             }
 1233             if ($exif_key eq 'FocalLength') {
 1234                 if ($value =~ /^(\d+)\/(\d+)$/) {
 1235                     $value = eval { $1 / $2 };
 1236                     if ($@) {
 1237                         $value = $@;
 1238                     } else {
 1239                         $value = int($value + 0.5) . "mm";
 1240 
 1241                     }
 1242                 }
 1243             }
 1244             if ($exif_key eq 'ShutterSpeedValue') {
 1245                 if ($value =~ /^((?:\-)?\d+)\/(\d+)$/) {
 1246                     $value = eval { $1 / $2 };
 1247                     if ($@) {
 1248                         $value = $@;
 1249                     } else {
 1250                         eval {
 1251                             $value = 1/(exp($value*log(2)));
 1252                             if ($value < 1) {
 1253                                 $value = "1/" . (int((1/$value)));
 1254                             } else {
 1255                                 $value = int($value*10)/10; 
 1256                             }
 1257                         };
 1258                         if ($@) {
 1259                             $value = $@;
 1260                         } else {
 1261                             $value = $value . " sec";
 1262                         }
 1263                     }
 1264                 }
 1265             }
 1266             if ($exif_key eq 'ApertureValue') {
 1267                 if ($value =~ /^(\d+)\/(\d+)$/) {
 1268                     $value = eval { $1 / $2 };
 1269                     if ($@) {
 1270                         $value = $@;
 1271                     } else {
 1272                         # poor man's rounding
 1273                         $value = int(exp($value*log(2)*0.5)*10)/10;
 1274                         $value = "f" . $value;
 1275                     }
 1276                 }
 1277             }
 1278             if ($exif_key eq 'FNumber') {
 1279                 if ($value =~ /^(\d+)\/(\d+)$/) {
 1280                     $value = eval { $1 / $2 };
 1281                     if ($@) {
 1282                         $value = $@;
 1283                     } else {
 1284                         $value = int($value*10+0.5)/10;
 1285                         $value = "f" . $value;
 1286                     }
 1287                 }
 1288             }
 1289             $imageinfo->{$human_key} = $value;
 1290         } 
 1291     }
 1292 
 1293     if ($r->dir_config('GalleryUseFileDate') &&
 1294         ($r->dir_config('GalleryUseFileDate') eq '1'
 1295         || !$imageinfo->{"Picture Taken"} )) {
 1296 
 1297         my $st = stat($file);
 1298         $imageinfo->{"DateTimeOriginal"} = $imageinfo->{"Picture Taken"} = scalar localtime($st->mtime) if $st;
 1299     }
 1300 
 1301     return $imageinfo;
 1302 }
 1303 
 1304 sub get_imageinfo_from_thm_file {
 1305 
 1306     my ($file, $width, $height) = @_;
 1307 
 1308     my $imageinfo = undef;
 1309     # Windows based file extensions are often .THM, so check 
 1310     # for both .thm and .THM
 1311     my $unix_file = $file;
 1312     my $windows_file = $file;
 1313     $unix_file =~ s/\.(\w+)$/.thm/;
 1314     $windows_file =~ s/\.(\w+)$/.THM/;
 1315 
 1316     if (-e $unix_file && -f $unix_file && -r $unix_file) {
 1317         $imageinfo = image_info($unix_file);
 1318         $imageinfo->{width} = $width;
 1319         $imageinfo->{height} = $height;
 1320     }
 1321     elsif (-e $windows_file && -f $windows_file && -r $windows_file) {
 1322         $imageinfo = image_info($windows_file);
 1323         $imageinfo->{width} = $width;
 1324         $imageinfo->{height} = $height;
 1325     }
 1326 
 1327     return $imageinfo;
 1328 }
 1329 
 1330 
 1331 sub readfile_getnum {
 1332     my ($r, $imageinfo, $filename) = @_;
 1333 
 1334     my $rotate = 0;
 1335 
 1336     print STDERR "orientation: ".$imageinfo->{Orientation}."\n";
 1337     # Check to see if the image contains the Orientation EXIF key,
 1338     # but allow user to override using rotate
 1339     if (!defined($r->dir_config("GalleryAutoRotate")) 
 1340         || $r->dir_config("GalleryAutoRotate") eq "1") {
 1341         if (defined($imageinfo->{Orientation})) {
 1342             print STDERR $imageinfo->{Orientation}."\n";
 1343             if ($imageinfo->{Orientation} eq 'right_top') {
 1344                 $rotate=1;
 1345             }   
 1346             elsif ($imageinfo->{Orientation} eq 'left_bot') {
 1347                 $rotate=3;
 1348             }
 1349         }
 1350     }
 1351 
 1352     if (open(FH, "<$filename")) {
 1353         my $temp = <FH>;
 1354         chomp($temp);
 1355         close(FH);
 1356         unless ($temp =~ /^\d$/) {
 1357             $rotate = 0;
 1358         }
 1359         unless ($temp == 1 || $temp == 2 || $temp == 3) {
 1360             $rotate = 0;
 1361         }
 1362         $rotate = $temp;
 1363     }
 1364 
 1365     return $rotate;
 1366 }
 1367 
 1368 sub get_filecontent {
 1369     my $file = shift;
 1370     open(FH, $file) or return undef;
 1371     my $content = '';
 1372     {
 1373         local $/;
 1374         $content = <FH>;
 1375     }
 1376     close(FH);
 1377     return $content;
 1378 }
 1379 
 1380 sub get_comment {
 1381     my $filename = shift;
 1382     my $comment_ref = {};
 1383     $comment_ref->{TITLE} = undef;
 1384     $comment_ref->{COMMENT} = '';
 1385 
 1386     open(FH, $filename) or return $comment_ref;
 1387     my $title = <FH>;
 1388     if ($title =~ m/^TITLE: (.*)$/) {
 1389         chomp($comment_ref->{TITLE} = $1);
 1390     } 
 1391     else {
 1392         $comment_ref->{COMMENT} = $title;
 1393     }
 1394 
 1395     while (<FH>) {
 1396         chomp;
 1397         $comment_ref->{COMMENT} .= $_;
 1398     }
 1399     close(FH);
 1400 
 1401     return $comment_ref;
 1402 }
 1403 
 1404 sub show_error {
 1405 
 1406     my ($r, $statuscode, $errortitle, $error) = @_;
 1407 
 1408     my $tpl = $r->dir_config('GalleryTemplateDir');
 1409 
 1410     my %templates = create_templates({layout => "$tpl/layout.tpl",
 1411                       error  => "$tpl/error.tpl",
 1412                      });
 1413 
 1414     my %tpl_vars;
 1415     $tpl_vars{TITLE}      = "Error! $errortitle";
 1416     $tpl_vars{META}       = "";
 1417     $tpl_vars{ERRORTITLE} = "Error! $errortitle";
 1418     $tpl_vars{ERROR}      = $error;
 1419 
 1420     $tpl_vars{MAIN} = $templates{error}->fill_in(HASH => \%tpl_vars);
 1421 
 1422     $tpl_vars{PAGE} = $templates{layout}->fill_in(HASH => \%tpl_vars);
 1423 
 1424     $r->status($statuscode);
 1425     $r->content_type('text/html');
 1426 
 1427     $r->print($tpl_vars{PAGE});
 1428 
 1429 }
 1430 
 1431 sub generate_menu {
 1432 
 1433     my $r = shift;
 1434 
 1435     my $root_text = (defined($r->dir_config('GalleryRootText')) ? $r->dir_config('GalleryRootText') : "root:" );
 1436     my $root_path = (defined($r->dir_config('GalleryRootPath')) ? $r->dir_config('GalleryRootPath') : "" );
 1437 
 1438     my $subr = $r->lookup_uri($r->uri);
 1439     my $filename = $subr->filename;
 1440 
 1441     my @links = split (/\//, $r->uri);
 1442     my $uri = $r->uri;
 1443     $uri =~ s/^$root_path//g;
 1444 
 1445     @links = split (/\//, $uri);
 1446 
 1447     # Get the full path of the base directory
 1448     my $dirname;
 1449     {
 1450         my @direlem = split (/\//, $filename);
 1451         for my $i ( 0 .. ( scalar(@direlem) - scalar(@links) ) ) {
 1452             $dirname .= shift(@direlem) . '/';
 1453         }
 1454         chop $dirname;
 1455     }
 1456 
 1457     my $picturename;
 1458     if (-f $filename) {
 1459         $picturename = pop(@links); 
 1460     }
 1461 
 1462     if ($r->uri eq $root_path) {
 1463         return qq{ <a href="$root_path">$root_text</a> };
 1464     }
 1465 
 1466     my $menu;
 1467     my $menuurl = $root_path;
 1468     foreach my $link (@links) {
 1469 
 1470         $menuurl .= $link."/";
 1471         my $linktext = $link;
 1472         unless (length($link)) {
 1473             $linktext = "$root_text ";
 1474         }
 1475         else {
 1476             
 1477             $dirname = File::Spec->catdir($dirname, $link);
 1478 
 1479             if (-e $dirname . ".folder") {
 1480                 $linktext = get_filecontent($dirname . ".folder");
 1481             }
 1482         }
 1483 
 1484         if ("$root_path$uri" eq $menuurl) {
 1485             $menu .= "$linktext  / ";
 1486         }
 1487         else {
 1488             $menu .= "<a href=\"".uri_escape($menuurl, $escape_rule)."\">$linktext</a> / ";
 1489         }
 1490 
 1491     }
 1492 
 1493     if (-f $filename) {
 1494         $menu .= $picturename;
 1495     }
 1496     else {
 1497 
 1498         if ($r->dir_config('GallerySelectionMode') && $r->dir_config('GallerySelectionMode') eq '1') {
 1499             $menu .= "<a href=\"".uri_escape($menuurl, $escape_rule);
 1500             $menu .= "?select=1\">[select]</a> ";
 1501         }
 1502     }
 1503 
 1504     return $menu;
 1505 }
 1506 
 1507 sub resizepicture {
 1508     my ($r, $infile, $outfile, $x, $y, $rotate, $copyrightfile, $GalleryTTFDir, $GalleryCopyrightText, $text_color, $GalleryTTFFile, $GalleryTTFSize, $GalleryCopyrightBackgroundColor, $quality) = @_;
 1509 
 1510     # Load image
 1511     my $image = Image::Imlib2->load($infile) or warn("Unable to open file $infile, $!");
 1512 
 1513     # Scale image
 1514     $image=$image->create_scaled_image($x, $y) or warn("Unable to scale image $infile. Are you running out of memory?");
 1515 
 1516     # Rotate image
 1517     if ($rotate != 0) {
 1518         $image->image_orientate($rotate);
 1519     }
 1520 
 1521     # blend copyright image onto image
 1522     if ($copyrightfile ne '') {
 1523         if (-f $copyrightfile and (my $logo=Image::Imlib2->load($copyrightfile))) {
 1524             my $x = $image->get_width();
 1525             my $y = $image->get_height();
 1526             my $logox = $logo->get_width();
 1527             my $logoy = $logo->get_height();
 1528             $image->blend($logo, 0, 0, 0, $logox, $logoy, $x-$logox, $y-$logoy, $logox, $logoy);
 1529         }
 1530         else {
 1531             log_error("GalleryCopyrightImage $copyrightfile was not found");
 1532         }
 1533     }
 1534 
 1535     if ($GalleryTTFDir && $GalleryCopyrightText && $GalleryTTFFile && $text_color) {
 1536         if (!-d $GalleryTTFDir) {
 1537 
 1538             log_error("GalleryTTFDir $GalleryTTFDir is not a dir\n");
 1539 
 1540         } elsif ($GalleryCopyrightText eq '') {
 1541 
 1542             log_error("GalleryCopyrightText is empty. No text inserted to picture\n");
 1543 
 1544         } elsif (!-e "$GalleryTTFDir/$GalleryTTFFile") {
 1545 
 1546             log_error("GalleryTTFFile $GalleryTTFFile was not found\n");
 1547 
 1548         } else {
 1549  
 1550             $GalleryTTFFile =~ s/\.TTF$//i;
 1551             $image->add_font_path("$GalleryTTFDir");
 1552 
 1553             $image->load_font("$GalleryTTFFile/$GalleryTTFSize");
 1554             my($text_x, $text_y) = $image->get_text_size("$GalleryCopyrightText");
 1555             my $x = $image->get_width();
 1556             my $y = $image->get_height();
 1557 
 1558             my $offset = 3;
 1559 
 1560             if (($text_x < $x - $offset) && ($text_y < $y - $offset)) {
 1561                 if ($GalleryCopyrightBackgroundColor =~ /^\d+,\d+,\d+,\d+$/) {
 1562                     my ($br_val, $bg_val, $bb_val, $ba_val) = split (/,/, $GalleryCopyrightBackgroundColor);
 1563                     $image->set_colour($br_val, $bg_val, $bb_val, $ba_val);
 1564                     $image->fill_rectangle ($x-$text_x-$offset, $y-$text_y-$offset, $text_x, $text_y);
 1565                 }
 1566                 my ($r_val, $g_val, $b_val, $a_val) = split (/,/, $text_color);
 1567                 $image->set_colour($r_val, $g_val, $b_val, $a_val);
 1568                 $image->draw_text($x-$text_x-$offset, $y-$text_y-$offset, "$GalleryCopyrightText");
 1569             } else {
 1570                 log_error("Text is to big for the picture.\n");
 1571             }
 1572         }
 1573     }
 1574 
 1575     if ($quality && $quality =~ m/^\d+$/) {
 1576         $image->set_quality($quality);
 1577     }
 1578 
 1579     $image->save($outfile);
 1580 
 1581 }
 1582 
 1583 sub gallerysort {
 1584     my $r=shift;
 1585     my @files=@_;
 1586     my $sortby = $r->dir_config('GallerySortBy');
 1587     my $filename=$r->lookup_uri($r->uri)->filename;
 1588     $filename=(File::Spec->splitpath($filename))[1] if (-f $filename);
 1589     if ($sortby && $sortby =~ m/^(size|atime|mtime|ctime)$/) {
 1590         @files = map(/^\d+ (.*)/, sort map(stat("$filename/$_")->$sortby()." $_", @files));
 1591     } else {
 1592         @files = sort @files;
 1593     }
 1594     return @files;
 1595 }
 1596 
 1597 # Create Text::Template objects used by Apache::Gallery. Takes a
 1598 # hashref of template_name, template_filename pairs, and returns a
 1599 # list of template_name, texttemplate_object pairs.
 1600 sub create_templates {
 1601      my $templates = shift;
 1602 
 1603      # This routine is called whenever a template has an error. Prints
 1604      # the error to STDERR and sticks the error in the output
 1605      sub tt_broken {
 1606       my %args = @_;
 1607       # Pull out the name and filename from the arg option [see
 1608       # Text::Template for details]
 1609       @args{qw(name file)} = @{$args{arg}};
 1610       print STDERR qq(Template $args{name} ("$args{file}") is broken: $args{error});
 1611       # Don't include the file name in the output, as the user can see this.
 1612       return qq(<!-- Template $args{name} is broken: $args{error} -->);
 1613      }
 1614 
 1615 
 1616 
 1617      my %texttemplate_objects;
 1618 
 1619      for my $template_name (keys %$templates) {
 1620       my $tt_obj = Text::Template->new(TYPE   => 'FILE',
 1621                        SOURCE => $$templates{$template_name},
 1622                        BROKEN => \&tt_broken,
 1623                        BROKEN_ARG => [$template_name, $$templates{$template_name}],
 1624                       )
 1625            or die "Unable to create new Text::Template object for $template_name: $Text::Template::ERROR";
 1626       $texttemplate_objects{$template_name} = $tt_obj;
 1627      }
 1628      return %texttemplate_objects;
 1629 }
 1630 
 1631 sub log_error {
 1632     if ($::MP2) {
 1633         Apache2::RequestUtil->request->log_error(shift());
 1634     } else {
 1635         Apache->request->log_error(shift());
 1636     }
 1637 }
 1638 
 1639 1;
 1640 
 1641 =head1 NAME
 1642 
 1643 Apache::Gallery - mod_perl handler to create an image gallery
 1644 
 1645 =head1 SYNOPSIS
 1646 
 1647 See the INSTALL file in the distribution for installation instructions.
 1648 
 1649 =head1 DESCRIPTION
 1650 
 1651 Apache::Gallery creates an thumbnail index of each directory and allows 
 1652 viewing pictures in different resolutions. Pictures are resized on the 
 1653 fly and cached. The gallery can be configured and customized in many ways
 1654 and a custom copyright image can be added to all the images without
 1655 modifying the original.
 1656 
 1657 =head1 CONFIGURATION
 1658 
 1659 In your httpd.conf you set the global options for the gallery. You can
 1660 also override each of the options in .htaccess files in your gallery
 1661 directories.
 1662 
 1663 The options are set in the httpd.conf/.htaccess file using the syntax:
 1664 B<PerlSetVar OptionName 'value'>
 1665 
 1666 Example: B<PerlSetVar GalleryCacheDir '/var/cache/www/'>
 1667 
 1668 =over 4
 1669 
 1670 =item B<GalleryAutoRotate>
 1671 
 1672 Some cameras, like the Canon G3, can detect the orientation of a 
 1673 the pictures you take and will save this information in the 
 1674 'Orientation' EXIF field. Apache::Gallery will then automatically
 1675 rotate your images. 
 1676 
 1677 This behavior is default but can be disabled by setting GalleryAutoRotate
 1678 to 0.
 1679 
 1680 =item B<GalleryCacheDir>
 1681 
 1682 Directory where Apache::Gallery should create its cache with scaled
 1683 pictures. The default is /var/cache/www/ . Here, a directory for each
 1684 virtualhost or location will be created automatically. Make sure your
 1685 webserver has write access to the CacheDir.
 1686 
 1687 =item B<GalleryTemplateDir>
 1688 
 1689 Full path to the directory where you placed the templates. This option
 1690 can be used both in your global configuration and in .htaccess files,
 1691 this way you can have different layouts in different parts of your 
 1692 gallery.
 1693 
 1694 No default value, this option is required.
 1695 
 1696 =item B<GalleryInfo>
 1697 
 1698 With this option you can define which EXIF information you would like
 1699 to present from the image. The format is: '<MyName => KeyInEXIF, 
 1700 MyOtherName => OtherKeyInEXIF'
 1701 
 1702 Examples of keys: B<ShutterSpeedValue>, B<ApertureValue>, B<SubjectDistance>,
 1703 and B<Camera>
 1704 
 1705 You can view all the keys from the EXIF header using this perl-oneliner:
 1706 
 1707 perl C<-e> 'use Data::Dumper; use Image::Info qw(image_info); print Dumper(image_info(shift));' filename.jpg
 1708 
 1709 Default is: 'Picture Taken => DateTimeOriginal, Flash => Flash'
 1710 
 1711 =item B<GallerySizes>
 1712 
 1713 Defines which widths images can be scaled to. Images cannot be
 1714 scaled to other widths than the ones you define with this option.
 1715 
 1716 The default is '640 800 1024 1600'
 1717 
 1718 =item B<GalleryThumbnailSize>
 1719 
 1720 Defines the width and height of the thumbnail images. 
 1721 
 1722 Defaults to '100x75'
 1723 
 1724 =item B<GalleryThumbnailSizeLS>
 1725 
 1726 If set to '1', B<GalleryThumbnailSize> is the long and the short side of
 1727 the thumbnail image instead of the width and height.
 1728 
 1729 Defaults to '0'.
 1730 
 1731 =item B<GalleryCopyrightImage>
 1732 
 1733 Image you want to blend into your images in the lower right
 1734 corner. This could be a transparent png saying "copyright
 1735 my name 2001".
 1736 
 1737 Optional.
 1738 
 1739 =item B<GalleryWrapNavigation>
 1740 
 1741 Make the navigation in the picture view wrap around (So Next
 1742 at the end displays the first picture, etc.)
 1743 
 1744 Set to 1 or 0, default is 0
 1745 
 1746 =item B<GalleryAllowOriginal>
 1747 
 1748 Allow the user to download the Original picture without
 1749 resizing or putting the CopyrightImage on it.
 1750 
 1751 Set to 1 or 0, default is 0
 1752 
 1753 =item B<GallerySlideshowIntervals>
 1754 
 1755 With this option you can configure which intervals can be selected for
 1756 a slideshow. The default is '3 5 10 15 30'
 1757 
 1758 =item B<GallerySortBy>
 1759 
 1760 Instead of the default filename ordering you can sort by any
 1761 stat attribute. For example size, atime, mtime, ctime.
 1762 
 1763 =item B<GalleryDirSortBy>
 1764 
 1765 Set this variable to sort directories differently than other items,
 1766 can be set to size, atime, mtime and ctime; setting any other value
 1767 will revert to sorting by name.
 1768 
 1769 =item B<GalleryMemoize>
 1770 
 1771 Cache EXIF data using Memoize - this will make Apache::Gallery faster
 1772 when many people access the same images, but it will also cache EXIF
 1773 data until the current Apache child dies.
 1774 
 1775 =item B<GalleryUseFileDate>
 1776 
 1777 Set this option to 1 to make A::G show the files timestamp
 1778 instead of the EXIF value for "Picture taken".
 1779 
 1780 =item B<GallerySelectionMode>
 1781 
 1782 Enable the selection mode. Select images with checkboxes and
 1783 get a list of filenames. 
 1784 
 1785 =item B<GalleryEXIFMode>
 1786 
 1787 You can choose how Apache::Gallery should display EXIF info
 1788 from your images. 
 1789 
 1790 The default setting is 'namevalue'. This setting will make 
 1791 Apache::Gallery print out the names and values of the EXIF values 
 1792 you configure with GalleryInfo. The information will be parsed into 
 1793 $INFO in pictureinfo.tpl.  
 1794 
 1795 You can also set it to 'values' which will make A::G parse
 1796 the configured values into the var $EXIFVALUES as 'value | value | value'
 1797 
 1798 If you set this option to 'variables' the items you configure in GalleryInfo 
 1799 will be available to your templates as $EXIF_<KEYNAME> (in all uppercase). 
 1800 That means that with the default setting "Picture Taken => DateTimeOriginal, 
 1801 Flash => Flash" you will have the variables $EXIF_DATETIMEORIGINAL and 
 1802 $EXIF_FLASH available to your templates. You can place them
 1803 anywhere you want.
 1804 
 1805 =item B<GalleryRootPath>
 1806 
 1807 Change the location of gallery root. The default is ""
 1808 
 1809 =item B<GalleryRootText>
 1810 
 1811 Change the name that appears as the root element in the menu. The
 1812 default is "root:"
 1813 
 1814 =item B<GalleryMaxThumbnailsPerPage>
 1815 
 1816 This options controls how many thumbnails should be displayed in a 
 1817 page. It requires $BROWSELINKS to be in the index.tpl template file.
 1818 
 1819 =item B<GalleryImgFile>
 1820 
 1821 Pattern matching the files you want Apache::Gallery to view in the
 1822 index as thumbnails. 
 1823 
 1824 The default is '\.(jpe?g|png|tiff?|ppm)$'
 1825 
 1826 =item B<GalleryDocFile>
 1827 
 1828 Pattern matching the files you want Apache::Gallery to view in the index
 1829 as normal files. All other filetypes will still be served by Apache::Gallery
 1830 but are not visible in the index.
 1831 
 1832 The default is '\.(mpe?g|avi|mov|asf|wmv|doc|mp3|ogg|pdf|rtf|wav|dlt|txt|html?|csv|eps)$'
 1833 
 1834 =item B<GalleryTTFDir>
 1835 
 1836 To use the GalleryCopyrightText feature you must set this option to the
 1837 directory where your True Type fonts are stored. No default is set.
 1838 
 1839 Example:
 1840 
 1841     PerlSetVar      GalleryTTFDir '/usr/share/fonts/'
 1842 
 1843 =item B<GalleryTTFFile>
 1844 
 1845 To use the GalleryCopyrightText feature this option must be set to the
 1846 name of the True Type font you wish to use. Example:
 1847 
 1848     PerlSetVar      GalleryTTFFile 'verdanab.ttf'
 1849 
 1850 =item B<GalleryTTFSize>
 1851 
 1852 Configure the size of the CopyrightText that will be inserted as 
 1853 copyright notice in the corner of your pictures.
 1854 
 1855 Example:
 1856 
 1857     PerlSetVar      GalleryTTFSize '10'
 1858 
 1859 =item B<GalleryCopyrightText>
 1860 
 1861 The text that will be inserted as copyright notice.
 1862 
 1863 Example:
 1864 
 1865         PerlSetVar      GalleryCopyrightText '(c) Michael Legart'
 1866 
 1867 =item B<GalleryCopyrightColor>
 1868 
 1869 The text color of your copyright notice.
 1870 
 1871 Examples:
 1872 
 1873 White:
 1874         PerlSetVar      GalleryCopyrightColor '255,255,255,255'
 1875 
 1876 Black:
 1877         PerlSetVar      GalleryCopyrightColor '0,0,0,255'
 1878 
 1879 Red:
 1880         PerlSetVar      GalleryCopyrightColor '255,0,0,255'
 1881 
 1882 Green:
 1883         PerlSetVar      GalleryCopyrightColor '0,255,0,255'
 1884 
 1885 Blue:
 1886         PerlSetVar      GalleryCopyrightColor '0,0,255,255'
 1887 
 1888 Transparent orange:
 1889         PerlSetVar      GalleryCopyrightColor '255,127,0,127'
 1890 
 1891 =item B<GalleryCopyrightBackgroundColor>
 1892 
 1893 The background-color of a GalleryCopyrightText
 1894 
 1895 r,g,b,a - for examples, see GalleryCopyrightColor
 1896 
 1897 =item B<GalleryQuality>
 1898 
 1899 The quality (1-100) of scaled images
 1900 
 1901 This setting affects the quality of the scaled images.
 1902 Set this to a low number to reduce the size of the scaled images.
 1903 Remember to clear out your cache if you change this setting.
 1904 Quality seems to default to 75, at least in the jpeg and png loader code in
 1905 Imlib2 1.1.0.
 1906 
 1907 Examples:
 1908 
 1909 Quality at 50:
 1910         PerlSetVar      GalleryQuality '50'
 1911 
 1912 =item B<GalleryUnderscoresToSpaces>
 1913 
 1914 Set this option to 1 to convert underscores to spaces in the listing
 1915 of directory and file names, as well as in the alt attribute for HTML
 1916 <img> tags.
 1917 
 1918 =back
 1919 
 1920 =over 4
 1921 
 1922 =item B<GalleryCommentExifKey>
 1923 
 1924 Set this option to e.g. ImageDescription to use this field as comments
 1925 for images.
 1926 
 1927 =item B<GalleryEnableMediaRss>
 1928 
 1929 Set this option to 1 to enable generation of a media RSS feed. This
 1930 can be used e.g. together with the PicLens plugin from http://piclens.com
 1931 
 1932 =back
 1933 
 1934 =head1 FEATURES
 1935 
 1936 =over 4
 1937 
 1938 =item B<Rotate images>
 1939 
 1940 Some cameras, like the Canon G3, detects the orientation of a picture
 1941 and adds this info to the EXIF header. Apache::Gallery detects this
 1942 and automatically rotates images with this info.
 1943 
 1944 If your camera does not support this, you can rotate the images 
 1945 manually, This can also be used to override the rotate information
 1946 from a camera that supports that. You can also disable this behavior
 1947 with the GalleryAutoRotate option.
 1948 
 1949 To use this functionality you have to create file with the name of the 
 1950 picture you want rotated appended with ".rotate". The file should include 
 1951 a number where these numbers are supported:
 1952 
 1953     "1", rotates clockwise by 90 degree
 1954     "2", rotates clockwise by 180 degrees
 1955     "3", rotates clockwise by 270 degrees
 1956 
 1957 So if we want to rotate "Picture1234.jpg" 90 degrees clockwise we would
 1958 create a file in the same directory called "Picture1234.jpg.rotate" with
 1959 the number 1 inside of it.
 1960 
 1961 =item B<Ignore directories/files>
 1962 
 1963 To ignore a directory or a file (of any kind, not only images) you
 1964 create a <directory|file>.ignore file.
 1965 
 1966 =item B<Comments>
 1967 
 1968 To include comments for a directory you create a <directory>.comment
 1969 file where the first line can contain "TITLE: New title" which
 1970 will be the title of the page, and a comment on the following 
 1971 lines.
 1972 To include comments for each picture you create files called 
 1973 picture.jpg.comment where the first line can contain "TITLE: New
 1974 title" which will be the title of the page, and a comment on the
 1975 following lines.
 1976 
 1977 Example:
 1978 
 1979     TITLE: This is the new title of the page
 1980     And this is the comment.<br />
 1981     And this is line two of the comment.
 1982 
 1983 The visible name of the folder is by default identical to the name of
 1984 the folder, but can be changed by creating a file <directory>.folder
 1985 with the visible name of the folder.
 1986 
 1987 It is also possible to set GalleryCommentExifKey to the name of an EXIF
 1988 field containing the comment, e.g. ImageDescription. The EXIF comment is
 1989 overridden by the .comment file if it exists.
 1990 
 1991 =back
 1992 
 1993 =head1 DEPENDENCIES
 1994 
 1995 =over 4
 1996 
 1997 =item B<Perl 5>
 1998 
 1999 =item B<Apache with mod_perl>
 2000 
 2001 =item B<URI::Escape>
 2002 
 2003 =item B<Image::Info>
 2004 
 2005 =item B<Image::Size>
 2006 
 2007 =item B<Text::Template>
 2008 
 2009 =item B<Image::Imlib2>
 2010 
 2011 =item B<X11 libraries>
 2012 (ie, XFree86)
 2013 
 2014 =item B<Imlib2>
 2015 Remember the -dev package when using rpm, deb or other package formats!
 2016 
 2017 =back
 2018 
 2019 =head1 AUTHOR
 2020 
 2021 Michael Legart <michael@legart.dk>
 2022 
 2023 =head1 COPYRIGHT AND LICENSE
 2024 
 2025 Copyright (C) 2001-2011 Michael Legart <michael@legart.dk>
 2026 
 2027 Templates designed by Thomas Kjaer <tk@lnx.dk>
 2028 
 2029 Apache::Gallery is free software and is released under the Artistic License.
 2030 See B<http://www.perl.com/language/misc/Artistic.html> for details.
 2031 
 2032 The video icons are from the GNOME project. B<http://www.gnome.org/>
 2033 
 2034 =head1 THANKS
 2035 
 2036 Thanks to Thomas Kjaer for templates and design of B<http://apachegallery.dk>
 2037 Thanks to Thomas Eibner and other for patches. (See the Changes file)
 2038 
 2039 =head1 SEE ALSO
 2040 
 2041 L<perl>, L<mod_perl>, L<Image::Imlib2>, L<CGI::FastTemplate>,
 2042 L<Image::Info>, and L<Image::Size>.
 2043 
 2044 =cut