Apache-Request-I18N
view release on metacpan or search on metacpan
package Apache::Request::I18N;
use 5.008;
use strict;
use warnings;
use Apache::Request 0.32;
use Carp;
use Encode qw(decode_utf8 encode_utf8);
our @ISA = 'Apache::Request';
our $VERSION = '0.08';
=head1 NAME
Apache::Request::I18N - Internationalization extension to Apache::Request
=head1 SYNOPSIS
use Apache::Request::I18N;
my $apr = Apache::Request::I18N->new($r, DECODE_PARMS => 'utf-8');
Or, add something like this to your Apache F<httpd.conf>:
PerlModule Apache::Request::I18N;
<Location ...>
SetHandler perl-script
PerlHandler Apache::Request::I18N <your other handlers ...>
PerlSetVar DecodeParms utf-8
</Location>
=head1 DESCRIPTION
I<Apache::Request::I18N> adds transparent support over I<Apache::Request> for
internationalized GET/POST parameters. Form field names and values are
automatically decoded and converted either to Perl's internal UTF-8 format, or
to another character encoding.
Since this module inherits from I<Apache::Request>, it can be used as a
drop-in replacement. (It is not a B<perfect> replacement, though; see
L<"COMPATIBILITY ISSUES"> below.) It can also be used in a I<PerlHandler>
directive, in which case all subsequent handlers will -- if they play nicely
-- automatically see the converted names and values.
=head1 CONSTRUCTORS
=over 2
=item new( REQ [, OPTIONS ] )
Creates and returns a new I<Apache::Request::I18N> object. REQ is the
I<Apache> or I<Apache::Request> associated with the current request.
OPTIONS is an optional list of name/value pairs. Each option also has a
corresponding I<mod_perl> variable (listed in parentheses) that can be set via
I<PerlSetVar> in F<httpd.conf>. Values in OPTIONS take precedence. The
available options are:
=over 4
=item DECODE_PARMS (I<DecodeParms>)
I<Required>. Declares the character encoding that will be used by default
when decoding form field names and values. This character encoding must be
supported by the I<Encode> module (see L<Encode::Supported> for more details).
=item ENCODE_PARMS (I<EncodeParms>)
Declares the character encoding that will be used to re-encode form field
names and values. If omitted, names and values will be in Perl's own internal
UTF-8 format.
=back
I<Apache::Request> options can also be included (although they will be ignored
if REQ is already an I<Apache::Request> object).
=cut
sub new {
my ($class, $r, %args) = @_;
my $self = bless {
_decode_parms => delete $args{DECODE_PARMS}
|| $r->dir_config('DecodeParms'),
_encode_parms => delete $args{ENCODE_PARMS}
|| $r->dir_config('EncodeParms'),
}, $class;
croak "The DECODE_PARMS parameter is currently required"
unless $self->decode_parms;
$r = Apache::Request->new($r, %args)
unless $r->isa('Apache::Request');
=head1 FILE UPLOADS
Uploads returned by the I<upload>() method are I<Apache::Upload::I18N>
objects; they behave like I<Apache::Upload> objects, and their I<name>() and
I<filename>() methods will return values according to ENCODE_PARMS.
(This is however not the case within the upload hook; see L<"BUGS"> below.)
=cut
# Apache::Upload objects are C structs, and no mechanism is provided to
# subclass them. We therefore maintain a parallel storage area where each
# object can stash additional information about itself.
{
my %stashes;
sub _stash { $stashes{refaddr $_[0]} ||= {} }
sub _delete_stash { delete $stashes{refaddr $_[0]} }
}
# Each upload object is reblessed into Apache::Upload::I18N, and remembers its
# new name and filename through its stash area. ($req is needed so we know
# which encoding is used.)
sub rebless {
my ($class, $upload, $req) = @_;
return undef unless $upload;
bless $upload, $class;
my ($name, $filename) = ($upload->_old_name, $upload->_old_filename);
foreach ($name, $filename) {
$_ = $req->_decode_value($_);
$_ = $req->_encode($_) if $req->encode_parms;
}
my $stash = $upload->_stash;
%$stash = ( name => $name, filename => $filename );
return $upload;
}
sub DESTROY { $_[0]->_delete_stash }
sub name { $_[0]->_stash->{name} }
sub filename { $_[0]->_stash->{filename} }
sub _old_name { $_[0]->SUPER::name }
sub _old_filename { $_[0]->SUPER::filename }
sub next { carp "next() is not supported"; $_[0]->SUPER::next }
package Apache::Request::I18N;
=head1 HANDLER
This module provides a simple Apache handler that can be used in a
I<PerlHandler> directive. This is useful when used in combination with other
handlers, which will then automatically access the decoded values. (This
works as long as each handler takes care to call B<instance>() instead of
creating a new object.)
For example, you can use this module in combination with Mason:
SetHandler perl-script
PerlHandler +Apache::Request::I18N +HTML::Mason::ApacheHandler
PerlSetVar DecodeParms EUC-JP
Each Mason component will now see its arguments as true Perl character
strings instead of EUC-JP bytes strings.
=cut
use Apache::Constants 'DECLINED';
sub handler($$) {
my ($class, $r) = @_;
$class->instance($r);
DECLINED;
}
1;
__END__
=head1 COMPATIBILITY ISSUES
=over
=item *
Calling I<parms>() is not supported if ENCODE_PARMS is empty, as
I<Apache::Table> cannot handle character strings. This also applies to
calling I<param>() in scalar context.
=item *
Query parameter keys may or may not be case-insensitive, depending on their
contents and on ENCODE_PARMS.
=item *
Calling I<next>() on an upload object is not currently supported.
=back
=head1 BUGS
=over
=item *
When using the B<multipart/form-data> encoding, the proper encoding of form
field names and filenames as specified by RFC 2184 is currently not supported.
(This is due to a limitation in I<libapreq>.)
Conversely, since some user-agents are known to encode such values via RFC
2047, we attempt decoding if possible. This means that a value supplied by a
standard-compliant user-agent may be wrongly decoded.
=item *
When using the B<multipart/form-data> encoding, each form field value may have
its character encoding specified via the I<charset> parameter of its
I<Content-Type> header. This value is currently ignored. (This is due to a
limitation in I<libapreq>.)
Similarly, the I<Content-Transfer-Encoding> header is also ignored.
=item *
When using upload hooks, the upload object supplied to UPLOAD_HOOK will not
have had its I<name>() and I<filename>() decoded yet.
=item *
When using the B<multipart/form-data> encoding, this module will get confused
if a form field appears in both the query string B<and> the request body. In
other words, don't try to do this:
<FORM METHOD=post ENCTYPE="multipart/form-data"
ACTION=".../my_script?foo=1">
<INPUT NAME="foo" ...>
...
You should also avoid mixing file uploads and regular input within a single
field name. In other words, don't try this either:
<INPUT TYPE=text NAME="foo">
<INPUT TYPE=file NAME="foo">
=item *
Since all query parameter keys are stored in encoded form within an
I<Apache::Table> (which is case-insensitive), it is possible for two distinct
keys to be fused together if their encoded representations are similar.
=back
=head1 TODO
=over
=item *
Allow changing DECODE_PARMS and ENCODE_PARMS after the object has been
created.
=for comment
Note that doing so within a Mason component will have no effect, as Mason will
have already parsed and remembered all form fields.
=for comment
We should probably make _mangle_parms lazy, and only call it from param() and
such.
=item *
Automatically decode the contents of a B<text/*> file upload if a charset has
been provided.
=for comment
This should probably be optional, since we wouldn't know what to do with an
upload that doesn't have a charset. (Neither DECODE_PARMS nor the local
native charset would be appropriate here.) Besides, if ENCODE_PARMS was
defined, we'll still return a handle that spits out wide characters. (Come to
think of it, do any user-agents even bother providing a charset anyway?)
=item *
Allow for more than one DECODE_PARMS, and try to guess which one is
appropriate.
( run in 2.208 seconds using v1.01-cache-2.11-cpan-6aa56a78535 )