#!/usr/bin/perl -w
#
# edit-images: browse a set of image thumbnails and perform operations on
# their source images (delete, resize, rotate)
#
# Gerald Oskoboiny, 9 May 2002
#
# $Id: edit-images,v 1.10 2006/09/24 10:22:18 gerald Exp $
#
# usage: http://your-server.example.org/edit-images?path=foo/2002/05/09
#
# bugs:
#   - security holes: lets people browse a filesystem, isn't careful about input
#   - doesn't actually change anything, just outputs commands to do stuff
#   - assumes a certain filenaming scheme (thumbs named foo-sq.jpg)
#   - assumes thumbnails etc are already generated
#   - only handles one dir at a time
#   - usage is not intuitive
#
# source: http://impressive.net/software/photo/source/edit-images
#
# see also:
#    http://impressive.net/software/photo/
#    http://impressive.net/archives/fogo/20020510011350.GA744@impressive.net
#
# changelog:
#
# $Log: edit-images,v $
# Revision 1.10  2006/09/24 10:22:18  gerald
# made output more compact
#
# Revision 1.9  2005/12/15 03:33:49  gerald
# made it use foo-tn.jpg if foo-sq.jpg does not exist
#
# Revision 1.8  2005/12/12 00:17:56  gerald
# use square -sq.jpg variants instead of -tn.jpg
#
# Revision 1.7  2005/02/10 06:23:54  gerald
# eliminated an error from error log (still a few more left)
#
# Revision 1.6  2004/08/20 07:41:22  gerald
# output a list of months if no path specified
#
# Revision 1.5  2004/08/20 00:39:26  gerald
# committing changes dated Aug 14 12:36 ET (done in radium):
# added month at a glance (random image on each day of a calendar)
#
# Revision 1.4  2004/06/28 19:26:44  gerald
# removed some uninteresting info from output of 'identify'
#
# Revision 1.3  2002/05/21 22:23:46  gerald
# added a pointer to writeup on fogo
#
# Revision 1.2  2002/05/12 01:16:21  gerald
# 4 images per row instead of 3
#
# Revision 1.1  2002/05/09 21:19:54  gerald
# CGI script to browse a set of image thumbnails and perform operations on
# their source images (delete, resize, rotate)
#
# this rev is a quick hack that just outputs unix commands that can be used
# to perform the operations intended.
#
#

use strict;
use CGI;
use CGI::Carp qw(fatalsToBrowser);

if ( $ENV{REMOTE_ADDR} ne "127.0.0.1" ) {
    print "Content-Type: text/plain\n\n";
    print "may be unsafe to use this except on localhost; exiting.\n";
    exit;
}

my $path = $ENV{QUERY_STRING};
   $path =~ s/^path=//;

my $docroot = $ENV{DOCUMENT_ROOT};
my $dir = $docroot . "/" . $path;
chdir( $dir ) or die "couldn't chdir to $dir: $!";

if ( "\L$ENV{REQUEST_METHOD}" eq "post" ) {
    &handle_posted_form;
    exit;
}

if ( $path =~ m,[0-9]{4}/[0-9]{2}/$, ) {
    &output_month_at_a_glance( $path );
}

if ( $path !~ m,[0-9]{4}/[0-9]{2}/[0-9]{2}/$, ) {
    &output_list_of_months( $ENV{DOCUMENT_ROOT} . "/" . $path );
}

$path =~ s,/$,,;

print qq{Content-Type: text/html\n\n};
print qq{<title>edit-images: $path</title>\n\n};
print qq{<h1>edit images in $path</h1>\n\n};
print qq{<form method='post'>\n};
print qq{<table border="2"><tr>\n};

my @files = sort <*-tn.jpg>;
my $count = 1;

foreach my $file (@files) {
    (my $medfile = $file) =~ s/-tn/-med/;
    (my $sqfile  = $file) =~ s/-tn/-sq/;
    (my $origfile = $file) =~ s/-tn//;
    my $srcfile = $origfile;
    $srcfile = $medfile if -f $medfile;
    $file = $sqfile if -f $sqfile;
    print qq{<td style="font-size: smaller"><p align="center"><a href="/$path/$srcfile">\n};
    print qq{<img src="/$path/$file" hspace="10" vspace="5"/></a><br />\n};
    chomp( my $info = `identify $origfile | cut -d" " -f3,6 | sed 's/+0+0//'`);
    print qq{$origfile<br />($info)\n};
    print qq{</p>\n<ul style="margin: 0; list-style-type: none">\n};
    print qq{<li><input type="radio" name="$origfile" value="ok" };
    print qq{selected="selected"> noop<br />\n};
    print qq{<li><input type="radio" name="$origfile" value="delete"> };
    print qq{delete<br />\n};
    print qq{<li><input type="radio" name="$origfile" value="rx1200"> };
    print qq{x1200<br />\n};
    print qq{<li><input type="radio" name="$origfile" value="rx960"> };
    print qq{x960<br />\n};
    print qq{<li><input type="radio" name="$origfile" value="rx480"> };
    print qq{x480<br />\n};
    print qq{</ul>\n</td>\n};
    print "</tr><tr>" unless $count++ % 7;	# new row every 7 images
}

print qq{</tr></table>\n};
print qq{<p><input type="submit" value="show me the money!"></p>\n};
print qq{</form>\n};

exit;

sub handle_posted_form {

    print "Content-Type: text/plain\n\n";
    print "# cut and paste this, sucka!\n\n";
    print "cd $dir\n";
    print "mkdir .trash\n";

    foreach my $pair (split(/[&;,]/, join('',<STDIN>))) {
	my ($image, $action) = split(/=/, $pair);
	$action =~ tr/+/ /;
	$action =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
	if ( $action eq "ok" ) {
	    next;				# leave this image as is
	}
	elsif ( $action eq "delete" ) {
	    (my $imagestar = $image) =~ s/\.jpg$/*.jpg/;	# want foo*.jpg
	    print "mv $imagestar .trash\n";
	}
	elsif ( $action =~ /^rx/ ) {
	    (my $size = $action) =~ s/^r//;
	    print "resize-images $size $image\n";
	}
	else {
	    print "# unknown name/value pair submitted: [$image] = [$action]\n";
	}
    }

    exit;

}

sub output_month_at_a_glance {

    my $path = shift;
    my %ymd;

    my ($year,$month) = ($path =~ m,([0-9]{4})/([0-9]{2})/,);
    my $mso = &day_of_week($year,$month,1);	# "mso" == "month starts on"

    my $verbose_mmyy = `date '+%B %Y' --date '$year-$month-01'`;
    print qq{Content-Type: text/html\n\n};
    print qq{<title>edit-images: $verbose_mmyy</title>\n\n};
    print qq{<h1 align="center">$verbose_mmyy</h1>\n\n};
    print qq{<table align="center" border="1"><tr>\n};
    print qq{<td>&nbsp;</td>} x $mso;

    open( FIND,
	"find $docroot/$path -type f -name '*tn.jpg' -print | sort -n |" )
	or die "unable to find: $!";
    while (<FIND>) {
	chomp;
	my ($yyyy,$mm,$dd,$file) =
	    (m,([0-9]{4})/([0-9]{2})/([0-9]{2})/([^/]+),);
    	$ymd{"$yyyy$mm$dd"} .= " " . $file;
    }
    foreach my $day (1..31) {
    	my $day0 = sprintf("%02d",$day);
	if ( ! defined $ymd{"$year$month$day0"} ) {
	    print qq{<td align="center">$day<br /></td>\n};
	}
	else {
	    my @files = split(" ",$ymd{"$year$month$day0"});
	    print qq{<td align="center">$day};
	    print qq{ (} . ($#files + 1) . qq{ pic};
	    print "s" unless $#files == 0;
	    print qq{)<br /><a };
	    print qq{href="$ENV{PATH_INFO}?path=$path$day0/"><img };
	    print qq{src="/$path$day0/};
	    my $choice = @files[int(rand($#files+1))];
	    (my $sqfile = $choice) =~ s/-tn/-sq/;
	    $choice = $sqfile if -f "$docroot/$path$day0/$sqfile";
	    print $choice;
	    print qq{" height="100" /></a></td>\n};
	}
	print "</tr>\n\n<tr>" if &day_of_week($year,$month,$day) == 6;
    }
    close( FIND ) or warn "error closing pipe to find: $!";
    print "</tr></table>\n";
    exit;

}

#
# day_of_week($year, $month, $date)
# 
# provided by Tkil, http://slinky.scrye.com/~tkil/
# on #perl, Dec 13, 1998 -- gerald
# 
# Return the day of the week (0 == Sunday ... 6 == Saturday) for the
# given date.  $year should have four digits; $month and $date
# both start at 1, not zero.
# 
# Algorithm is from the CRC Handbook of Mathematics, 30th Ed, page 738.
# 
# see also: appendix B of RFC 3339, http://www.ietf.org/rfc/rfc3339.txt
# 

sub day_of_week {
  my ($year, $month, $date) = @_;

  $month -= 2;
  if ($month <= 0) {
    -- $year;
    $month += 12;
  }

  my $cent = int($year/100);
  $year %= 100;

  # print STDERR "year=$year, cent=$cent, date=$date, month=$month\n";

  my $dow = ($date
             + int(2.6*$month - 0.2)
             - 2*$cent
             + $year
             + int($year/4)
             + int($cent/4));

  $dow += 7 while $dow < 0;

  $dow %= 7;

  return $dow;
}

sub output_list_of_months {

    my $path = shift;

    print "Content-Type: text/html\n\n";
    print qq{<title>edit-images: list of months</title>\n\n};
    print qq{<h1>list of months</h1>\n\n};
    print "<ul>\n";

    open( FIND,
	"find $path -name '[0-9][0-9]' -print | egrep '/[0-9][0-9][0-9][0-9]/[0-9][0-9]\$' | sort -rn |" )
	or die "unable to find: $!";
    while (<FIND>) {
	chomp;
	s,$docroot/,,;
	print qq{  <li><a href="$ENV{PATH_INFO}?path=$_/">$_</a></li>\n};
    }
    close( FIND ) or warn "error closing pipe to find: $!";
    print "</ul>\n";
    exit;

}

