Apache2-MimeInfo
view release on metacpan or search on metacpan
lib/Apache2/MimeInfo.pm view on Meta::CPAN
package Apache2::MimeInfo;
use 5.010;
use strict;
use warnings FATAL => 'all';
use base qw(Apache2::Filter);
use Apache2::Const -compile => qw(OK DECLINED HTTP_BAD_GATEWAY);
use APR::Const -compile => qw(SUCCESS);
use Apache2::Log ();
use Apache2::FilterRec ();
use Apache2::RequestRec ();
use Apache2::RequestUtil ();
use APR::Table ();
use APR::Bucket ();
use APR::Brigade ();
use IO::Scalar ();
BEGIN {
# do this because holy god File::BaseDir is annoying
# no need to local
#local $ENV{HOME};
if ($ENV{MOD_PERL}) {
use Apache2::ServerUtil ();
$ENV{HOME} ||= Apache2::ServerUtil::server_root();
}
# shut UPPP
require File::MimeInfo::Magic;
}
=head1 NAME
Apache2::MimeInfo - Content-Type header informed by shared-mime-info
=head1 VERSION
Version 0.06
=cut
our $VERSION = '0.06';
=head1 SYNOPSIS
PerlLoadModule Apache2::MimeInfo
PerlOutputFilterHandler Apache2::MimeInfo
=head1 DESCRIPTION
This module bolts L<File::MimeInfo::Magic> and the
L<shared-mime-info|http://freedesktop.org/wiki/Software/shared-mime-info/>
database to Apache 2.x. Its purpose is to clean up C<Content-Type>
headers on responses that come from potentially misconfigured or
untrustworthy sources (e.g. a proxy).
Insert L<Apache2::MimeInfo> as an output filter as described in the
synopsis to perform content-based type checking against a more robust
database than the one that comes along with
L<file|http://darwinsys.com/file/>. The type asserted by the original
content handler will be overridden by this filter unless the asserted
type is a more specific instance of a more general I<detected>
type. For instance, Microsoft's C<.docx> format is a ZIP file with
special contents. Its MIME type is this:
application/vnd.openxmlformats-officedocument.wordprocessingml.document
The detector, however, will perceive C<application/zip>, which is an
ancestor type. If whatever content handler producing the document
asserts the correct type, then this module will do nothing. If,
however, it returns something like C<application/octet-stream>, which
is even more generic than C<application/zip>, this module will replace
the C<Content-Type> header with C<application/zip>. It will likewise
replace the C<Content-Type> header if it is missing altogether, or if
it asserts a type that is inconsitent with the one which was detected.
If you aren't familiar with
L<shared-mime-info|http://freedesktop.org/wiki/Software/shared-mime-info/>,
it's an XML database containing useful information about the mappings
of, and relations between MIME types. This means that you can hack new
type signatures into it, just as you can with
L<file|http://darwinsys.com/file/>.
=cut
my %SKIP = (
'application/x-gzip' => 1,
'application/x-compress' => 1,
'application/x-bzip2' => 1,
);
# File::MimeInfo::mimetype_isa only tells you if the child type is an
# immediate descendant of its parent, which is practically useless.
sub _mimetype_isa_really {
my ($child, $ancestor) = @_;
my @q = ($child);
my %t;
do {
for my $t (File::MimeInfo::mimetype_isa(shift @q)) {
$t = lc $t; # JIC
push @q, $t unless defined $t{$t};
$t{$t}++;
}
} while @q;
if (defined $ancestor) {
return !!$t{$ancestor};
}
else {
return sort keys %t;
}
}
sub handler : FilterRequestHandler {
my ($f, $bb) = @_;
unless ($f->ctx) {
my $r = $f->r;
my $type = $r->content_type || $r->default_type;
# XXX not sure if the first bucket is going to be the right one
my $b = $bb->first;
if ($b->read(my $data)) {
my $io = IO::Scalar->new(\$data);
my $mg = File::MimeInfo::Magic->new;
my $mt = File::MimeInfo::Magic::mimetype($io);
$r->log->debug("Content type asserted: $type, Detected: $mt");
if (_mimetype_isa_really($type, $mt)) {
$r->log->debug("Leaving more-specific type alone");
}
elsif ($SKIP{$mt}) {
# one day we can actually inspect the uncompressed contents.
$r->log->debug("Not replacing $type with $mt.");
}
else {
$r->log->debug("Replacing content type on " . $r->uri);
$r->content_type($mt);
( run in 0.572 second using v1.01-cache-2.11-cpan-9581c071862 )