mourningdove/cgi-bin/Apache/LiveJournal/PalImg.pm

182 lines
5.4 KiB
Perl
Raw Permalink Normal View History

2026-05-24 01:03:05 +00:00
#!/usr/bin/perl
#
# This code was forked from the LiveJournal project owned and operated
# by Live Journal, Inc. The code has been modified and expanded by
# Dreamwidth Studios, LLC. These files were originally licensed under
# the terms of the license supplied by Live Journal, Inc, which can
# currently be found at:
#
# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
#
# In accordance with the original license, this code and all its
# modifications are provided under the GNU General Public License.
# A copy of that license can be found in the LICENSE file included as
# part of this distribution.
package Apache::LiveJournal::PalImg;
use strict;
use Apache2::Const qw(:common REDIRECT HTTP_NOT_MODIFIED);
use PaletteModify;
# for callers to 'ping' as a class method for Class::Autouse to lazily load
sub load { 1 }
# URLs of form /palimg/somedir/file.gif[extra]
# where extras can be:
# /p... - palette modify
sub handler {
my $apache_r = shift;
my $uri = $apache_r->uri;
my ( $base, $ext, $extra ) = $uri =~ m!^/palimg/(.+)\.(\w+)(.*)$!;
return 404 unless $base && $base !~ m!\.\.!;
my $disk_file = "$LJ::HTDOCS/palimg/$base.$ext";
return 404 unless -e $disk_file;
my @st = stat(_);
my $size = $st[7];
my $modtime = $st[9];
my $etag = "$modtime-$size";
my $mime = {
'gif' => 'image/gif',
'png' => 'image/png',
}->{$ext};
my $palspec;
if ($extra) {
if ( $extra =~ m!^/p(.+)$! ) {
$palspec = $1;
}
else {
return 404;
}
}
return send_file(
$apache_r,
$disk_file,
{
'mime' => $mime,
'etag' => $etag,
'palspec' => $palspec,
'size' => $size,
'modtime' => $modtime,
}
);
}
sub parse_hex_color {
my $color = shift;
return [ map { hex( substr( $color, $_, 2 ) ) } ( 0, 2, 4 ) ];
}
sub send_file {
my ( $apache_r, $disk_file, $opts ) = @_;
my $etag = $opts->{'etag'};
# palette altering
my %pal_colors;
if ( my $pals = $opts->{'palspec'} ) {
my $hx = "[0-9a-f]";
if ( $pals =~ /^g($hx{2,2})($hx{6,6})($hx{2,2})($hx{6,6})$/ ) {
# gradient from index $1, color $2, to index $3, color $4
my $from = hex($1);
my $to = hex($3);
return 404 if $from == $to;
my $fcolor = parse_hex_color($2);
my $tcolor = parse_hex_color($4);
if ( $to < $from ) {
( $from, $to, $fcolor, $tcolor ) = ( $to, $from, $tcolor, $fcolor );
}
$etag .= ":pg$pals";
for ( my $i = $from ; $i <= $to ; $i++ ) {
$pal_colors{$i} = [
map {
int( $fcolor->[$_] +
( $tcolor->[$_] - $fcolor->[$_] ) *
( $i - $from ) /
( $to - $from ) )
} ( 0 .. 2 )
];
}
}
elsif ( $pals =~ /^t($hx{6,6})($hx{6,6})?$/ ) {
# tint everything towards color
my ( $t, $td ) = ( $1, $2 );
$pal_colors{'tint'} = parse_hex_color($t);
$pal_colors{'tint_dark'} = $td ? parse_hex_color($td) : [ 0, 0, 0 ];
}
elsif ( length($pals) > 42 || $pals =~ /[^0-9a-f]/ ) {
return 404;
}
else {
my $len = length($pals);
return 404 if $len % 7; # must be multiple of 7 chars
for ( my $i = 0 ; $i < $len / 7 ; $i++ ) {
my $palindex = hex( substr( $pals, $i * 7, 1 ) );
$pal_colors{$palindex} = [
hex( substr( $pals, $i * 7 + 1, 2 ) ),
hex( substr( $pals, $i * 7 + 3, 2 ) ),
hex( substr( $pals, $i * 7 + 5, 2 ) ),
substr( $pals, $i * 7 + 1, 6 ),
];
}
$etag .= ":p$_($pal_colors{$_}->[3])" for ( sort keys %pal_colors );
}
}
$etag = '"' . $etag . '"';
my $ifnonematch = $apache_r->headers_in->{'If-None-Match'};
return HTTP_NOT_MODIFIED
if defined $ifnonematch && $etag eq $ifnonematch;
# send the file
$apache_r->content_type( $opts->{'mime'} );
$apache_r->headers_out->{'Content-length'} = $opts->{'size'};
$apache_r->headers_out->{ETag} = $etag;
if ( $opts->{'modtime'} ) {
$apache_r->update_mtime( $opts->{'modtime'} );
$apache_r->set_last_modified();
}
# HEAD request?
return OK if $apache_r->method eq "HEAD";
open my ($fh), $disk_file;
return 404 unless $fh;
binmode($fh);
my $palette;
if (%pal_colors) {
if ( $opts->{'mime'} eq "image/gif" ) {
$palette = PaletteModify::new_gif_palette( $fh, \%pal_colors );
}
elsif ( $opts->{'mime'} == "image/png" ) {
$palette = PaletteModify::new_png_palette( $fh, \%pal_colors );
}
unless ($palette) {
return 404; # image isn't palette changeable?
}
}
$apache_r->print($palette) if $palette; # when palette modified.
# now read the rest of the file, but let's max on a 1MB image for sanity
read $fh, my $buf, 1024 * 1024;
$apache_r->print($buf) if $buf;
$fh->close();
return OK;
}
1;