1 package Catalyst::Plugin::Unicode::Encoding;
4 use base 'Class::Data::Inheritable';
11 our $CHECK = Encode::FB_CROAK | Encode::LEAVE_SRC;
15 __PACKAGE__->mk_classdata('_encoding');
22 # Let it be set to undef
23 if (my $wanted = shift) {
24 $encoding = Encode::find_encoding($wanted)
25 or Carp::croak( qq/Unknown encoding '$wanted'/ );
29 ? $c->{encoding} = $encoding
30 : $c->_encoding($encoding);
32 $encoding = ref $c && exists $c->{encoding}
40 sub finalize_headers {
43 my $body = $c->response->body;
45 return $c->next::method(@_)
46 unless defined($body);
48 my $enc = $c->encoding;
50 return $c->next::method(@_)
53 my ($ct, $ct_enc) = $c->response->content_type;
55 # Only touch 'text-like' contents
56 return $c->next::method(@_)
57 unless $c->response->content_type =~ /^text|xml$|javascript$/;
59 if ($ct_enc && $ct_enc =~ /charset=([^;]*)/) {
60 if (uc($1) ne uc($enc->mime_name)) {
61 $c->log->debug("Unicode::Encoding is set to encode in '" .
63 "', content type is '$1', not encoding ");
64 return $c->next::method(@_);
67 $c->res->content_type($c->res->content_type . "; charset=" . $enc->mime_name);
70 # Encode expects plain scalars (IV, NV or PV) and segfaults on ref's
71 $c->response->body( $c->encoding->encode( $body, $CHECK ) )
72 if ref(\$body) eq 'SCALAR';
77 # Note we have to hook here as uploads also add to the request parameters
83 my $enc = $c->encoding;
85 for my $key (qw/ parameters query_parameters body_parameters /) {
86 for my $value ( values %{ $c->request->{$key} } ) {
87 # N.B. Check if already a character string and if so do not try to double decode.
88 # http://www.mail-archive.com/catalyst@lists.scsys.co.uk/msg02350.html
89 # this avoids exception if we have already decoded content, and is _not_ the
90 # same as not encoding on output which is bad news (as it does the wrong thing
91 # for latin1 chars for example)..
92 $value = $c->_handle_unicode_decoding($value);
95 for my $value ( values %{ $c->request->uploads } ) {
96 # skip if it fails for uploads, as we don't usually want uploads touched
98 for my $inner_value ( ref($value) eq 'ARRAY' ? @{$value} : $value ) {
99 $inner_value->{filename} = try {
100 $enc->decode( $inner_value->{filename}, $CHECK )
102 $c->handle_unicode_encoding_exception({
103 param_value => $inner_value->{filename},
105 encoding_step => 'uploads',
115 my $ret = $c->next::method(@_);
117 foreach (@{$c->req->arguments}, @{$c->req->captures}) {
118 $_ = $c->_handle_param_unicode_decoding($_);
127 my $conf = $self->config;
129 # Allow an explict undef encoding to disable default of utf-8
130 my $enc = delete $conf->{encoding};
131 $self->encoding( $enc );
133 return $self->next::method(@_)
134 unless $self->setup_finished; ## hack to stop possibily meaningless test fail... (jnap)
137 sub _handle_unicode_decoding {
138 my ( $self, $value ) = @_;
140 return unless defined $value;
142 if ( ref $value eq 'ARRAY' ) {
143 foreach ( @$value ) {
144 $_ = $self->_handle_unicode_decoding($_);
148 elsif ( ref $value eq 'HASH' ) {
149 foreach ( values %$value ) {
150 $_ = $self->_handle_unicode_decoding($_);
155 return $self->_handle_param_unicode_decoding($value);
159 sub _handle_param_unicode_decoding {
160 my ( $self, $value ) = @_;
161 my $enc = $self->encoding;
163 Encode::is_utf8( $value ) ?
165 : $enc->decode( $value, $CHECK );
168 $self->handle_unicode_encoding_exception({
169 param_value => $value,
171 encoding_step => 'params',
176 sub handle_unicode_encoding_exception {
177 my ( $self, $exception_ctx ) = @_;
178 die $exception_ctx->{error_msg};
187 Catalyst::Plugin::Unicode::Encoding - Unicode aware Catalyst
193 MyApp->config( encoding => 'UTF-8' ); # A valid Encode encoding
198 This plugin is automatically loaded by apps. Even though is not a core component
199 yet, it will vanish as soon as the code is fully integrated. For more
200 information, please refer to C<ENCODING> section at L<Catalyst>.
204 Catalyst Contributors, see Catalyst.pm
208 This library is free software. You can redistribute it and/or modify
209 it under the same terms as Perl itself.