/[suikacvs]/messaging/manakai/lib/Message/Body/Multipart.pm
Suika

Contents of /messaging/manakai/lib/Message/Body/Multipart.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (hide annotations) (download)
Tue Jun 11 12:58:06 2002 UTC (22 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.1: +73 -3 lines
2002-06-11  wakaba <w@suika.fam.cx>

	* Multipart.pm: Some bug fixes.

1 wakaba 1.1
2     =head1 NAME
3    
4     Message::Body::Multipart --- Perl module
5     for "multipart/*" Internet Media Types
6    
7     =cut
8    
9     package Message::Body::Multipart;
10     use strict;
11     use vars qw(%DEFAULT @ISA $VERSION);
12     $VERSION=do{my @r=(q$Revision: 1.1 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
13    
14     require Message::Body::Text;
15     push @ISA, qw(Message::Body::Text);
16    
17     my @BCHARS = ('0'..'9', 'A'..'Z', 'a'..'z', qw#+ _ , - . / : =#);
18     #my @BCHARS = ('0'..'9', 'A'..'Z', 'a'..'z', qw#' ( ) + _ , - . / : = ?#, ' '); ## RFC 2046
19     my %REG;
20     $REG{NON_bchars} = qr#[^0-9A-Za-z'()+_,-./:=?\x20]#;
21    
22     %DEFAULT = (
23     ## "#i" : only inherited from parent Entity and inherits to child Entity
24     -_ARRAY_NAME => 'value',
25 wakaba 1.2 -_METHODS => [qw|entity_header add delete count item preamble epilogue|],
26     -_MEMBERS => [qw|boundary preamble epilogue|],
27 wakaba 1.1 #i accept_cte
28     #i body_default_charset
29     #i body_default_charset_input
30     #i cte_default
31     -default_media_type => 'text',
32     -default_media_subtype => 'plain',
33     -media_type => 'multipart',
34     -media_subtype => 'mixed',
35     #output_epilogue
36     -parse_all => 0,
37     #i text_coderange
38     #use_normalization => 0,
39     #use_param_charset => 0,
40     -value_type => {},
41     );
42    
43     =head1 CONSTRUCTORS
44    
45     The following methods construct new objects:
46    
47     =over 4
48    
49     =cut
50    
51     ## Initialize of this class -- called by constructors
52     sub _init ($;%) {
53     my $self = shift;
54     my $DEFAULT = Message::Util::make_clone (\%DEFAULT);
55     my %option = @_;
56     $self->SUPER::_init (%$DEFAULT, %option);
57    
58     unless (defined $self->{option}->{output_epilogue}) {
59     $self->{option}->{output_epilogue} = $self->{option}->{format} !~ /http/;
60     }
61     $self->{option}->{value_type}->{body_part}->[1]->{-format}
62     =
63     my @ilist = qw/accept_coderange body_default_charset body_default_charset_input cte_default text_coderange/;
64     $self->{option}->{value_type}->{preamble} = ['Message::Body::TextPlain',
65     {-media_type => 'text', -media_subtype => '/multipart-preamble'},
66     \@ilist];
67     $self->{option}->{value_type}->{body_part} = sub {['Message::Entity',
68     {-format => $_[0]->{option}->{format} . '/' . 'mime-entity',
69     -body_default_media_type => $_[0]->{option}->{default_media_type},
70     -body_default_media_subtype => $_[0]->{option}->{default_media_subtype}},
71     \@ilist]};
72     $self->{option}->{value_type}->{epilogue} = ['Message::Body::TextPlain',
73     {-media_type => 'text', -media_subtype => '/multipart-epilogue'},
74     \@ilist];
75    
76     $self->{boundary} = $option{boundary};
77     if (!length $self->{boundary} && ref $self->{header}) {
78     my $ct = $self->{header}->field ('content-type', -new_item_unless_exist => 0);
79     $self->{boundary} = $ct->parameter ('boundary') if ref $ct;
80     }
81     }
82    
83     =item $body = Message::Body::Multipart->new ([%options])
84    
85     Constructs a new object. You might pass some options as parameters
86     to the constructor.
87    
88     =cut
89    
90     ## Inherited
91    
92     =item $body = Message::Body::Multipart->parse ($body, [%options])
93    
94     Constructs a new object with given field body. You might pass
95     some options as parameters to the constructor.
96    
97     =cut
98    
99     sub parse ($$;%) {
100     my $class = shift;
101     my $self = bless {}, $class;
102     my $body = shift;
103     $self->_init (@_);
104     my $b = $self->{boundary};
105     if (length $b) {
106     $self->{value} = [ split /\x0D\x0A--\Q$b\E[\x09\x20]*\x0D\x0A/, $body ];
107     if (length $self->{value}->[0]) {
108     my @p = split /(?:\x0D\x0A)?--\Q$b\E[\x09\x20]*\x0D\x0A/, $self->{value}->[0], 2;
109     $self->{preamble} = $p[0];
110     if (length $p[1]) {
111     $self->{value}->[0] = $p[1];
112     } else { shift (@{$self->{value}}) }
113     }
114     if (length $self->{value}->[-1]) {
115     my @p = split /\x0D\x0A--\Q$b\E--[\x09\x20]*(?:\x0D\x0A)?/, $self->{value}->[-1], 2;
116     $self->{value}->[-1] = $p[0];
117     $self->{epilogue} = $p[1];
118     }
119     } else {
120     $self->{preamble} = [ $body ];
121     }
122     if ($self->{option}->{parse_all}) {
123     $self->{value} = [map {
124     $self->_parse_value (body_part => $_);
125     } @{$self->{value}}];
126     $self->{preamble} = $self->_parse_value (preamble => $self->{preamble});
127     $self->{epilogue} = $self->_parse_value (epilogue => $self->{epilogue});
128     }
129     $self;
130     }
131    
132     =back
133    
134     =cut
135    
136 wakaba 1.2 ## add, item, delete, count
137    
138     ## item-by?, \$checked-item, {item-key => 1}, \%option
139     sub _item_match ($$\$\%\%) {
140     my $self = shift;
141     my ($by, $i, $list, $option) = @_;
142     return 0 unless ref $$i; ## Already removed
143     if ($by eq 'content-type') {
144     $$i = $self->_parse_value (body_part => $$i);
145     return 1 if ref $$i && $$list{$$i->content_type};
146     } elsif ($by eq 'content-id') {
147     $$i = $self->_parse_value (body_part => $$i);
148     return 1 if ref $$i && ( $$list{$$i->id} || $$list{'<'.$$i->id.'>'} );
149     }
150     0;
151     }
152     *_delete_match = \&_item_match;
153    
154     ## Returns returned item value \$item-value, \%option
155     sub _item_return_value ($\$\%) {
156     unless (ref ${$_[1]}) {
157     ${$_[1]} = $_[0]->_parse_value (body_part => ${$_[1]});
158     }
159     ${$_[1]};
160     }
161     *_add_return_value = \&_item_return_value;
162    
163     ## Returns returned (new created) item value $name, \%option
164     sub _item_new_value ($$\%) {
165     my $v = shift->_parse_value (body_part => '');
166     my ($key, $option) = @_;
167     if ($option->{by} eq 'content-type') {
168     $v->header->field ('content-type')->media_type ($key);
169     } elsif ($option->{by} eq 'content-id') {
170     $v->header->add ('content-id' => $key);
171     }
172     $v;
173     }
174    
175     sub _add_array_check ($$\%) {
176     my $self = shift;
177     my ($value, $option) = @_;
178     my $value_option = {};
179     if (ref $value eq 'ARRAY') {
180     ($value, %$value_option) = @$value;
181     }
182     $value = $self->_parse_value (body_part => $value) if $$option{parse};
183     $$option{parse} = 0;
184     (1, value => $value);
185     }
186    
187 wakaba 1.1 ## entity_header: Inherited
188    
189 wakaba 1.2 sub preamble ($;$) {
190     my $self = shift;
191     my $np = shift;
192     if (defined $np) {
193     $np = $self->_parse_value (preamble => $np) if $self->{option}->{parse_all};
194     $self->{preamble} = $np;
195     }
196     $self->{preamble};
197     }
198     sub epilogue ($;$) {
199     my $self = shift;
200     my $np = shift;
201     if (defined $np) {
202     $np = $self->_parse_value (epilogue => $np) if $self->{option}->{parse_all};
203     $self->{epilogue} = $np;
204     }
205     $self->{epilogue};
206     }
207    
208 wakaba 1.1 =head2 $self->stringify ([%option])
209    
210     Returns the C<body> as a string.
211    
212     =cut
213    
214     sub stringify ($;%) {
215     my $self = shift;
216     my %o = @_; my %option = %{$self->{option}};
217     for (grep {/^-/} keys %o) {$option{substr ($_, 1)} = $o{$_}}
218     my $max = $option{max} || $#{$self->{value}}+1; $max--;
219     $max = $#{$self->{value}} if $max > $#{$self->{value}};
220     my @parts = map { ''. $_ } @{$self->{value}}[0..$max];
221     my $b = $self->{boundary};
222     if ($b =~ $REG{NON_bchars} || length ($b) > 70) {
223     undef $b;
224     } elsif (substr ($b, -1, 1) eq "\x20") {
225     $b .= 'B';
226     }
227     my $blength = 45;
228     $b ||= $self->_generate_boundary ($blength);
229     my $i = 1; while ($i++) {
230     my @t = grep {/\Q--$b\E/} @parts;
231     last if @t == 0;
232     $b = $self->_generate_boundary ($blength);
233     if ($i > @BCHARS ** $blength) {
234     $blength++; $i = 1;
235     }
236     }
237     if (ref $self->{header}) {
238     $self->{header}->field ('content-type')->parameter (boundary => $b);
239     }
240     $self->{preamble}."\x0D\x0A--".$b."\x0D\x0A".
241     join ("\x0D\x0A--".$b."\x0D\x0A", @parts)
242     ."\x0D\x0A--$b--\x0D\x0A".
243     ($option{output_epilogue}? $self->{epilogue}: '');
244     }
245     *as_string = \&stringify;
246    
247     ## Inherited: option, clone
248    
249     ## $self->_option_recursive (\%argv)
250     sub _option_recursive ($\%) {
251     my $self = shift;
252     my $o = shift;
253     for (@{$self->{value}}) {
254     $_->option (%$o) if ref $_;
255     }
256     $self->{preamble}->option (%$o) if ref $self->{preamble};
257     $self->{epilogue}->option (%$o) if ref $self->{epilogue};
258     }
259    
260     sub _generate_boundary ($$) {
261     my $self = shift;
262     my $blength = shift || 45; ## Length of boundary
263     join('', map($BCHARS[rand @BCHARS], 1..$blength));
264     }
265    
266     =head1 SEE ALSO
267    
268     RFC 2046 <urn:ietf:rfc:2046>
269    
270     =head1 LICENSE
271    
272     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
273    
274     This program is free software; you can redistribute it and/or modify
275     it under the terms of the GNU General Public License as published by
276     the Free Software Foundation; either version 2 of the License, or
277     (at your option) any later version.
278    
279     This program is distributed in the hope that it will be useful,
280     but WITHOUT ANY WARRANTY; without even the implied warranty of
281     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
282     GNU General Public License for more details.
283    
284     You should have received a copy of the GNU General Public License
285     along with this program; see the file COPYING. If not, write to
286     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
287     Boston, MA 02111-1307, USA.
288    
289     =head1 CHANGE
290    
291     See F<ChangeLog>.
292 wakaba 1.2 $Date: 2002/06/09 10:57:16 $
293 wakaba 1.1
294     =cut
295    
296     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24