/[suikacvs]/messaging/manakai/lib/Message/Markup/XML/EntityManager.pm
Suika

Contents of /messaging/manakai/lib/Message/Markup/XML/EntityManager.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.16 - (hide annotations) (download)
Sun Oct 31 12:29:53 2004 UTC (21 years, 9 months ago) by wakaba
Branch: MAIN
CVS Tags: before-dis2-200411, manakai-release-0-3-2, manakai-release-0-3-1, manakai-release-0-4-0, manakai-200612, HEAD
Changes since 1.15: +6 -4 lines
Not to be warned by -w

1 wakaba 1.1
2     =head1 NAME
3    
4 wakaba 1.11 Message::Markup::XML::EntityManager --- manakai: XML Entity manager
5 wakaba 1.1
6     =head1 DESCRIPTION
7    
8 wakaba 1.3 The entity manager is part of XML system to retrive internal/external entity in
9     this implementation. In addition to it, this module provides some procedures
10     to validate public identifier or system identifier and to get one of/list of
11     markup declarations for element, attribute list or notation from the DTD.
12    
13     This module have customizable interface to get external resource.
14     Defining the additional or replacing function for the "external identifier(s)
15     to entity value convertion", more flexible or secure entity resolving can be
16     implemented. (For detail, see examples below.)
17    
18 wakaba 1.11 This module is part of XML.
19 wakaba 1.1
20     =cut
21    
22 wakaba 1.11 package Message::Markup::XML::EntityManager;
23 wakaba 1.1 use strict;
24 wakaba 1.16 our $VERSION = do{my @r=(q$Revision: 1.15 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
25 wakaba 1.5 our %NS;
26 wakaba 1.11 *NS = \%Message::Markup::XML::NS;
27 wakaba 1.1
28 wakaba 1.5 # $class->new ($yourself)
29 wakaba 1.1 sub new ($$) {
30 wakaba 1.5 my $self = bless {node => $_[1]}, $_[0];
31 wakaba 1.6 return $self unless ref $self->{node};
32 wakaba 1.5 for (@{$self->{node}->{node}}) {
33 wakaba 1.1 if ($_->{type} eq '#declaration' && $_->{namespace_uri} eq $NS{SGML}.'doctype') {
34     $self->{doctype} = $_;
35     last;
36     }
37     }
38     $self;
39     }
40    
41 wakaba 1.6 sub set_root_node ($$) { $_[0]->{node} = $_[1] }
42 wakaba 1.5 sub set_doctype_node ($$) { $_[0]->{doctype} = $_[1] }
43 wakaba 1.4
44     sub is_declared_entity ($$;%) {
45     my ($self, $name, %o) = @_;
46     if (ref $name) {
47     $o{namespace_uri} ||= $name->{namespace_uri};
48     $name = $name->{local_name};
49     } else {
50     $o{namespace_uri} ||= $NS{SGML}.'entity';
51     }
52     $self->{cache}->{is_declared_entity}->{$o{namespace_uri}} = {} if $o{clear_cache};
53 wakaba 1.13 if (ref $o{set_value}) {
54     $self->{cache}->{entity_declaration}->{$o{namespace_uri}}->{$name} = $o{set_value};
55     $self->{cache}->{is_declared_entity}->{$o{namespace_uri}}->{$name} = 1;
56     } elsif (defined $o{set_value}) {
57     $self->{cache}->{is_declared_entity}->{$o{namespace_uri}}->{$name} = $o{set_value};
58     }
59 wakaba 1.4 unless (defined $self->{cache}->{is_declared_entity}->{$o{namespace_uri}}->{$name}) {
60     if ($o{seek}) {
61     $self->{cache}->{is_declared_entity}->{$o{namespace_uri}}->{$name}
62     = $self->get_entity ($name, %o) ? 1 : 0;
63     }
64     }
65     $self->{cache}->{is_declared_entity}->{$o{namespace_uri}}->{$name};
66     }
67    
68     sub get_entity ($$;%) {
69 wakaba 1.1 my ($self, $name, %o) = @_;
70     if (ref $name) {
71     $o{namespace_uri} ||= $name->{namespace_uri};
72     $name = $name->{local_name};
73     } else {
74     $o{namespace_uri} ||= $NS{SGML}.'entity';
75     }
76 wakaba 1.2 if (!$o{dont_use_predefined_entities}
77 wakaba 1.3 && ($o{namespace_uri} eq $NS{SGML}.'entity')) { ## General entity
78 wakaba 1.2 my $predec = {
79     amp => '&',
80     apos => ''',
81     gt => '>',
82     lt => '<',
83     quot => '"',
84     }->{$name};
85     if ($predec) {
86 wakaba 1.11 for (Message::Markup::XML->new (type => '#declaration',
87 wakaba 1.2 namespace_uri => $NS{SGML}.'entity')) {
88     $_->set_attribute ('value')->append_new_node (type => '#xml', value => $predec);
89     return $_;
90     }
91 wakaba 1.1 }
92     }
93 wakaba 1.4 $self->{cache}->{entity_declaration}->{$o{namespace_uri}} = {} if $o{clear_cache};
94     my $e = $self->{cache}->{entity_declaration}->{$o{namespace_uri}}->{$name};
95 wakaba 1.5 return $e if ref $e;
96 wakaba 1.13 return undef if !$o{seek} || !(ref $self->{doctype});
97 wakaba 1.4 $e = $self->_get_entity ($name, $self->{doctype}->{node}, \%o);
98     if (ref $e) {
99     $self->{cache}->{entity_declaration}->{$o{namespace_uri}}->{$name} = $e;
100     return $e;
101     }
102     my $xsub = $self->{doctype}->get_attribute ('external-subset');
103     if (ref $xsub) {
104     $e = $self->_get_entity ($name, $xsub->{node}, \%o);
105     if (ref $e) {
106     $self->{cache}->{entity_declaration}->{$o{namespace_uri}}->{$name} = $e;
107     return $e;
108     }
109     }
110     return undef;
111 wakaba 1.1 }
112     sub _get_entity ($$$$) {
113     my ($self, $name, $nodes, $o) = @_;
114     return undef unless ref $nodes;
115     for (@$nodes) {
116 wakaba 1.4 next if $_->{flag}->{smxp__non_processed_declaration};
117 wakaba 1.1 if ($_->{type} eq '#declaration' && $_->{namespace_uri} eq $o->{namespace_uri}
118     && $_->{local_name} eq $name) {
119     return $_;
120     } elsif ($_->{type} eq '#reference') {
121     my $e = $self->_get_entity ($name, $_->{node}, $o);
122     return $e if ref $e;
123 wakaba 1.4 } elsif ($_->{type} eq '#section'
124     && ($_->get_attribute ('status', make_new_node => 1)->inner_text||'INCLUDE')
125     eq 'INCLUDE') {
126     my $e = $self->_get_entity ($name, $_->{node}, $o);
127     return $e if ref $e;
128 wakaba 1.1 }
129     }
130     return undef;
131     }
132    
133 wakaba 1.2 # DOM's get*By*
134     sub get_entities ($$%) {
135     my ($self, $l, %o) = @_;
136 wakaba 1.4 $o{namespace_uri} = $NS{SGML}.'entity' unless defined $o{namespace_uri};
137     $o{type} ||= '#declaration';
138     $o{parent_node} ||= $self->{doctype};
139     $self->_get_entities ($l, $o{parent_node}->{node}, \%o);
140 wakaba 1.2 }
141     sub _get_entities ($$$$) {
142     my ($self, $l, $nodes, $o) = @_;
143     return undef unless ref $nodes;
144     for (@$nodes) {
145 wakaba 1.4 next if $_->{flag}->{smxp__non_processed_declaration};
146 wakaba 1.15 if (($_->{type} eq $o->{type})
147     and (
148     ( defined $o->{namespace_uri}
149     and defined $_->{namespace_uri}
150     and $_->{namespace_uri} eq $o->{namespace_uri})
151     or (not defined $o->{namespace_uri} and not defined $_->{namespace_uri}))) {
152 wakaba 1.2 push @$l, $_;
153 w 1.9 } elsif ($_->{type} eq '#reference' || $_->{type} eq '#element') {
154 wakaba 1.2 $self->_get_entities ($l, $_->{node}, $o);
155 w 1.9 } elsif (($_->{type} eq '#section'
156     && ($_->get_attribute ('status', make_new_node => 1)->inner_text||'INCLUDE')
157     eq 'INCLUDE')
158     || ($_->{type} eq '#declaration' && $_->{namespace_uri} eq $NS{SGML}.'doctype')) {
159 wakaba 1.4 $self->_get_entities ($l, $_->{node}, $o);
160     } elsif ($_->{type} eq '#attribute' && $_->{local_name} eq 'external-subset') {
161     $self->_get_entities ($l, $_->{node}, $o);
162 wakaba 1.2 }
163     }
164     }
165    
166 w 1.8 sub get_attr_definitions ($%) {
167     my ($self, %o) = @_;
168     return $self->{cache}->{attr_defs}->{$o{qname}}
169     if $self->{cache}->{attr_defs}->{$o{qname}};
170     my %r;
171     $o{namespace_uri} = $NS{SGML}.'attlist';
172     $o{type} = '#declaration';
173     $o{parent_node} ||= $self->{doctype};
174     my $l = [];
175     $self->_get_entities ($l, $o{parent_node}->{node}, \%o);
176     $r{declaration} = [];
177     for (@$l) {
178 wakaba 1.15 if ($_->get_attribute_value ('qname') eq $o{qname}) {
179 w 1.8 push @{$r{declaration}}, $_;
180     }
181     }
182     for my $decl (@{$r{declaration}}) {
183     for (@{$decl->{node}}) {
184 wakaba 1.15 if ($_->{type} eq '#element'
185     and $_->{namespace_uri} eq $NS{XML}.'attlist'
186     and $_->{local_name} eq 'AttDef') {
187     my $aname = $_->get_attribute_value ('qname');
188 w 1.8 if ($r{attr}->{$aname}) {
189 w 1.10 #
190 w 1.8 } else {
191     $r{attr}->{$aname} = $_;
192     $r{attr_may_not_be_read}->{$aname} = $decl->{flag}->{smxp__declaration_may_not_be_read};
193 w 1.10 for (@{$_->{node}}) {
194     if ($_->{type} eq '#element' && $_->{namespace_uri} eq $NS{XML}.'attlist'
195     && $_->{local_name} eq 'enum') {
196     $r{enum}->{$aname}->{$_->inner_text} = 1;
197     }
198     }
199 w 1.8 }
200     }
201     }
202     }
203     $self->{cache}->{attr_defs}->{$o{qname}} = \%r;
204     \%r;
205     }
206    
207 wakaba 1.4 ## TODO: uri based recursion
208 wakaba 1.14 sub get_external_entity ($$$$;%) {
209     my ($self, $parser, $decl, $o, %opt) = @_;
210     my $declns = ref $decl ? $decl->namespace_uri : '#document';
211     my $name = ref $decl ?
212     ($declns eq $NS{SGML}.'doctype' ?
213 wakaba 1.15 ($decl->get_attribute_value ('qname')
214 wakaba 1.12 || '#IMPLIED') :
215 wakaba 1.14 $decl->local_name) :
216     $opt{uri};
217 wakaba 1.4 my $p = $self->{external_entity_cache}->{$declns}->{$name};
218 wakaba 1.3 if ($name && !$p) {
219 wakaba 1.4 $p = {name => $name}; $self->{external_entity_cache}->{$declns}->{$name} = $p;
220 wakaba 1.14 if (ref $decl) {
221     for (qw/PUBLIC SYSTEM NDATA/) {
222     $p->{$_} = ref $decl ? $decl->get_attribute ($_) : $opt{$_};
223     $p->{$_} = ref $p->{$_} ? $p->{$_}->inner_text : undef;
224     }
225     $p->{uri} = $decl->resolve_relative_uri ($p->{SYSTEM}, use_references_base_uri => 1);
226     } else {
227     $p->{uri} = $opt{uri};
228 wakaba 1.3 }
229     }
230     for ($o) {
231     $_->{entity_type} ||= 'external_parsed_entity';
232     $_->{uri} = $p->{uri}; $_->{line} = 0; $_->{pos} = 0;
233     }
234     if ($name && !$p->{__flag}) {
235 wakaba 1.6 my $resolver = $parser->option ('uri_resolver');
236 wakaba 1.3 if (ref $resolver) {
237 wakaba 1.14 $resolver = &$resolver ($self, $parser, $decl, $p, $o, %opt); ## If returned false,
238     $self->default_uri_resolver ($parser, $decl, $p, $o, %opt) if $resolver; ## don't call this.
239 wakaba 1.3 } else {
240 wakaba 1.14 $self->default_uri_resolver ($parser, $decl, $p, $o, %opt);
241 wakaba 1.3 }
242 w 1.7 ## Line-break normalization
243 wakaba 1.16 if (defined $p->{text}) {
244     $p->{text} =~ s/\x0D\x0A/\x0A/gs;
245     $p->{text} =~ tr/\x0D/\x0A/;
246     }
247 wakaba 1.3 $p->{__flag} = 1;
248     }
249     $p;
250     }
251    
252     =pod example
253    
254 wakaba 1.14 my $parser = Message::Markup::XML::Parser->new (option => {uri_resolver => sub {
255 wakaba 1.3 my ($self, $decl, $p) = @_;
256     @@ $p->{SYSTEM} =~ s///g @@
257     return 1;
258     }});
259    
260     =cut
261    
262 wakaba 1.6 sub default_uri_resolver ($$$$$;%) {
263     my ($self, $parser, $decl, $p, $o, %opt) = @_;
264 wakaba 1.3 require LWP::UserAgent;
265     my $ua = LWP::UserAgent->new;
266 wakaba 1.11 $ua->agent ('"Message::Markup::XML::EntityManager"/'.$VERSION);
267 wakaba 1.3 ## TODO: use Message::Field::UA
268     my $req = HTTP::Request->new (GET => $p->{uri});
269     my $res = $ua->request ($req);
270 wakaba 1.6 if ($res->is_success || $opt{accept_error_page}) {
271 wakaba 1.3 ## TODO: use Message::Entity for more intelligent/strict parsing:-)
272     $p->{base_uri} = $res->base; ## See Content-Base: and Content-Location: (and HTML:BASE)
273     $p->{uri} = $res->request->uri; $o->{uri} = $p->{uri}; ## Redirect support
274     ## Check media type
275     my $CT = $res->header ('Content-Type');
276     my $ct = lc $res->content_type;
277     $self->_check_media_type ($o, $ct);
278     $p->{text} = $res->content;
279     #$p->{text} .= "<!--$p->{uri}-->"; ## DEBUG: base URI
280     ## Charset/encoding convertion
281     my $encoding;
282 wakaba 1.6 if ($CT =~ /charset\s*=\s*"?([^",;\s]+)"?/i) { ## BUG: This check is not strict
283 wakaba 1.3 $encoding = lc $1;
284     } else { ## No charset parameter
285     if ($p->{uri}->scheme eq 'file') {
286     ## Protocol does not provide charset information
287     } elsif (lc (substr ($ct, 0, 5)) eq 'text/') { ## BUG: This check is not strict
288     $encoding = 'us-ascii'; ## See RFC 3023
289     $self->_raise_error ($o, type => 'WARN_NO_CHARSET_PARAM_FOR_TEXT', t => $ct);
290     } else {
291     ## BUG: Warn even if the media type does not have the charset parameter
292     $self->_raise_error ($o, type => 'WARN_NO_CHARSET_PARAM', t => $ct);
293     }
294     }
295     unless ($encoding) {
296     $encoding = $self->_guess_entity_encoding ($p->{text}, $o) || 'utf-8';
297     }
298     #print "<$p->{uri}>: encode is {$encoding}\n"; ## DEBUG: Detected encoding
299     if ($encoding) {
300     require Encode;
301     eval q{$p->{text} = Encode::decode ($encoding, $p->{text}); 1}
302     or $self->_raise_error ($o, type => 'FATAL_ERR_DECODE_IMPL_ERR', t => $@);
303     } else {
304     #$self->_raise_error ($o, type => 'WARN_NO_EXPLICIT_ENCODING_INFO');
305     }
306     ## parse and remove xml declaration
307 wakaba 1.6 unless ($opt{dont_parse_text_declaration}) {
308     $p->{text_declaration} = (ref ($decl)||$decl)->new (type => '#pi', local_name => 'xml');
309     $parser->_parse_xml_or_text_declaration ($p->{text_declaration}, \$p->{text}, $o);
310     }
311 wakaba 1.3 } else {
312     $p->{error}->{no_data} = 1;
313     $p->{error}->{reason_text} = $res->status_line;
314 wakaba 1.6 $p->{uri} = $res->request->uri; $o->{uri} = $p->{uri}; ## Redirect support
315 wakaba 1.3 }
316     }
317    
318 wakaba 1.4 sub is_standalone_document ($) {
319     my $self = shift;
320     return $self->{node}->{flag}->{smxe__standalone}
321     if defined $self->{node}->{flag}->{smxe__standalone};
322     for (@{$self->{node}->{node}}) {
323     if ($_->{type} eq '#pi' && $_->{local_name} eq 'xml') {
324     my $a = $_->get_attribute ('standalone');
325     if (ref $a) {
326     $self->{node}->{flag}->{smxe__standalone} = $a->inner_text eq 'yes' ? 1 : 0;
327     return $self->{node}->{flag}->{smxe__standalone};
328     }
329 w 1.8 } elsif ($_->{type} eq '#attribute') {
330     ## Check next node too
331     } else {
332     last; ## No xml declaration
333 wakaba 1.4 }
334     }
335     $self->{node}->{flag}->{smxe__standalone} = 0;
336     return $self->{node}->{flag}->{smxe__standalone};
337     }
338 wakaba 1.1 sub is_standalone_document_1 ($) {
339     my $self = shift;
340     return $self->{node}->{flag}->{smxe__standalone_1}
341     if defined $self->{node}->{flag}->{smxe__standalone_1};
342     for (@{$self->{node}->{node}}) {
343     if ($_->{type} eq '#pi' && $_->{local_name} eq 'xml') {
344     my $a = $_->get_attribute ('standalone');
345     if (ref $a) {
346     $self->{node}->{flag}->{smxe__standalone_1} = $a->inner_text eq 'yes' ? 1 : 0;
347     return $self->{node}->{flag}->{smxe__standalone_1};
348     }
349     }
350 wakaba 1.4 last;
351 wakaba 1.1 }
352     if ($self->{doctype}) {
353     if ($self->{doctype}->external_id) {
354     $self->{node}->{flag}->{smxe__standalone_1} = 0;
355     return $self->{node}->{flag}->{smxe__standalone_1};
356     }
357     for (@{$self->{doctype}->{node}}) {
358     if ($_->{type} eq '#declaration' && $_->{namespace_uri} eq $NS{SGML}.'entity:parameter') {
359     $self->{node}->{flag}->{smxe__standalone_1} = 0;
360     return $self->{node}->{flag}->{smxe__standalone_1};
361     }
362     }
363     }
364     $self->{node}->{flag}->{smxe__standalone_1} = 1;
365     return $self->{node}->{flag}->{smxe__standalone_1};
366     }
367    
368 wakaba 1.3 sub check_public_id ($$$) {
369     my ($self, $o, $pubid) = @_;
370 wakaba 1.4 if (length ($pubid) == 0) {
371     $self->_raise_error ($o, type => 'WARN_PID_EMPTY');
372     }
373     if ($pubid =~ m"([^\x0A\x0D\x20A-Za-z0-9'()+,./:=?;!*#\@\$_%-])"s) {
374 wakaba 1.3 $self->_raise_error ($o, type => 'SYNTAX_INVALID_PUBID', t => $1);
375     }
376     $pubid =~ s/[\x0A\x0D\x20]+/\x20/gs;
377     if (length ($pubid) > 240) { ## this check is not strict
378     $self->_raise_error ($o, type => 'WARN_PID_IS_TOO_LONG', t => $pubid);
379     }
380     $pubid =~ s/^\x20//; $pubid =~ s/\x20$//;
381     if ($pubid =~ /^[Uu][Rr][Nn]:/) {
382     if ($pubid !~ m"^[Uu][Rr][Nn]:[0-9A-Za-z][0-9A-Za-z-]{0,31}:(?:[0-9A-Za-z()+,.:=\@;\$_!*'/?-]|%[0-9A-Fa-f]{2})+$") {
383     $self->_raise_error ($o, type => 'WARN_PID_IS_INVALID_URN', t => $pubid);
384     } elsif ($pubid =~ m![/?]!) {
385     $self->_raise_error ($o, type => 'WARN_PID_IS_URN_WITH_RESERVED_CHAR', t => $pubid);
386     }
387     } elsif ($pubid !~ m<^(?:[+-]//|ISO)(?:(?!//).)+//[A-Z]+ (?:(?!//).)+//(?:(?!//).)+(?://(?:(?!//).)+)?$>) {
388     $self->_raise_error ($o, type => 'WARN_PID_IS_NOT_FPI_NOR_URN', t => $pubid);
389     }
390     $pubid;
391     }
392    
393     sub check_system_id ($$$) {
394     my ($self, $o, $sysid) = @_;
395 wakaba 1.6 if ($sysid =~ m"([^0-9A-Za-z_.!~*'();/?:\@&=+\$,%\[\]#-])"s) {
396 wakaba 1.3 $self->_raise_error ($o, type => 'WARN_INVALID_URI_CHAR_IN_SYSID', t => $1);
397 wakaba 1.4 }
398     if ($sysid =~ s/(#[^#]*)$//g) {
399     $self->_raise_error ($o, type => 'ERR_XML_SYSID_HAS_FRAGMENT', t => $1);
400     }
401     if (length ($sysid) == 0) {
402     $self->_raise_error ($o, type => 'WARN_SYSID_EMPTY');
403 wakaba 1.3 }
404     $sysid;
405 wakaba 1.5 }
406    
407     sub check_ns_uri ($$$$) { ## TODO: check predefined NS
408     my ($self, $o, $ns_pfx => $ns_name) = @_;
409 wakaba 1.6 if ($ns_name =~ m"([^0-9A-Za-z_.!~*'();/?:\@&=+\$,%\[\]#-])"s) {
410 wakaba 1.5 $self->_raise_error ($o, type => 'WARN_INVALID_URI_CHAR_IN_NS_NAME', t => $1);
411     }
412     if ($ns_name !~ /^[0-9A-Za-z.+-]+:/) {
413     $self->_raise_error ($o, type => 'WARN_XML_NS_URI_IS_RELATIVE', t => $ns_name);
414     }
415 wakaba 1.3 }
416    
417     ## Guess encoding of the entity by BOM and '<?' and Encode::Guess --- Used by default resolver
418     sub _guess_entity_encoding ($$) {
419     my ($self, $entity, $o) = @_;
420     my $encoding;
421     my $f2 = substr ($entity, 0, 2);
422     my $s2 = substr ($entity, 2, 2);
423     if ($f2 eq "<?") {
424     $encoding = '*ascii';
425     } elsif ($f2 eq "\xEF\xBB" && substr ($s2, 0, 1) eq "\xBF") {
426     $encoding = '*utf-8';
427     } elsif ($f2 eq "\xFE\xFF" || $f2 eq "\x00\x3C") {
428     if ($s2 eq "\x00\x00") {
429     $encoding = '*ucs-4-3412';
430     } else {
431     $encoding = '*ucs-2be';
432     }
433     } elsif ($f2 eq "\xFF\xFE" || $f2 eq "\x3C\x00") {
434     if ($s2 eq "\x00\x00") {
435     $encoding = '*ucs-4le';
436     } else {
437     $encoding = '*ucs-2le';
438     }
439     } elsif ($f2 eq "\x00\x00") {
440     if ($s2 eq "\xFE\xFF" || $s2 eq "\x00\x3C") {
441     $encoding = '*ucs-4be';
442     } elsif ($s2 eq "\xFF\xFE" || $s2 eq "\x3C\x00") {
443     $encoding = '*ucs-4-2143';
444     }
445     } elsif ($f2 eq "\x4C\x6F") {
446     $encoding = '*ebcdic';
447     }
448    
449     ## TODO: Charset list need more consideration
450     my @guess_list;
451     if ($encoding eq '*ascii' || $encoding eq '*utf-8') {
452     if ($entity =~ /<\?xml.+?encoding=["']([0-9A-Za-z._-]+)["']/) {
453     $encoding = lc $1;
454     } else {
455     if ($encoding eq '*utf-8') {
456     $encoding = undef;
457     @guess_list = qw/utf-8 cesu-8 unicode-1-1-utf-8/;
458     } else {
459     $encoding = undef;
460     @guess_list = qw/us-ascii iso-8859-1 iso-8859-2 iso-8859-8 iso-8859-15
461     iso-2022-jp euc-jp shift_jis euc-kr
462     gbk gb18030 big5-eten big5-hkscs windows-1252 koi8-r koi8-u/;
463     }
464     }
465     } elsif ($encoding =~ /ucs/) {
466     my $ent = $entity; $ent =~ tr/\x00//d;
467     if ($ent =~ /<\?xml.+?encoding=["']([0-9A-Za-z._-]+)["']/) {
468     $encoding = lc $1;
469     } else {
470     if ($encoding eq '*ucs-2be') {
471     $encoding = 'utf-16be';
472     } elsif ($encoding eq '*ucs-2le') {
473     $encoding = 'utf-16le';
474     } elsif ($encoding =~ /(ucs-4.+)/) {
475     $encoding = $1;
476     }
477     }
478     } elsif ($encoding eq '*ebcdic') {
479     @guess_list = qw/cp037 cp500 cp875 cp1026 cp1047 posix-bc/; ## All Encode::EBCDIC supporteds
480     }
481    
482     unless ($encoding) {
483     $self->_raise_error ($o, type => 'WARN_NO_EXPLICIT_ENCODING_INFO');
484     require Encode::Guess;
485     unless (@guess_list) {
486     @guess_list = qw/utf-8 utf-16 utf-16be utf-16le
487     7bit-jis euc-jp shift_jis gbk gb18030 euc-kr big5-eten big5-hkscs
488     iso-8859-1 iso-8859-8 koi8-r koi8-u tis-620
489     windows-1252/;
490     }
491     my @gl;
492     for (@guess_list) {
493     push @gl, $_ if Encode::find_encoding ($_);
494     }
495     my $enc;
496     eval q{$enc = Encode::Guess::guess_encoding ($entity, @gl); 1}
497     or $self->_raise_error ($o, type => 'WARN_GUESS_ENCODING_IMPL_ERR', t => $@);
498     $encoding = $enc->name if ref $enc;
499     }
500     $encoding;
501     }
502    
503     ## Check whether the media type specified is better one for that type of entity
504     ## and raise error if not --- See RFC 3023
505     ##
506     ## Known error: Warn even when source is local file doesn't have meta type information
507     ## (but LWP give inappropreate value such as 'text/plain'). This is an error but spec.
508     sub _check_media_type ($$$) {
509     my ($self, $o, $ct) = @_;
510     if ($ct eq 'application/xml' || $ct eq 'text/xml') {
511     if ($o->{entity_type} eq 'external_parsed_entity'
512     || $o->{entity_type} eq 'external_general_parsed_entity') {
513     $self->_raise_error ($o, type => 'WARN_MT_XML_FOR_EXT_GENERAL_ENTITY', t => $ct);
514     } elsif ($o->{entity_type} ne 'document_entity') {
515     $self->_raise_error ($o, type => 'ERR_MT_XML_FOR_EXT_ENTITY', t => $ct);
516     }
517     if ($ct eq 'text/xml') {
518     $self->_raise_error ($o, type => 'WARN_MT_TEXT_XML');
519     }
520     } elsif ($o->{entity_type} eq 'external_general_parsed_entity') {
521     if ($ct eq 'text/xml-external-parsed-entity') {
522     $self->_raise_error ($o, type => 'WARN_MT_TEXT_XML_EXTERNAL_PARSED_ENTITY');
523     } elsif ($ct ne 'application/xml-external-parsed-entity') {
524     $self->_raise_error ($o, type => 'WARN_MT_EXTERNAL_GENERAL_PARSED_ENTITY', t => $ct);
525     }
526     } elsif ($o->{entity_type} eq 'dtd_external_subset'
527     || $o->{entity_type} eq 'external_parameter_entity') {
528     if ($ct ne 'application/xml-dtd') {
529     $self->_raise_error ($o, type => 'WARN_MT_DTD_EXTERNAL_SUBSET', t => $ct);
530     }
531     }
532     }
533    
534 wakaba 1.6 sub option ($$;$) {
535     my ($self, $name, $value) = @_;
536     if (defined $value) {
537     $self->{option}->{$name} = $value;
538     }
539     $self->{option}->{$name};
540     }
541    
542     sub flag ($$;$) {
543     my ($self, $name, $value) = @_;
544     if (defined $value) {
545     $self->{flag}->{$name} = $value;
546     }
547     $self->{flag}->{$name};
548     }
549    
550 wakaba 1.3 ## $self->_raise_error: Raising error or warn
551 wakaba 1.11 require Message::Markup::XML::Error;
552     *_raise_error = \&Message::Markup::XML::Error::raise;
553 wakaba 1.3
554 wakaba 1.4 =head1 DEVELOPER'S NOTE
555    
556 wakaba 1.11 This module "knows" how Message::Markup::XML works, i.e. this module accesses
557 wakaba 1.4 internal structure of that module directly.
558    
559 wakaba 1.1 =head1 LICENSE
560    
561     Copyright 2003 Wakaba <[email protected]>
562    
563     This program is free software; you can redistribute it and/or
564     modify it under the same terms as Perl itself.
565    
566     =cut
567    
568 wakaba 1.16 1; # $Date: 2003/10/31 08:41:35 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24