]> git.donarmstrong.com Git - debbugs.git/commitdiff
add libravatar caching cgi; add 1x1.png; add web directory documentation
authorDon Armstrong <don@donarmstrong.com>
Thu, 28 Mar 2013 23:58:45 +0000 (16:58 -0700)
committerDon Armstrong <don@donarmstrong.com>
Thu, 28 Mar 2013 23:58:45 +0000 (16:58 -0700)
cgi/libravatar.cgi [new file with mode: 0644]
html/1x1.png [new file with mode: 0644]

diff --git a/cgi/libravatar.cgi b/cgi/libravatar.cgi
new file mode 100644 (file)
index 0000000..7ba5333
--- /dev/null
@@ -0,0 +1,167 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+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 Libravatar::URL;
+
+use LWP::UserAgent;
+use HTTP::Request;
+
+use CGI::Simple;
+
+my $q = CGI::Simple->new();
+
+my %param =
+    cgi_parameters(query => $q,
+                   single => [qw(email avatar default)],
+                   default => {avatar => 'yes',
+                               default => $config{libravatar_uri_options},
+                              },
+                  );
+# 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);
+    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);
+# 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);
+    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}),
+                                     );
+if (not defined $cache_location) {
+    # failure, serve the default image
+    serve_cache('',$q);
+    exit 0;
+} else {
+    serve_cache($cache_location,$q);
+    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','-geometry','80x80',
+               $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) = @_;
+    if (not defined $cache_location or not length $cache_location) {
+        # serve the default image
+        $cache_location = $config{libravatar_default_image};
+    }
+    my $fh = IO::File->new($cache_location,'r') or
+        error(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');
+    print $q->header(-type => $mime_string,
+                     -expires => '+1d',
+                    );
+    print STDOUT <$fh>;
+    close($fh);
+}
+
+
+sub error {
+    my ($error,$text) = @_;
+    $text //= '';
+    print $q->header(-status => $error);
+    print "<h2>$error: $text</h2>";
+    exit 0;
+}
diff --git a/html/1x1.png b/html/1x1.png
new file mode 100644 (file)
index 0000000..1914264
Binary files /dev/null and b/html/1x1.png differ