/[suikacvs]/messaging/manakai/lib/Message/Field/UA.pm
Suika

Contents of /messaging/manakai/lib/Message/Field/UA.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.13.2.1 - (show annotations) (download)
Sat Dec 28 08:38:42 2002 UTC (21 years, 11 months ago) by wakaba
Branch: stable
Changes since 1.13: +4 -9 lines
Minor fix

1
2 =head1 NAME
3
4 Message::Field::UA -- Perl module for Internet message
5 header field body consist of C<product> tokens
6
7 =cut
8
9 package Message::Field::UA;
10 use strict;
11 use vars qw(%DEFAULT @ISA %REG $VERSION);
12 $VERSION=do{my @r=(q$Revision: 1.13.2.1 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
13 require Message::Field::Structured;
14 push @ISA, qw(Message::Field::Structured);
15 use overload '.=' => sub {
16 if (ref $_[1] eq 'HASH') {
17 $_[0]->add (%{$_[1]});
18 } elsif (ref $_[1] eq 'ARRAY') {
19 $_[0]->add (@{$_[1]});
20 } else {
21 $_[0]->add ($_[1] => '', -prepend => 0);
22 }
23 $_[0];
24 },
25 fallback => 1;
26
27 *REG = \%Message::Util::REG;
28
29 ## Initialize of this class -- called by constructors
30 %DEFAULT = (
31 -_HASH_NAME => 'product',
32 -_METHODS => [qw|add count delete item|],
33 -_MEMBERS => [qw|product|],
34 -by => 'product-name', ## Default key for item, delete,...
35 #encoding_after_encode
36 #encoding_before_decode
37 #field_param_name
38 #field_name
39 #format
40 #hook_encode_string
41 #hook_decode_string
42 -prepend => 1, ## For add, replace
43 -use_Config => 1,
44 -use_comment => 1,
45 #-use_quoted_string => 1,
46 -use_Win32 => 1,
47 -use_Win32_API => 1,
48 );
49
50 =head1 CONSTRUCTORS
51
52 The following methods construct new objects:
53
54 =over 4
55
56 =cut
57
58 ## Initialize of this class -- called by constructors
59 sub _init ($;%) {
60 my $self = shift;
61 my %options = @_;
62 $self->SUPER::_init (%DEFAULT, %options);
63
64 unless (defined $self->{option}->{use_quoted_string}) {
65 if ($self->{option}->{format} =~ /http/) {
66 $self->{option}->{use_quoted_string} = 0;
67 } else {
68 $self->{option}->{use_quoted_string} = 1;
69 }
70 }
71
72 my @a = ();
73 for (grep {/^[^-]/} keys %options) {
74 push @a, $_ => $options{$_};
75 }
76 $self->add (@a) if $#a > -1;
77 }
78
79 =item $ua = Message::Field::UA->new ([%options])
80
81 Constructs a new C<Message::Field::UA> object. You might pass some
82 options as parameters to the constructor.
83
84 =cut
85
86 ## Inherited
87
88 =item $ua = Message::Field::UA->parse ($field-body, [%options])
89
90 Constructs a new C<Message::Field::UA> object with
91 given field body. You might pass some options as parameters to the constructor.
92
93 =cut
94
95 sub parse ($$;%) {
96 my $class = shift;
97 my $self = bless {}, $class;
98 my $field_body = shift; my @ua = ();
99 $self->_init (@_);
100 use re 'eval';
101 $field_body =~ s{^((?:$REG{FWS}$REG{comment})+)}{
102 my $comments = $1;
103 $comments =~ s{$REG{M_comment}}{
104 my $comment = $self->Message::Util::decode_ccontent ($1);
105 push @ua, {comment => [$comment]} if $comment;
106 }goex;
107 '';
108 }goex;
109 $field_body =~ s{
110 ($REG{quoted_string}|[^\x09\x20\x22\x28\x2F]+) ## product-name
111 (?:
112 ((?:$REG{FWS}$REG{comment})*)$REG{FWS}
113 /
114 ((?:$REG{FWS}$REG{comment})*)$REG{FWS}
115 ($REG{quoted_string}|[^\x09\x20\x22\x28]+) ## product-version
116 )?
117 ((?:$REG{FWS}$REG{comment})*) ## comment
118 }{
119 my ($product, $product_version, $comments) = ($1, $4, $2.$3.$5);
120 for ($product, $product_version) {
121 my ($s,$q) = (Message::Util::unquote_if_quoted_string ($_), 0);
122 my %s = &{$self->{option}->{hook_decode_string}} ($self, $s,
123 type => ($q?'token/quoted':'token')); ## What token/quoted is? :-)
124 $_ = $s{value};
125 }
126 my @comment = ();
127 $comments =~ s{$REG{M_comment}}{
128 my $comment = $self->Message::Util::decode_ccontent ($1);
129 push @comment, $comment if $comment;
130 }goex;
131 push @ua, {name => $product, version => $product_version,
132 comment => \@comment};
133 }goex;
134 push @{$self->{product}}, @ua;
135 $self;
136 }
137
138 =back
139
140 =head1 METHODS
141
142 =over 4
143
144 =cut
145
146
147 =item $hdr->add ($name, $version, [$name, $version, ...])
148
149 Adds some field name/version pairs. Even if there are
150 one or more C<product>s whose name is same as C<$name>
151 (case sensible), given name/body pairs are ADDed. Use C<replace>
152 to remove C<old> one.
153
154 Instead of C<$version>, you can pass array reference.
155 [0] is used for C<version>, the others are saved as elements
156 of C<comment>.
157
158 C<-prepend> options is available. C<1> is default.
159
160 Example:
161
162 $ua->add (Perl => [$^V, $^O], 'foo.pl' => $VERSION, -prepend => 0);
163 print $ua; # foo.pl/1.00 Perl/5.6.1 (MSWin32)
164
165 =cut
166
167 sub _add_hash_check ($$$\%) {
168 my $self = shift;
169 my ($name, $version, $option) = @_;
170 my @comment;
171 if (ref $version eq 'ARRAY') {
172 ($version, @comment) = @$version;
173 }
174
175 ## Convert vX.Y.Z value to string (But there is no way to be sure that
176 ## the value is a version value.)
177 #$^V gt v5.6.0 && ## <- This check itself doesn't work before v5.6.0:)
178 if ($version =~ /[\x00-\x1F]/) {
179 $version = sprintf '%vd', $version;
180 }
181
182 (1, $name => {
183 name => $name,
184 version => $version,
185 comment => \@comment,
186 });
187 }
188
189 *_add_return_value = \&_replace_return_value;
190
191 ## (1/0, $name => $value) = $self->_replace_hash_check ($name => $value, \%option)
192 ## -- Checks given value and prepares saving value (hash version)
193 *_replace_hash_check = \&_add_hash_check;
194
195
196 ## $value = $self->_replace_hash_shift (\%values, $name, $option)
197 ## -- Returns a value (from %values) and deletes it from %values
198 ## (like CORE::shift for array).
199 sub _replace_hash_shift ($\%$\%) {
200 shift; my $r = shift; my $n = $_[0]->{name};
201 if ($$r{$n}) {
202 my $d = $$r{$n};
203 $$r{$n} = undef;
204 return $d;
205 }
206 undef;
207 }
208
209 ## $value = $self->_replace_return_value (\$item, \%option)
210 ## -- Returns returning value of replace method
211 sub _replace_return_value ($\$\%) {
212 my $self = shift;
213 my ($item, $value) = @_;
214 $$item;
215 }
216
217 ## 1/0 = $self->_delete_match ($by, \$item, \%delete_list, \%option)
218 ## -- Checks and returns whether given item is matched with
219 ## deleting item list
220 sub _delete_match ($$\$\%\%) {
221 my $self = shift;
222 my ($by, $item, $list, $option) = @_;
223 return 0 unless ref $$item; ## Already removed
224 if ($by eq 'name') {
225 return 1 if $$list{ $$item->{name} };
226 } elsif ($by eq 'version') {
227 return 1 if $$list{ $$item->{version} };
228 }
229 0;
230 }
231
232 ## Delete empty items
233 sub _delete_empty ($) {
234 my $self = shift;
235 my $array = $self->{option}->{_HASH_NAME};
236 $self->{ $array } = [grep { ref $_ } @{$self->{ $array }}] if $array;
237 }
238
239 *_item_match = \&_delete_match;
240 *_item_return_value = \&_replace_return_value;
241
242 ## $item = $self->_item_new_value ($name, \%option)
243 ## -- Returns new item with key of $name (called when
244 ## no returned value is found and -new_value_unless_exist
245 ## option is true)
246 sub _item_new_value ($$\%) {
247 my $self = shift;
248 my ($key, $option) = @_;
249 if ($option->{by} eq 'name') {
250 return {name => $key, version => '', comment => []};
251 } elsif ($option->{by} eq 'version') {
252 return {name => '', version => $key, comment => []};
253 }
254 undef;
255 }
256
257 ## TODO: Implement count,item_exist method
258
259 =item $self->stringify ()
260
261 Returns C<field-body> as a string.
262
263 =cut
264
265 sub stringify ($;%) {
266 my $self = shift;
267 my %o = @_; my %option = %{$self->{option}};
268 for (grep {/^-/} keys %o) {$option{substr ($_, 1)} = $o{$_}}
269 my @r = ();
270 for my $p (@{$self->{product}}) {
271 if (length $p->{name}) {
272 my %name = &{$self->{option}->{hook_encode_string}} ($self,
273 $p->{name}, type => 'token');
274 my %version = &{$self->{option}->{hook_encode_string}} ($self,
275 $p->{version}, type => 'token');
276 if (!$option{use_quoted_string}
277 && ( $name{value} =~ /$REG{NON_http_token}/
278 || $version{value} =~ /$REG{NON_http_token}/)) {
279 if ($name{value} =~ /$REG{NON_http_token}/) {
280 ## Both of name & version are unsafe
281 push @r, '(' . Message::Util::quote_ccontent (
282 $name{value} .
283 (length $version{value}? '/' . $version{value} : '')
284 ) . ')';
285 } else {
286 ## Only version is unsafe
287 push @r, $name{value}
288 .' (' . Message::Util::quote_ccontent ($version{value}) . ')';
289 }
290 } else {
291 push @r,
292 Message::Util::quote_unsafe_string
293 ($name{value}, unsafe => 'NON_http_token')
294 .(length $version{value} ?
295 '/' . Message::Util::quote_unsafe_string
296 ($version{value}, unsafe => 'NON_http_token') : '');
297 }
298 } elsif ($p->{version}) {
299 ## There is no product-name but the product-version. It's error!
300 push @r, '('. $self->Message::Util::encode_ccontent ($p->{version}) .')';
301 }
302 ## If there are some additional information,
303 for (@{$p->{comment}}) {
304 push @r, '('. $self->Message::Util::encode_ccontent ($_) .')' if $_;
305 }
306 }
307 join ' ', @r;
308 }
309 *as_string = \&stringify;
310
311 =item $option-value = $ua->option ($option-name)
312
313 Gets option value.
314
315 =item $ua->option ($option-name, $option-value, ...)
316
317 Set option value(s). You can pass multiple option name-value pair
318 as parameter when setting.
319
320 =cut
321
322 ## Inherited
323
324 =item $clone = $ua->clone ()
325
326 Returns a copy of the object.
327
328 =cut
329
330 ## Inherited
331
332 sub add_our_name ($;%) {
333 my $ua = shift;
334 my %o = @_; my %option = %{ $ua->{option} };
335 for (grep {/^-/} keys %o) {$option{substr ($_, 1)} = $o{$_}}
336
337 if ($Message::Entity::VERSION) {
338 $ua->replace_rcs ($option{date}, name => 'Message-pm',
339 version => $Message::Entity::VERSION,
340 -prepend => 0);
341 }
342
343 ## Perl version and architecture
344 my @perl_comment;
345 eval q{use Config; push @perl_comment, $Config{archname}} if $option{use_Config};
346 eval q{require Win32; my $build; $build = &Win32::BuildNumber ();
347 push @perl_comment, "ActivePerl build $build" if $build;
348 } if $option{use_Win32};
349 undef $@;
350
351 if ($^V) { ## 5.6 or later
352 $ua->replace (Perl => [sprintf ('%vd', $^V), @perl_comment], -prepend => 0);
353 } elsif ($]) { ## Before 5.005
354 $ua->replace (Perl => [ $], @perl_comment], -prepend => 0);
355 }
356 $option{prepend} = 0;
357 $ua->replace_system_version ('os', \%option);
358 $ua;
359 }
360
361 sub replace_system_version ($$;%) {
362 my $ua = shift;
363 my $type = shift;
364 my %option;
365 if (ref $_[0]) {
366 %option = %{$_[0]};
367 } else {
368 my %o = @_; %option = %{ $ua->{option} };
369 for (grep {/^-/} keys %o) {$option{substr ($_, 1)} = $o{$_}}
370 }
371
372 if ($type eq 'os') {
373 my @os_comment = ('');
374 my @os = ($^O => \@os_comment);
375 eval q{use Config; @os_comment = ($Config{osvers})} if $option{use_Config};
376 eval q{require Win32;
377 my @osv = &Win32::GetOSVersion ();
378 @os = (
379 $osv[4] == 0? 'Win32s':
380 $osv[4] == 1? 'Windows':
381 $osv[4] == 2? 'Windows NT':
382 'Win32', \@os_comment);
383 @os_comment = (sprintf ('%d.%02d.%d', @osv[1,2], $osv[3] & 0xFFFF));
384 push @os_comment, $osv[0] if $osv[0] =~ /[^\x00\x09\x20]/;
385 if ($osv[4] == 1) {
386 if ($osv[1] == 4) {
387 if ($osv[2] == 0) {
388 if ($osv[0] =~ /[Aa]/) { push @os_comment, 'Windows 95 OSR1' }
389 elsif ($osv[0] =~ /[Bb]/) { push @os_comment, 'Windows 95 OSR2' }
390 elsif ($osv[0] =~ /[Cc]/) { push @os_comment, 'Windows 95 OSR2.5' }
391 else { push @os_comment, 'Windows 95' }
392 } elsif ($osv[2] == 10) {
393 if ($osv[0] =~ /[Aa]/) { push @os_comment, 'Windows 98 SE' }
394 else { push @os_comment, 'Windows 98' }
395 } elsif ($osv[2] == 90) {
396 push @os_comment, 'Windows Me';
397 }
398 }
399 } elsif ($osv[4] == 2) {
400 push @os_comment, 'Windows 2000' if $osv[1] == 5 && $osv[2] == 0;
401 push @os_comment, 'Windows XP' if $osv[1] == 5 && $osv[2] == 1;
402 }
403 push @os_comment, &Win32::GetChipName ();
404 } if $option{use_Win32};
405 undef $@;
406 $ua->replace (@os, -prepend => $option{prepend});
407 } elsif ('ie') { ## Internet Explorer
408 my $flag = 0;
409 eval q{use Win32::Registry;
410 my $ie;
411 $::HKEY_LOCAL_MACHINE->Open('SOFTWARE\Microsoft\Internet Explorer', $ie) or die $^E;
412 my ($type, $value);
413 $ie->QueryValueEx (Version => $type, $value) or die $^E;
414 die unless $value;
415 $ua->replace (MSIE => $value, -prepend => $option{prepend});
416 $flag = 1;
417 } or Carp::carp ($@) if !$flag;
418 eval q{require Win32::API;
419 my $GV = new Win32::API (shlwapi => "DllGetVersion", P => 'N');
420 my $ver = pack lllll => 4*5, 0, 0, 0, 0;
421 $GV->Call ($ver);
422 my (undef, $major, $minor, $build) = unpack lllll => $ver;
423 $ua->replace (MSIE => sprintf ("%d.%02d.%04d", $major, $minor, $build),
424 -prepend => $option{prepend});
425 $flag = 1;
426 } if $option{use_Win32_API} && !$flag;
427 }
428 $ua;
429 }
430
431 sub add_rcs ($$;%) {
432 my $self = shift;
433 my ($rcsid, %option) = @_;
434 my ($name, $version, $date) = ($option{name}, $option{version}, $option{date});
435 for (grep {/^[^-]/} keys %option) { delete $option{$_} }
436 if ($rcsid =~ m!(?:Id|Header): (?:.+?/)?([^/]+?),v ([\d.]+) (\d+/\d+/\d+ \d+:\d+:\d+)!) {
437 $name ||= $1;
438 $version ||= $2;
439 $date ||= $3;
440 } elsif ($rcsid =~ m!^Date: (\d+/\d+/\d+ \d+:\d+:\d+)!) {
441 $date ||= $1;
442 } elsif ($rcsid =~ m!^Revision: ([\d.]+)!) {
443 $version ||= $1;
444 } elsif ($rcsid =~ m!(?:Source|RCSfile): (?:.+?/)?([^/]+?),v!) {
445 $name ||= $1;
446 }
447 if ($option{is_replace}) {
448 $self->replace ($name => [$version, $date], %option);
449 } else {
450 $self->add ($name => [$version, $date], %option);
451 }
452 }
453 sub replace_rcs ($$;%) {
454 shift->add_rcs (@_, is_replace => 1);
455 }
456
457 =back
458
459 =head1 LICENSE
460
461 Copyright 2002 Wakaba <w@suika.fam.cx>
462
463 This program is free software; you can redistribute it and/or modify
464 it under the terms of the GNU General Public License as published by
465 the Free Software Foundation; either version 2 of the License, or
466 (at your option) any later version.
467
468 This program is distributed in the hope that it will be useful,
469 but WITHOUT ANY WARRANTY; without even the implied warranty of
470 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
471 GNU General Public License for more details.
472
473 You should have received a copy of the GNU General Public License
474 along with this program; see the file COPYING. If not, write to
475 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
476 Boston, MA 02111-1307, USA.
477
478 =cut
479
480 1; # $Date: 2002/12/28 08:38:42 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24