/[suikacvs]/messaging/manakai/lib/Message/Util.pm
Suika

Contents of /messaging/manakai/lib/Message/Util.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.14 - (hide annotations) (download)
Tue Jul 2 06:37:56 2002 UTC (24 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.13: +19 -5 lines
2002-07-02  Wakaba <w@suika.fam.cx>

	* Util.pm (decide_newline): New function.
	* Entity.pm (parse): Call Message::Util::decide_newline
	instead of local code.

1 wakaba 1.1
2     =head1 NAME
3    
4 wakaba 1.4 Message::Util -- Utilities for Message::* Perl modules.
5 wakaba 1.1
6     =head1 DESCRIPTION
7    
8 wakaba 1.4 Useful functions for Message::* Perl modules.
9     This module is only intended for internal use.
10     But some can be useful for general use.
11 wakaba 1.1
12     =cut
13    
14     package Message::Util;
15 wakaba 1.4 require 5.6.0;
16 wakaba 1.1 use strict;
17 wakaba 1.4 use re 'eval';
18 wakaba 1.13 use vars qw(%FMT2STR %REG $VERSION);
19 wakaba 1.14 $VERSION=do{my @r=(q$Revision: 1.13 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
20 wakaba 1.1
21 wakaba 1.5 use Carp ();
22 wakaba 1.12 require Message::MIME::EncodedWord;
23     require Message::MIME::Charset;
24 wakaba 1.1
25 wakaba 1.5 =head1 REGEXPS (%Message::Util::REG)
26 wakaba 1.4
27 wakaba 1.5 =head2 Naming Rules
28 wakaba 1.4
29 wakaba 1.5 key = *(prefix) [format] token-name
30    
31     prefix = 'M_' ;; With matching "(" ")"s
32     / 'S_' ;; Simple (not strict) expression
33     / 'NON_' ;; Negative character class
34     format = E<lt>specification id, such as C<http>E<gt> ;; if necessary
35     token-name = E<lt>BNF name =~ tr/-/_/E<gt>
36    
37     =cut
38    
39 wakaba 1.6 $REG{MATCH_NONE} = qr/(?!)/;
40     $REG{MATCH_ALL} = qr/[\x00-\xFF]/;
41 wakaba 1.5 ## Whitespace
42     $REG{WSP} = qr/[\x09\x20]/;
43 wakaba 1.7 $REG{FWS} = qr/[\x09\x20]*/; ## not same as 2822's
44 wakaba 1.5 ## Basic structure
45     $REG{comment} = qr/\x28(?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x27\x2A-\x5B\x5D-\xFF]|(??{$REG{comment}}))*\x29/;
46 wakaba 1.13 $REG{M_comment} = qr/\x28((?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x27\x2A-\x5B\x5D-\xFF]|(??{$REG{comment}}))*)\x29/;
47    
48 wakaba 1.5 $REG{quoted_string} = qr/\x22(?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\xFF])*\x22/;
49 wakaba 1.13 $REG{M_quoted_string} = qr/\x22((?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\xFF])*)\x22/;
50    
51 wakaba 1.5 $REG{domain_literal} = qr/\x5B(?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x5A\x5E-\xFF])*\x5D/;
52 wakaba 1.13 $REG{M_domain_literal} = qr/\x5B((?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x5A\x5E-\xFF])*)\x5D/;
53    
54     #$REG{angle_quoted} = qr/\x3C[\x09\x20\x21\x23-\x3B\x3D\x3F-\x5B\x5D\x5F\x61-\x7A\x7E]*\x3E/;
55     $REG{angle_qcontent} = qr/(?:$REG{quoted_string}|$REG{domain_literal}|[^\x3C\x3E\x22\x5B])+/;
56     $REG{angle_quoted} = qr/<$REG{angle_qcontent}>|<>/;
57     $REG{M_angle_quoted} = qr/<($REG{angle_qcontent})>|<>/;
58 wakaba 1.5
59    
60     =head2 tokens
61    
62     atext NON_atext 822.atext
63     atext_dot NON_atext_dot 822.atext / "."
64     NON_atext_dot_wsp 822.atext / "." / WSP
65     http_token NON_http_token http.token
66     NON_http_token_wsp http.token / WSP
67     attribute_char rfc2231.attribute-char
68 wakaba 1.6 NON_http_attribute_char http.token AND rfc2231.attribute-char
69 wakaba 1.5
70     =cut
71    
72     $REG{atext} = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2F\x30-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]+/;
73     $REG{atext_dot} = qr/[\x21\x23-\x27\x2A\x2B\x2D-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]+/;
74 wakaba 1.7 $REG{atext_dot_wsp} = qr/[\x09\x20\x21\x23-\x27\x2A\x2B\x2D-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]+/;
75     $REG{atext_dot8} = qr/[\x21\x23-\x27\x2A\x2B\x2D-\x39\x3D\x3F\x41-\x5A\x5E-\x7E\x80-\xFF]+/;
76 wakaba 1.6 $REG{token} = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+/;
77 wakaba 1.5 $REG{http_token} = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+/;
78     $REG{attribute_char} = qr/[\x21\x23-\x24\x26\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+/;
79    
80 wakaba 1.7 $REG{NON_atext} = qr/[^\x21\x23-\x27\x2A\x2B\x2D\x2F\x30-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]/;
81     $REG{NON_atext_wsp} = qr/[^\x09\x20\x21\x23-\x27\x2A\x2B\x2D\x2F\x30-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]/;
82 wakaba 1.5 $REG{NON_atext_dot} = qr/[^\x21\x23-\x27\x2A\x2B\x2D-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]/;
83     $REG{NON_atext_dot_wsp} = qr/[^\x09\x20\x21\x23-\x27\x2A\x2B\x2D-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]/;
84 wakaba 1.6 $REG{NON_token} = qr/[^\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]/;
85 wakaba 1.5 $REG{NON_http_token} = qr/[^\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
86     $REG{NON_http_token_wsp} = qr/[^\x09\x20\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
87 wakaba 1.6 $REG{NON_attribute_char} = qr/[^\x21\x23-\x24\x26\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]/;
88     $REG{NON_http_attribute_char} = qr/[^\x21\x23-\x24\x26\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
89 wakaba 1.8 $REG{NON_http_attribute_char_wsp} = qr/[^\x09\x20\x21\x23-\x24\x26\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
90 wakaba 1.6 ## Yes, C<attribute-char> does not appear in HTTP spec.
91 wakaba 1.5
92     $REG{dot_atom} = qr/$REG{atext}(?:$REG{FWS}\x2E$REG{FWS}$REG{atext})*/;
93 wakaba 1.7 $REG{dot_atom_dot} = qr/$REG{atext_dot}(?:$REG{FWS}\x2E$REG{FWS}$REG{atext_dot})*/;
94 wakaba 1.5 $REG{dot_word} = qr/(?:$REG{atext}|$REG{quoted_string})(?:$REG{FWS}\x2E$REG{FWS}(?:$REG{atext}|$REG{quoted_string}))*/;
95 wakaba 1.7 $REG{dot_word_dot} = qr/(?:$REG{atext_dot}|$REG{quoted_string})(?:$REG{FWS}\x2E$REG{FWS}(?:$REG{atext_dot}|$REG{quoted_string}))*/;
96 wakaba 1.5 $REG{phrase} = qr/(?:$REG{atext}|$REG{quoted_string})(?:$REG{atext}|$REG{quoted_string}|\.|$REG{FWS})*/;
97     ## RFC 822 phrase (not strict)
98    
99 wakaba 1.7 #$REG{domain} = qr/(?:$REG{dot_atom}|$REG{domain_literal})/;
100     $REG{domain} = qr/(?:$REG{dot_atom_dot}|$REG{domain_literal})/;
101     #$REG{addr_spec} = qr/$REG{dot_word}$REG{FWS}\x40$REG{FWS}$REG{domain}/;
102     $REG{addr_spec} = qr/$REG{dot_word_dot}$REG{FWS}\x40$REG{FWS}$REG{domain}/;
103     $REG{msg_id} = qr/<$REG{FWS}$REG{addr_spec}$REG{FWS}>/;
104    
105     $REG{M_addr_spec} = qr/($REG{dot_word_dot})$REG{FWS}\x40$REG{FWS}($REG{domain})/;
106    
107     $REG{date_time} = qr/(?:[A-Za-z]+$REG{FWS},$REG{FWS})?[0-9]+$REG{WSP}*[A-Za-z]+$REG{WSP}*[0-9]+$REG{WSP}+[0-9]+$REG{FWS}:$REG{WSP}*[0-9]+(?:$REG{FWS}:$REG{WSP}*[0-9]+)?$REG{FWS}(?:[A-Za-z]+|[+-]$REG{WSP}*[0-9]+)/;
108     $REG{asctime} = qr/[A-Za-z]+$REG{WSP}*[A-Za-z]+$REG{WSP}*[0-9]+$REG{WSP}+[0-9]+$REG{FWS}:$REG{WSP}*[0-9]+$REG{FWS}:$REG{WSP}*[0-9]+$REG{WSP}+[0-9]+/;
109    
110 wakaba 1.5 ## MIME encoded-word
111     $REG{M_encoded_word} = qr/=\x3F($REG{attribute_char})(?:\x2A($REG{attribute_char}))?\x3F($REG{attribute_char})\x3F([\x21-\x3E\x40-\x7E]+)\x3F=/;
112     $REG{S_encoded_word} = qr/=\x3F$REG{atext_dot}\x3F=/;
113     #$REG{S_encoded_word_comment} = qr/=\x3F[\x21-\x27\x2A-\x5B\x5D-\x7E]+\x3F=/;
114     ## not used anywhere
115 wakaba 1.4
116 wakaba 1.13 ## See also 'sprintxf'
117     %FMT2STR = (
118     char => sub {
119     my $p = $_[0];
120     if ($p->{ucs} =~ /^0[xob][0-9A-Fa-f]+$/) {
121     return pack 'U', oct $p->{ucs};
122     } elsif (defined $p->{ucs}) {
123     return pack 'U', $p->{ucs};
124     } else {
125     return "\x{FFFD}";
126     }
127     },
128     percent => '%',
129     );
130    
131 wakaba 1.4 =head1 STRUCTURED FIELD FUNCTIONS
132    
133     =over 4
134    
135 wakaba 1.5 =item $nocomment:-) = Message::Util::delete_comment ($string)
136 wakaba 1.4
137     Gets rid of all C<comment>s. Inserts a SP instead.
138    
139     =cut
140    
141     sub delete_comment ($) {
142     my $body = shift;
143 wakaba 1.5 $body =~ s{($REG{quoted_string}|$REG{domain_literal}|$REG{angle_quoted})|$REG{comment}}{
144 wakaba 1.4 my $o = $1; $o? $o : ' ';
145     }gex;
146     $body;
147     }
148    
149 wakaba 1.7 sub delete_wsp ($) {
150     my $body = shift;
151     $body =~ s{($REG{quoted_string}|$REG{domain_literal})|((?:$REG{token}|$REG{S_encoded_word})(?:$REG{WSP}+(?:$REG{token}|$REG{S_encoded_word}))+)|$REG{WSP}+}{
152     my ($o,$p) = ($1,$2);
153     if ($o) {$o}
154     elsif ($p) {$p=~s/$REG{WSP}+/\x20/g;$p}
155     else {''}
156     }gex;
157     $body;
158     }
159    
160     sub remove_meaningless_wsp ($) {
161     my $body = shift;
162     $body =~ s{($REG{quoted_string}|$REG{domain_literal})|$REG{WSP}+}{
163     $1 || '';
164     }gex;
165     $body;
166     }
167    
168 wakaba 1.8 sub wsps_to_sp ($) {
169     my $body = shift;
170     $body =~ s{($REG{quoted_string}|$REG{domain_literal})|$REG{WSP}+}{
171     $1 || ' ';
172     }gex;
173     $body;
174     }
175    
176 wakaba 1.5 =item $unquoted = Message::Util::unquote_ccontent ($string)
177 wakaba 1.4
178     Unquotes C<quoted-pair> in C<comment>s.
179    
180     =cut
181    
182     sub unquote_ccontent ($) {
183     my $comment = shift;
184     $comment =~ s{$REG{M_comment}}{
185     my $ctext = $1;
186     $ctext =~ s/\x5C([\x00-\xFF])/$1/g;
187     '('.$ctext.')';
188     }goex;
189     $comment;
190     }
191    
192 wakaba 1.5 =item $unquoted = Message::Util::unquote_quoted_string ($string)
193 wakaba 1.4
194     Unquotes C<quoted-pair> in C<quoted-string>s and
195     unquotes C<quoted-string> (or gets rid of C<DQUOTE>s).
196    
197     =cut
198    
199     sub unquote_quoted_string ($) {
200     my $quoted_string = shift;
201     $quoted_string =~ s{$REG{M_quoted_string}}{
202     my $qtext = $1;
203     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
204     $qtext;
205     }goex;
206     $quoted_string;
207     }
208    
209 wakaba 1.5 =item Message::Util::unquote_if_quoted_string ($string)
210    
211     Unquotes if and only if given string is A C<quoted-string>.
212     This function returns two value, (C<$unquoted-string>,
213     C<$was-quoted-string?>).
214    
215     =cut
216    
217     sub unquote_if_quoted_string ($) {
218     my $quoted_string = shift; my $isq = 0;
219     $quoted_string =~ s{^$REG{M_quoted_string}$}{
220     my $qtext = $1;
221     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
222     $isq = 1;
223     $qtext;
224     }goex;
225     wantarray? ($quoted_string, $isq): $quoted_string;
226     }
227    
228 wakaba 1.13 sub unquote_if_angle_quoted ($) {
229     my $quoted_string = shift; my $isq = 0;
230     $quoted_string =~ s{^$REG{M_angle_quoted}$}{
231     my $qtext = $1;
232     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
233     $isq = 1;
234     $qtext;
235     }goex;
236     wantarray? ($quoted_string, $isq): $quoted_string;
237     }
238    
239 wakaba 1.7 sub unquote_if_domain_literal ($) {
240     my $quoted_string = shift; my $isq = 0;
241     $quoted_string =~ s{^$REG{M_domain_literal}$}{
242     my $qtext = $1;
243     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
244     $isq = 1;
245     $qtext;
246     }goex;
247     wantarray? ($quoted_string, $isq): $quoted_string;
248     }
249    
250 wakaba 1.5 =item $quoted = Message::Util::quote_unsafe_string ($string)
251 wakaba 1.4
252     Quotes string itself by C<DQUOTES> if it contains of
253     I<unsafe> character.
254    
255     Default I<unsafe> is defined as E<lt>not ( atom / "." / %x09 / %x20 ) E<gt>.
256    
257     =cut
258    
259 wakaba 1.5 sub quote_unsafe_string ($;%) {
260 wakaba 1.4 my $string = shift;
261 wakaba 1.5 my %option = @_;
262     $option{unsafe} ||= 'NON_atext_dot';
263 wakaba 1.6 $option{unsafe_regex} = $option{unsafe} if $option{unsafe} =~ /^\(\?-xism:/;
264 wakaba 1.14 $option{unsafe_regex} ||= qr/$REG{$option{unsafe}}|$REG{WSP}$REG{WSP}|^$REG{WSP}|$REG{WSP}$|^=\x3F/;
265 wakaba 1.7 my $r = qr/([\x22\x5C])([\x21-\x7E])?/;
266     $r = qr/([\x22\x5C])/ if $option{strict}; ## usefor-article
267 wakaba 1.6 if ($string =~ /$option{unsafe_regex}/) {
268 wakaba 1.7 $string =~ s/$r/"\x5C$1".(defined $2?"\x5C$2":'')/ge;
269 wakaba 1.4 $string = '"'.$string.'"';
270     }
271     $string;
272     }
273    
274 wakaba 1.7 sub quote_unsafe_domain ($) {
275     my $string = shift;
276     if ($string =~ /^\[[^\[\]]+\]$/) {
277     #
278     } elsif ($string =~ /$REG{NON_atext_dot}/ || $string =~ /^\.|\.$/) {
279 wakaba 1.14 $string =~ s/([\x0D\x5B-\x5D])/\x5C$1/g;
280 wakaba 1.7 $string = '['.$string.']';
281     }
282     $string;
283     }
284    
285     sub remove_wsp ($) {
286     my $s = shift;
287     $s =~ s{($REG{quoted_string}|$REG{domain_literal}|$REG{angle_quoted})|$REG{WSP}+}{
288     $1
289     }gex;
290     $s;
291     }
292    
293 wakaba 1.5 =item $Message::Util::make_clone ($parent)
294    
295     Returns clone.
296    
297     =cut
298    
299     sub make_clone ($) {
300     my $s = shift;
301 wakaba 1.7 if (ref $s eq 'ARRAY') {
302     $s = [map {make_clone ($_)} @$s];
303     } elsif (ref $s eq 'HASH') {
304     $s = {map {make_clone ($_)} (%$s)};
305 wakaba 1.9 } elsif (ref $s && ref $s ne 'CODE' && ref $s ne 'Regexp') {
306 wakaba 1.5 $s = $s->clone;
307     }
308     $s;
309     }
310    
311 wakaba 1.4 =head1 ENCODER and DECODER
312    
313     =over 4
314    
315     =item Message::Util::encode_header_string ($yourself, $string, [%options])
316    
317     =cut
318    
319 wakaba 1.2 sub encode_header_string ($$;%) {
320 wakaba 1.3 my $yourself = shift; my $s = shift; my %o = @_;
321     $o{charset} ||= $yourself->{option}->{encoding_after_encode};
322 wakaba 1.1 $o{charset} = Message::MIME::Charset::name_normalize ($o{charset});
323     $o{current_charset} = Message::MIME::Charset::name_normalize ($o{current_charset});
324 wakaba 1.11 my ($t,%r) = Message::MIME::Charset::encode ($o{charset}, $s);
325 wakaba 1.1 my @o = (language => $o{language});
326 wakaba 1.12 if ($r{success}) { ## Convertion succeed
327 wakaba 1.11 $o{charset} = $r{charset} if $r{charset};
328 wakaba 1.13 $o{charset} = '' if $o{charset} =~ /\*/;
329     (value => $t, @o, Message::MIME::Charset::name_minimumize ($o{charset}, $t));
330 wakaba 1.1 } else { ## Fault
331 wakaba 1.13 $o{current_charset} = '' if $o{current_charset} =~ /\*/;
332     (value => $t, @o,
333     Message::MIME::Charset::name_minimumize ($o{current_charset}, $t));
334 wakaba 1.1 }
335     }
336    
337 wakaba 1.2 sub decode_header_string ($$;%) {
338 wakaba 1.3 my $yourself = shift; my $s = shift; my %o = @_;
339     $o{charset} ||= $yourself->{option}->{encoding_before_decode};
340 wakaba 1.1 $o{charset} = Message::MIME::Charset::name_normalize ($o{charset});
341 wakaba 1.5 my ($t, $r); ## decoded-text, success?
342 wakaba 1.7 if ($o{type} !~ /quoted|encoded|domain|word/) {
343 wakaba 1.5 my (@s, @r);
344 wakaba 1.13 $s =~ s{(([\x09\x20]*(?:\x5C[\x00-\xFF]
345     |[\x00-\x08\x0A-\x1F\x21-\x5B\x5D-\xFF])+|[\x09\x20]+$))}
346     { push @s, $1; '' }goesx;
347 wakaba 1.5 for my $i (0..$#s) {
348     if ($s[$i] =~ /^($REG{FWS})$REG{M_encoded_word}$/) {
349     my ($t, $w) = ('', $1);
350     ($t, $r[$i]) = (Message::MIME::EncodedWord::_decode_eword ($2, $3, $4, $5));
351     if ($r[$i]) {
352     $s[$i] = $t;
353     if ($i == 0 || $r[$i-1] == 0) {
354     $s[$i] = $w.$s[$i];
355     }
356     }
357     } else {
358     my ($u, $q) = ($s[$i], 0);
359 wakaba 1.13 $u =~ s/\x5C([\x00-\xFF])/$1/g unless $o{type} =~ /text/;
360 wakaba 1.5 ($u,$q) = Message::MIME::Charset::decode ($o{charset}, $u);
361     $s[$i] = $u if $q;
362     }
363     }
364     $t = join '', @s; $r = 1;
365     } else {
366     ($t,$r) = Message::MIME::Charset::decode ($o{charset}, $s);
367     }
368     $r ? (value => $t, language => $o{language}): ## suceess
369 wakaba 1.1 (value => $s, language => $o{language},
370     charset => ($o{charset}=~/\*/?'':$o{charset})); ## fault
371     }
372    
373     sub encode_body_string {
374 wakaba 1.3 my $yourself = shift; my $s = shift; my %o = @_;
375     $o{charset} ||= $yourself->{option}->{encoding_after_encode};
376 wakaba 1.1 $o{charset} = Message::MIME::Charset::name_normalize ($o{charset});
377     $o{current_charset} = Message::MIME::Charset::name_normalize ($o{current_charset});
378 wakaba 1.11 my ($t,%r) = Message::MIME::Charset::encode ($o{charset}, $s);
379 wakaba 1.1 my @o = ();
380 wakaba 1.11 if ($r{success}) { ## Convertion successed
381     $o{charset} = $r{charset} if $r{charset};
382 wakaba 1.13 $o{charset} = '' if $o{charset} =~ /\*/;
383     (value => $t, @o, Message::MIME::Charset::name_minimumize ($o{charset}, $t));
384     } else { ## Fault
385     $o{current_charset} = '' if $o{current_charset} =~ /\*/;
386     (value => $t, @o,
387     Message::MIME::Charset::name_minimumize ($o{current_charset}, $t));
388 wakaba 1.1 }
389     }
390    
391     sub decode_body_string {
392 wakaba 1.3 my $yourself = shift; my $s = shift; my %o = @_;
393     $o{charset} ||= $yourself->{option}->{encoding_before_decode};
394 wakaba 1.1 $o{charset} = Message::MIME::Charset::name_normalize ($o{charset});
395     my ($t,$r) = Message::MIME::Charset::decode ($o{charset}, $s);
396     $r>0 ? (value => $t): ## suceess
397     (value => $s,
398     charset => ($o{charset}=~/\*/?'':$o{charset})); ## fault
399     }
400    
401 wakaba 1.5 =item Message::Util::decode_quoted_string ($yourself, $quoted-string)
402    
403     Returns unquoted and decoded a given C<quoted-string>
404     or a string containing one or multiple C<quoted-string>s.
405    
406     =cut
407    
408 wakaba 1.7 sub decode_quoted_string ($$;%) {
409 wakaba 1.5 my $yourself = shift;
410     my $quoted_string = shift;
411 wakaba 1.7 my %option = @_;
412     $option{type} ||= 'phrase';
413 wakaba 1.5 $quoted_string =~ s{$REG{M_quoted_string}|([^\x22]+)}{
414     my ($qtext, $t) = ($1, $2);
415     if (length $t) {
416 wakaba 1.8 $t =~ s/$REG{WSP}+/\x20/g;
417 wakaba 1.5 my %s = &{$yourself->{option}->{hook_decode_string}}
418 wakaba 1.7 ($yourself, $t, type => $option{type},
419     charset => $option{charset});
420 wakaba 1.5 $s{value};
421     } else {
422     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
423     my %s = &{$yourself->{option}->{hook_decode_string}}
424 wakaba 1.7 ($yourself, $qtext, type => $option{type}.'/quoted',
425     charset => $option{charset});
426 wakaba 1.5 $s{value};
427     }
428     }goex;
429     $quoted_string;
430     }
431    
432 wakaba 1.4 =item Message::Util::encode_qcontent ($yourself, $string)
433    
434     Encodes (by C<hook_encode_string> of C<$yourself-E<gt>{option}>)
435     C<qcontent> (content of C<quoted-string>) within C<$string>.
436    
437     =cut
438    
439     sub encode_qcontent ($$) {
440     my $yourself = shift;
441     my $quoted_strings = shift;
442     $quoted_strings =~ s{$REG{M_quoted_string}}{
443     my ($qtext) = ($1);
444     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
445     my %s = &{$yourself->{option}->{hook_encode_string}} ($yourself, $qtext,
446     type => 'phrase/quoted');
447 wakaba 1.14 $s{value} =~ s/([\x0D\x22\x5C])([\x20-\xFF])?/"\x5C$1".($2?"\x5C$2":'')/ges;
448 wakaba 1.4 '"'.$s{value}.'"';
449     }goex;
450     $quoted_strings;
451     }
452    
453     =item Message::Util::decode_qcontent ($yourself, $string)
454    
455     Decodes (by C<hook_decode_string> of C<$yourself-E<gt>{option}>)
456     C<qcontent> (content of C<quoted-string>) within C<$string>.
457    
458     =cut
459    
460     sub decode_qcontent ($$) {
461     my $yourself = shift;
462     my $quoted_string = shift;
463     $quoted_string =~ s{$REG{M_quoted_string}}{
464     my ($qtext) = ($1);
465     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
466     my %s = &{$yourself->{option}->{hook_decode_string}} ($yourself, $qtext,
467     type => 'phrase/quoted');
468     $s{value} =~ s/([\x22\x5C])([\x20-\xFF])?/"\x5C$1".($2?"\x5C$2":'')/ge;
469     '"'.$s{value}.'"';
470     }goex;
471     $quoted_string;
472     }
473    
474     =item @comments = Message::Util::comment_to_array ($youtself, $comments)
475    
476     Replaces C<comment>s to C< > (a SP), decodes C<ccontent>s,
477     and returns them as array.
478    
479     =cut
480    
481     sub comment_to_array ($$) {
482     my $yourself = shift;
483     my $body = shift;
484     my @r = ();
485     $body =~ s{($REG{quoted_string}|$REG{domain_literal})|$REG{M_comment}}{
486     my ($o, $c) = ($1, $2);
487     if ($o) {$o}
488     else {
489     push @r, decode_ccontent ($yourself, $c);
490     ' ';
491     }
492     }gex;
493     @r;
494     }
495    
496 wakaba 1.8 sub delete_comment_to_array ($$;%) {
497 wakaba 1.7 my $yourself = shift;
498     my $body = shift;
499 wakaba 1.8 my %option = @_;
500     my $areg = ''; $areg = '|'.$REG{angle_quoted} if $option{-use_angle_quoted};
501 wakaba 1.7 my @r = ();
502 wakaba 1.8 $body =~ s{($REG{quoted_string}|$REG{domain_literal}$areg)|$REG{M_comment}}{
503 wakaba 1.7 my ($o, $c) = ($1, $2);
504     if ($o) {$o}
505     else {
506     push @r, decode_ccontent ($yourself, $c);
507     ' ';
508     }
509     }gex;
510     ($body, @r);
511     }
512    
513 wakaba 1.4 =item Message::Util::encode_ccontent ($yourself, $ccontent)
514    
515     Encodes C<ccontent> (content of C<comment>).
516    
517     =cut
518    
519     sub encode_ccontent ($$) {
520     my $yourself = shift;
521     my $ccontent = shift;
522     my %f = &{$yourself->{option}->{hook_encode_string}} ($yourself,
523     $ccontent, type => 'ccontent');
524 wakaba 1.9 $f{value} =~ s/([\x28\x29\x5C]|\x3D\x3F)([\x21-\x7E])?/"\x5C$1".(defined $2?"\x5C$2":'')/ge;
525 wakaba 1.4 $f{value};
526     }
527    
528     =item Message::Util::decode_ccontent ($yourself, $ccontent)
529    
530     Decodes C<ccontent> (content of C<comment>).
531    
532     =cut
533    
534     sub decode_ccontent ($$) {
535 wakaba 1.12 Message::MIME::EncodedWord::decode_ccontent (@_);
536 wakaba 1.4 }
537    
538 wakaba 1.13 sub sprintxf ($;\%) {
539     my $format = shift;
540     my $gparam = shift;
541     $format =~ s{%([A-Za-z0-9_]+)(?:\(([^\x29]*)\))?;}{
542     my ($f, $a) = ($1, $2);
543     my $function = $gparam->{fmt2str}->{$f} || $FMT2STR{$f};
544     if (ref $function) {
545     my %a;
546     for (split /[\x09\x20]*,[\x09\x20]*/, $a) {
547     if (/^([^=]*[^\x09\x20=])[\x09\x20]*=>[\x09\x20]*([^\x09\x20].*)$/) {
548     $a{ unquote_if_quoted_string ($1) } = unquote_if_quoted_string ($2);
549     } else {
550     $a{ unquote_if_quoted_string ($_) } = 1;
551     }
552     }
553     my $r = &$function (\%a, $gparam);
554     length $r? $a{prefix}.$r.$a{suffix}: '';
555     } elsif (length $function) {
556     $function;
557     } else {
558     "[$f: undef]";
559     }
560     }gex;
561     $format;
562     }
563 wakaba 1.10
564 wakaba 1.14 sub decide_newline ($) {
565     my $s = shift;
566     my $nl = "\x0D\x0A";
567     my $crlf = $s =~ s/\x0D\x0A/\x0D\x0A/gs;
568     my $lfcr = $s =~ s/\x0A\x0D/\x0A\x0D/gs;
569     my $cr = $s =~ s/\x0D(?!\x0A)/\x0D/gs;
570     my $lf = $s =~ s/(?<!\x0D)\x0A/\x0A/gs;
571     if ($crlf >= $cr && $crlf >= $lf && $crlf >= $lfcr ) { $nl = "\x0D\x0A" }
572     elsif ($lfcr >= $cr && $lfcr >= $lf) { $nl = "\x0A\x0D" }
573     elsif ($cr >= $lf) { $nl = "\x0D" }
574     else { $nl = "\x0A" }
575     $nl;
576     }
577    
578 wakaba 1.1 =head1 LICENSE
579    
580     Copyright 2002 wakaba E<lt>[email protected]<gt>.
581    
582     This program is free software; you can redistribute it and/or modify
583     it under the terms of the GNU General Public License as published by
584     the Free Software Foundation; either version 2 of the License, or
585     (at your option) any later version.
586    
587     This program is distributed in the hope that it will be useful,
588     but WITHOUT ANY WARRANTY; without even the implied warranty of
589     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
590     GNU General Public License for more details.
591    
592     You should have received a copy of the GNU General Public License
593     along with this program; see the file COPYING. If not, write to
594     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
595     Boston, MA 02111-1307, USA.
596    
597     =head1 CHANGE
598    
599     See F<ChangeLog>.
600 wakaba 1.14 $Date: 2002/06/23 12:20:11 $
601 wakaba 1.1
602     =cut
603    
604     1;

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24