use Debbugs::Config qw(:config);
use Debbugs::CGI qw(cgi_parameters);
use Debbugs::Common;
-use Digest::MD5 qw(md5_hex);
-use Gravatar::URL;
use File::LibMagic;
-use File::Temp qw(tempfile);
+use Debbugs::Libravatar qw(:libravatar);
use Libravatar::URL;
-use LWP::UserAgent;
-use HTTP::Request;
-
use CGI::Simple;
+use Cwd qw(abs_path);
+use Digest::MD5 qw(md5_hex);
my $q = CGI::Simple->new();
);
# if avatar is no, serve the empty png
if ($param{avatar} ne 'yes' or not defined $param{email} or not length $param{email}) {
- serve_cache('',$q);
+ serve_cache('',$q,0);
exit 0;
}
-# figure out what the md5sum of the e-mail is.
-my $email_md5sum = md5_hex(lc($param{email}));
-my $cache_location = cache_location($email_md5sum);
+my ($cache_location, $timestamp) = cache_location(email => lc($param{email}));
# if we've got it, and it's less than one hour old, return it.
-if (cache_valid($cache_location)) {
- serve_cache($cache_location,$q);
+if ($timestamp) {
+ serve_cache($cache_location,$q,$timestamp);
exit 0;
}
# if we don't have it, get it, and store it in the cache
-$cache_location = retreive_libravatar(location => $cache_location,
- email => lc($param{email}),
- );
+($cache_location,$timestamp) =
+ retrieve_libravatar(location => $cache_location,
+ email => lc($param{email}),
+ );
if (not defined $cache_location) {
# failure, serve the default image
- serve_cache('',$q);
+ serve_cache('',$q,0);
exit 0;
} else {
- serve_cache($cache_location,$q);
+ serve_cache($cache_location,$q,$timestamp);
exit 0;
}
-sub cache_valid{
- my ($cache_location) = @_;
- if (-e $cache_location) {
- if (time - (stat($cache_location))[9] < 60*60) {
- return 1;
- }
- }
- return 0;
-}
-
-sub retreive_libravatar{
- my %type_mapping =
- (jpeg => 'jpg',
- png => 'png',
- gif => 'png',
- tiff => 'png',
- tif => 'png',
- pjpeg => 'jpg',
- jpg => 'jpg'
- );
- my %param = @_;
- my $cache_location = $param{location};
- $cache_location =~ s/\.[^\.]+$//;
- my $uri = libravatar_url(email => $param{email},
- default => 404,
- size => 80);
- my $ua = LWP::UserAgent->new(agent => 'Debbugs libravatar service (not Mozilla)',
- );
- $ua->from($config{maintainer});
- # if we don't get an avatar within 10 seconds, return so we don't
- # block forever
- $ua->timeout(10);
- # if the avatar is bigger than 30K, we don't want it either
- $ua->max_size(30*1024);
- my $r = $ua->get($uri);
- if (not $r->is_success()) {
- return undef;
- }
- my $aborted = $r->header('Client-Aborted');
- # if we exceeded max size, I'm not sure if we'll be successfull or
- # not, but regardless, there will be a Client-Aborted header. Stop
- # here if that header is defined.
- return undef if defined $aborted;
- my $type = $r->header('Content-Type');
- # if there's no content type, or it's not one we like, we won't
- # bother going further
- return undef if not defined $type;
- return undef if not $type =~ m{^image/([^/]+)$};
- my $dest_type = $type_mapping{$1};
- return undef if not defined $dest_type;
- # undo any content encoding
- $r->decode() or return undef;
- # ok, now we need to convert it from whatever it is into a format
- # that we actually like
- my ($temp_fh,$temp_fn) = tempfile() or
- return undef;
- eval {
- print {$temp_fh} $r->content() or
- die "Unable to print to temp file";
- close ($temp_fh);
- system('convert','-resize','80x80',
- '-strip',
- $temp_fn,
- $cache_location.'.'.$dest_type) == 0 or
- die "convert file failed";
- unlink($temp_fh);
- };
- if ($@) {
- unlink($cache_location.'.'.$dest_type) if -e $cache_location.'.'.$dest_type;
- unlink($temp_fn) if -e $temp_fn;
- return undef;
- }
- return $cache_location.'.'.$dest_type;
-}
-
-sub cache_location {
- my ($md5sum) = @_;
- for my $ext (qw(.png .jpg)) {
- if (-e $config{libravatar_cache_dir}.'/'.$md5sum.$ext) {
- return $config{libravatar_cache_dir}.'/'.$md5sum.$ext;
- }
- }
- return $config{libravatar_cache_dir}.'/'.$md5sum;
-}
sub serve_cache {
- my ($cache_location,$q) = @_;
+ my ($cache_location,$q,$timestamp) = @_;
if (not defined $cache_location or not length $cache_location) {
# serve the default image
$cache_location = $config{libravatar_default_image};
+ if (not defined $timestamp or not $timestamp) {
+ $timestamp = (stat($cache_location))[9];
+ }
+ }
+ if (not defined $timestamp) {
+ # this probably means that the default image doesn't exist
+ print $q->header(status => 404);
+ print "404: Not found\n";
+ return;
+ }
+ my $etag = md5_hex($cache_location.$timestamp);
+ if (defined $q->http('if-none-match')
+ and $etag eq $q->http('if-none-match')) {
+ print $q->header(-status => 304);
+ print "304: Not modified\n";
+ return;
}
my $fh = IO::File->new($cache_location,'r') or
- error(404, "Failed to open cached image $cache_location");
+ error($q,404, "Failed to open cached image $cache_location");
my $m = File::LibMagic->new() or
- error(500,'Unable to create File::LibMagic object');
- my $mime_string = $m->checktype_filename($cache_location) or
- error(500,'Bad file; no mime known');
+ error($q,500,'Unable to create File::LibMagic object');
+ my $mime_string = $m->checktype_filename(abs_path($cache_location)) or
+ error($q,500,'Bad file; no mime known');
print $q->header(-type => $mime_string,
- -expires => '+1d',
- );
- print STDOUT <$fh>;
+ -expires => '+1d',
+ -status => 200,
+ -etag => $etag,
+ );
+ print <$fh>;
close($fh);
}
sub error {
- my ($error,$text) = @_;
+ my ($q,$error,$text) = @_;
$text //= '';
print $q->header(-status => $error);
print "<h2>$error: $text</h2>";