/[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.21 - (hide annotations) (download)
Thu Aug 1 06:43:19 2002 UTC (24 years ago) by wakaba
Branch: MAIN
Changes since 1.20: +25 -2 lines
2002-07-28  Wakaba <w@suika.fam.cx>

	* Entity.pm (destination, sender): New methods.
	* Tool.pm (escape_from, unescape_from): New functions.

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 wakaba 1.20 Note that there is another module, Message::Tool.
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.17 use vars qw(%FMT2STR %OPTION %REG $VERSION);
19 wakaba 1.21 $VERSION=do{my @r=(q$Revision: 1.20 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
20 wakaba 1.1
21 wakaba 1.20 require 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 wakaba 1.16 $REG{NON_base64alphabet} = qr#[^A-Za-z0-9+/=]#;
93    
94 wakaba 1.5 $REG{dot_atom} = qr/$REG{atext}(?:$REG{FWS}\x2E$REG{FWS}$REG{atext})*/;
95 wakaba 1.7 $REG{dot_atom_dot} = qr/$REG{atext_dot}(?:$REG{FWS}\x2E$REG{FWS}$REG{atext_dot})*/;
96 wakaba 1.5 $REG{dot_word} = qr/(?:$REG{atext}|$REG{quoted_string})(?:$REG{FWS}\x2E$REG{FWS}(?:$REG{atext}|$REG{quoted_string}))*/;
97 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}))*/;
98 wakaba 1.5 $REG{phrase} = qr/(?:$REG{atext}|$REG{quoted_string})(?:$REG{atext}|$REG{quoted_string}|\.|$REG{FWS})*/;
99     ## RFC 822 phrase (not strict)
100    
101 wakaba 1.7 #$REG{domain} = qr/(?:$REG{dot_atom}|$REG{domain_literal})/;
102     $REG{domain} = qr/(?:$REG{dot_atom_dot}|$REG{domain_literal})/;
103     #$REG{addr_spec} = qr/$REG{dot_word}$REG{FWS}\x40$REG{FWS}$REG{domain}/;
104     $REG{addr_spec} = qr/$REG{dot_word_dot}$REG{FWS}\x40$REG{FWS}$REG{domain}/;
105     $REG{msg_id} = qr/<$REG{FWS}$REG{addr_spec}$REG{FWS}>/;
106    
107     $REG{M_addr_spec} = qr/($REG{dot_word_dot})$REG{FWS}\x40$REG{FWS}($REG{domain})/;
108    
109     $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]+)/;
110     $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]+/;
111    
112 wakaba 1.5 ## MIME encoded-word
113     $REG{M_encoded_word} = qr/=\x3F($REG{attribute_char})(?:\x2A($REG{attribute_char}))?\x3F($REG{attribute_char})\x3F([\x21-\x3E\x40-\x7E]+)\x3F=/;
114     $REG{S_encoded_word} = qr/=\x3F$REG{atext_dot}\x3F=/;
115     #$REG{S_encoded_word_comment} = qr/=\x3F[\x21-\x27\x2A-\x5B\x5D-\x7E]+\x3F=/;
116     ## not used anywhere
117 wakaba 1.4
118 wakaba 1.13 ## See also 'sprintxf'
119     %FMT2STR = (
120     char => sub {
121     my $p = $_[0];
122     if ($p->{ucs} =~ /^0[xob][0-9A-Fa-f]+$/) {
123     return pack 'U', oct $p->{ucs};
124     } elsif (defined $p->{ucs}) {
125     return pack 'U', $p->{ucs};
126     } else {
127     return "\x{FFFD}";
128     }
129     },
130     percent => '%',
131     );
132    
133 wakaba 1.4 =head1 STRUCTURED FIELD FUNCTIONS
134    
135     =over 4
136    
137 wakaba 1.5 =item $nocomment:-) = Message::Util::delete_comment ($string)
138 wakaba 1.4
139     Gets rid of all C<comment>s. Inserts a SP instead.
140    
141     =cut
142    
143     sub delete_comment ($) {
144     my $body = shift;
145 wakaba 1.5 $body =~ s{($REG{quoted_string}|$REG{domain_literal}|$REG{angle_quoted})|$REG{comment}}{
146 wakaba 1.4 my $o = $1; $o? $o : ' ';
147     }gex;
148     $body;
149     }
150    
151 wakaba 1.7 sub delete_wsp ($) {
152     my $body = shift;
153     $body =~ s{($REG{quoted_string}|$REG{domain_literal})|((?:$REG{token}|$REG{S_encoded_word})(?:$REG{WSP}+(?:$REG{token}|$REG{S_encoded_word}))+)|$REG{WSP}+}{
154     my ($o,$p) = ($1,$2);
155     if ($o) {$o}
156     elsif ($p) {$p=~s/$REG{WSP}+/\x20/g;$p}
157     else {''}
158     }gex;
159     $body;
160     }
161    
162     sub remove_meaningless_wsp ($) {
163     my $body = shift;
164     $body =~ s{($REG{quoted_string}|$REG{domain_literal})|$REG{WSP}+}{
165     $1 || '';
166     }gex;
167     $body;
168     }
169    
170 wakaba 1.8 sub wsps_to_sp ($) {
171     my $body = shift;
172     $body =~ s{($REG{quoted_string}|$REG{domain_literal})|$REG{WSP}+}{
173     $1 || ' ';
174     }gex;
175     $body;
176     }
177    
178 wakaba 1.21 =item Message::Util::quote_ccontent ($yourself, $ccontent)
179    
180     Quotes C<ccontent> (to be).
181    
182     =cut
183    
184     sub quote_ccontent ($;%) {
185     my $ccontent = shift;
186     my %option = @_;
187     if ($option{strict_quoted_pair}) {
188     $ccontent =~ s/([\x28\x29\x5C]|\x3D\x3F)/\x5C$1/g;
189     } else {
190     $ccontent =~ s/([\x28\x29\x5C]|\x3D\x3F)([\x21-\x7E])?/"\x5C$1".(defined $2?"\x5C$2":'')/ge;
191     }
192     $ccontent;
193     }
194    
195 wakaba 1.5 =item $unquoted = Message::Util::unquote_ccontent ($string)
196 wakaba 1.4
197     Unquotes C<quoted-pair> in C<comment>s.
198    
199     =cut
200    
201     sub unquote_ccontent ($) {
202     my $comment = shift;
203     $comment =~ s{$REG{M_comment}}{
204     my $ctext = $1;
205     $ctext =~ s/\x5C([\x00-\xFF])/$1/g;
206     '('.$ctext.')';
207     }goex;
208     $comment;
209     }
210    
211 wakaba 1.5 =item $unquoted = Message::Util::unquote_quoted_string ($string)
212 wakaba 1.4
213     Unquotes C<quoted-pair> in C<quoted-string>s and
214     unquotes C<quoted-string> (or gets rid of C<DQUOTE>s).
215    
216     =cut
217    
218     sub unquote_quoted_string ($) {
219     my $quoted_string = shift;
220     $quoted_string =~ s{$REG{M_quoted_string}}{
221     my $qtext = $1;
222     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
223     $qtext;
224     }goex;
225     $quoted_string;
226     }
227    
228 wakaba 1.5 =item Message::Util::unquote_if_quoted_string ($string)
229    
230     Unquotes if and only if given string is A C<quoted-string>.
231     This function returns two value, (C<$unquoted-string>,
232     C<$was-quoted-string?>).
233    
234     =cut
235    
236     sub unquote_if_quoted_string ($) {
237     my $quoted_string = shift; my $isq = 0;
238     $quoted_string =~ s{^$REG{M_quoted_string}$}{
239     my $qtext = $1;
240     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
241     $isq = 1;
242     $qtext;
243     }goex;
244     wantarray? ($quoted_string, $isq): $quoted_string;
245     }
246    
247 wakaba 1.13 sub unquote_if_angle_quoted ($) {
248     my $quoted_string = shift; my $isq = 0;
249     $quoted_string =~ s{^$REG{M_angle_quoted}$}{
250     my $qtext = $1;
251     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
252     $isq = 1;
253     $qtext;
254     }goex;
255     wantarray? ($quoted_string, $isq): $quoted_string;
256     }
257    
258 wakaba 1.7 sub unquote_if_domain_literal ($) {
259     my $quoted_string = shift; my $isq = 0;
260     $quoted_string =~ s{^$REG{M_domain_literal}$}{
261     my $qtext = $1;
262     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
263     $isq = 1;
264     $qtext;
265     }goex;
266     wantarray? ($quoted_string, $isq): $quoted_string;
267     }
268    
269 wakaba 1.5 =item $quoted = Message::Util::quote_unsafe_string ($string)
270 wakaba 1.4
271     Quotes string itself by C<DQUOTES> if it contains of
272     I<unsafe> character.
273    
274     Default I<unsafe> is defined as E<lt>not ( atom / "." / %x09 / %x20 ) E<gt>.
275    
276     =cut
277    
278 wakaba 1.5 sub quote_unsafe_string ($;%) {
279 wakaba 1.4 my $string = shift;
280 wakaba 1.5 my %option = @_;
281     $option{unsafe} ||= 'NON_atext_dot';
282 wakaba 1.6 $option{unsafe_regex} = $option{unsafe} if $option{unsafe} =~ /^\(\?-xism:/;
283 wakaba 1.14 $option{unsafe_regex} ||= qr/$REG{$option{unsafe}}|$REG{WSP}$REG{WSP}|^$REG{WSP}|$REG{WSP}$|^=\x3F/;
284 wakaba 1.7 my $r = qr/([\x22\x5C])([\x21-\x7E])?/;
285     $r = qr/([\x22\x5C])/ if $option{strict}; ## usefor-article
286 wakaba 1.6 if ($string =~ /$option{unsafe_regex}/) {
287 wakaba 1.7 $string =~ s/$r/"\x5C$1".(defined $2?"\x5C$2":'')/ge;
288 wakaba 1.4 $string = '"'.$string.'"';
289     }
290     $string;
291     }
292    
293 wakaba 1.7 sub quote_unsafe_domain ($) {
294     my $string = shift;
295     if ($string =~ /^\[[^\[\]]+\]$/) {
296     #
297     } elsif ($string =~ /$REG{NON_atext_dot}/ || $string =~ /^\.|\.$/) {
298 wakaba 1.14 $string =~ s/([\x0D\x5B-\x5D])/\x5C$1/g;
299 wakaba 1.7 $string = '['.$string.']';
300     }
301     $string;
302     }
303    
304     sub remove_wsp ($) {
305     my $s = shift;
306     $s =~ s{($REG{quoted_string}|$REG{domain_literal}|$REG{angle_quoted})|$REG{WSP}+}{
307     $1
308     }gex;
309     $s;
310     }
311    
312 wakaba 1.15 =item $encoded = Message::Util::encode_printable_string ($string)
313     =item $decoded = Message::Util::decode_printable_string ($string)
314    
315     Encodes or decodes string in PrintableString, described in
316     RFC 1327, RFC 2156. These functions supports 8bit octets encoded
317     as '(ddd)' format, although RFC 2156 allows it for only 7bit
318     octets.
319    
320     =cut
321    
322     my %To_Printable_String = (
323     '@' => '(a)',
324     '%' => '(p)',
325     '!' => '(b)',
326     '"' => '(q)',
327     '_' => '(u)',
328     '(' => '(l)',
329     ')' => '(r)',
330     );
331     my %From_Printable_String = reverse %To_Printable_String;
332     sub encode_printable_string ($) {
333     my $s = shift;
334     $s =~ s{ ([^0-9A-Za-z\x20'+,./:=?-]) }{
335     my $c = $1;
336     unless ($To_Printable_String{$c}) {
337     $To_Printable_String{$c} = sprintf '(%03d)', ord $c;
338     }
339     $To_Printable_String{$c};
340     }gex;
341     $s;
342     }
343     sub decode_printable_string ($) {
344     my $s = shift;
345     $s =~ s{ \( ([0-9A-Za-z]+) \) }{
346     my $c = lc $1; my $p = "($c)";
347     if ($c !~ /[^0-9]/) {
348     $From_Printable_String{$p} = pack 'C', 0+$c;
349     } elsif (!defined $From_Printable_String{$p}) {
350     $From_Printable_String{$p} = $p; ## Invalid!
351     }
352     $From_Printable_String{$p};
353     }gex;
354     $s;
355     }
356    
357     =item $encoded = Message::Util::encode_t61_string ($string)
358     =item $decoded = Message::Util::decode_t61_string ($string)
359    
360     Encodes or decodes string in T.61String described in RFC 1327,
361     RFC 2056.
362    
363     =cut
364    
365     sub encode_t61_string ($) {
366     my $s = shift;
367     $s =~ s{ ([^0-9A-Za-z\x20'+,./:=?-]) }{
368     sprintf '{%03d}', ord $1;
369     }gex;
370     $s;
371     }
372     sub decode_t61_string ($) {
373     my $s = shift;
374     $s =~ s{ \{ ([0-9]+) \} }{
375     my $c = $1; my $i = 0;
376     my $r = '';
377     while (my $d = substr ($c, $i * 3, 3)) {
378     $r .= pack 'C', 0+$d; $i++;
379     }
380     $r;
381     }gex;
382     $s;
383     }
384     =item $encoded = Message::Util::encode_printable_string ($string)
385     =item $decoded = Message::Util::decode_printable_string ($string)
386    
387     Encodes or decodes string in RFC 822 with restricted encoding,
388     defined by RFC 1137.
389    
390     =cut
391    
392     my %To_Encoded_Atom = (
393     "\x20" => '_',
394     '_' => '#u#',
395     '(' => '#l#',
396     ')' => '#r#',
397     ',' => '#m#',
398     ':' => '#c#',
399     "\x5C" => '#b#',
400     '#' => '#h#',
401     '=' => '#e#',
402     '/' => '#s#',
403     );
404     my %From_Encoded_Atom = reverse %To_Encoded_Atom;
405     sub encode_restricted_rfc822 ($) {
406     my $s = shift;
407     $s =~ s{ ([^\x21\x24-\x27\x2A\x2B\x2D\x2F\x30-\x39\x3D\x3F\x41-\x5A\x5E\x60-\x7E]) }{
408     my $c = $1; ## \x2E \x40
409     unless ($To_Encoded_Atom{$c}) {
410     $To_Encoded_Atom{$c} = sprintf '#%03d#', ord $c;
411     }
412     $To_Encoded_Atom{$c};
413     }gex;
414     $s;
415     }
416     sub decode_restricted_rfc822 ($) {
417     my $s = shift;
418     $s =~ s{ \# ([0-9A-Za-z]+) \# | _ }{
419     my $c = lc $1; my $p = "#$c#";
420     if ($c !~ /[^0-9]/) {
421     $From_Encoded_Atom{$p} = pack 'C', 0+$c;
422     } elsif (!defined $From_Encoded_Atom{$p}) {
423     $From_Encoded_Atom{$p} = $p; ## Invalid!
424     }
425     $From_Encoded_Atom{$p};
426     }gex;
427     $s;
428     }
429    
430 wakaba 1.5 =item $Message::Util::make_clone ($parent)
431    
432     Returns clone.
433    
434     =cut
435    
436     sub make_clone ($) {
437     my $s = shift;
438 wakaba 1.7 if (ref $s eq 'ARRAY') {
439     $s = [map {make_clone ($_)} @$s];
440     } elsif (ref $s eq 'HASH') {
441     $s = {map {make_clone ($_)} (%$s)};
442 wakaba 1.9 } elsif (ref $s && ref $s ne 'CODE' && ref $s ne 'Regexp') {
443 wakaba 1.5 $s = $s->clone;
444     }
445     $s;
446     }
447    
448 wakaba 1.4 =head1 ENCODER and DECODER
449    
450     =over 4
451    
452     =item Message::Util::encode_header_string ($yourself, $string, [%options])
453    
454     =cut
455    
456 wakaba 1.2 sub encode_header_string ($$;%) {
457 wakaba 1.3 my $yourself = shift; my $s = shift; my %o = @_;
458     $o{charset} ||= $yourself->{option}->{encoding_after_encode};
459 wakaba 1.1 $o{charset} = Message::MIME::Charset::name_normalize ($o{charset});
460     $o{current_charset} = Message::MIME::Charset::name_normalize ($o{current_charset});
461 wakaba 1.11 my ($t,%r) = Message::MIME::Charset::encode ($o{charset}, $s);
462 wakaba 1.1 my @o = (language => $o{language});
463 wakaba 1.12 if ($r{success}) { ## Convertion succeed
464 wakaba 1.11 $o{charset} = $r{charset} if $r{charset};
465 wakaba 1.13 $o{charset} = '' if $o{charset} =~ /\*/;
466     (value => $t, @o, Message::MIME::Charset::name_minimumize ($o{charset}, $t));
467 wakaba 1.1 } else { ## Fault
468 wakaba 1.13 $o{current_charset} = '' if $o{current_charset} =~ /\*/;
469 wakaba 1.18 (value => $t, failed => 1, @o,
470 wakaba 1.13 Message::MIME::Charset::name_minimumize ($o{current_charset}, $t));
471 wakaba 1.1 }
472     }
473    
474 wakaba 1.2 sub decode_header_string ($$;%) {
475 wakaba 1.3 my $yourself = shift; my $s = shift; my %o = @_;
476     $o{charset} ||= $yourself->{option}->{encoding_before_decode};
477 wakaba 1.1 $o{charset} = Message::MIME::Charset::name_normalize ($o{charset});
478 wakaba 1.19 my ($t, %r); ## decoded-text, success?
479 wakaba 1.7 if ($o{type} !~ /quoted|encoded|domain|word/) {
480 wakaba 1.5 my (@s, @r);
481 wakaba 1.13 $s =~ s{(([\x09\x20]*(?:\x5C[\x00-\xFF]
482     |[\x00-\x08\x0A-\x1F\x21-\x5B\x5D-\xFF])+|[\x09\x20]+$))}
483     { push @s, $1; '' }goesx;
484 wakaba 1.5 for my $i (0..$#s) {
485     if ($s[$i] =~ /^($REG{FWS})$REG{M_encoded_word}$/) {
486     my ($t, $w) = ('', $1);
487     ($t, $r[$i]) = (Message::MIME::EncodedWord::_decode_eword ($2, $3, $4, $5));
488     if ($r[$i]) {
489     $s[$i] = $t;
490     if ($i == 0 || $r[$i-1] == 0) {
491     $s[$i] = $w.$s[$i];
492     }
493     }
494     } else {
495 wakaba 1.19 my ($u, %q) = ($s[$i]);
496 wakaba 1.13 $u =~ s/\x5C([\x00-\xFF])/$1/g unless $o{type} =~ /text/;
497 wakaba 1.19 ($u,%q) = Message::MIME::Charset::decode ($o{charset}, $u);
498     $s[$i] = $u if $q{success};
499 wakaba 1.5 }
500     }
501 wakaba 1.19 $t = join '', @s; $r{success} = 1;
502 wakaba 1.5 } else {
503 wakaba 1.19 ($t,%r) = Message::MIME::Charset::decode ($o{charset}, $s);
504 wakaba 1.5 }
505 wakaba 1.19 $r{success} ? (value => $t, success => 1, language => $o{language}): ## suceess
506     (value => $s, language => $o{language}, success => 0,
507 wakaba 1.1 charset => ($o{charset}=~/\*/?'':$o{charset})); ## fault
508     }
509    
510     sub encode_body_string {
511 wakaba 1.3 my $yourself = shift; my $s = shift; my %o = @_;
512     $o{charset} ||= $yourself->{option}->{encoding_after_encode};
513 wakaba 1.1 $o{charset} = Message::MIME::Charset::name_normalize ($o{charset});
514     $o{current_charset} = Message::MIME::Charset::name_normalize ($o{current_charset});
515 wakaba 1.11 my ($t,%r) = Message::MIME::Charset::encode ($o{charset}, $s);
516 wakaba 1.1 my @o = ();
517 wakaba 1.11 if ($r{success}) { ## Convertion successed
518     $o{charset} = $r{charset} if $r{charset};
519 wakaba 1.13 $o{charset} = '' if $o{charset} =~ /\*/;
520     (value => $t, @o, Message::MIME::Charset::name_minimumize ($o{charset}, $t));
521     } else { ## Fault
522     $o{current_charset} = '' if $o{current_charset} =~ /\*/;
523 wakaba 1.18 (value => $t, failed => 1, @o,
524 wakaba 1.13 Message::MIME::Charset::name_minimumize ($o{current_charset}, $t));
525 wakaba 1.1 }
526     }
527    
528     sub decode_body_string {
529 wakaba 1.3 my $yourself = shift; my $s = shift; my %o = @_;
530     $o{charset} ||= $yourself->{option}->{encoding_before_decode};
531 wakaba 1.1 $o{charset} = Message::MIME::Charset::name_normalize ($o{charset});
532 wakaba 1.19 my ($t,%r) = Message::MIME::Charset::decode ($o{charset}, $s);
533     $r{success} ? (value => $t, success => 1): ## suceess
534     (value => $s, success => 0,
535 wakaba 1.1 charset => ($o{charset}=~/\*/?'':$o{charset})); ## fault
536     }
537    
538 wakaba 1.5 =item Message::Util::decode_quoted_string ($yourself, $quoted-string)
539    
540     Returns unquoted and decoded a given C<quoted-string>
541     or a string containing one or multiple C<quoted-string>s.
542    
543     =cut
544    
545 wakaba 1.7 sub decode_quoted_string ($$;%) {
546 wakaba 1.5 my $yourself = shift;
547     my $quoted_string = shift;
548 wakaba 1.7 my %option = @_;
549     $option{type} ||= 'phrase';
550 wakaba 1.5 $quoted_string =~ s{$REG{M_quoted_string}|([^\x22]+)}{
551     my ($qtext, $t) = ($1, $2);
552     if (length $t) {
553 wakaba 1.8 $t =~ s/$REG{WSP}+/\x20/g;
554 wakaba 1.5 my %s = &{$yourself->{option}->{hook_decode_string}}
555 wakaba 1.7 ($yourself, $t, type => $option{type},
556     charset => $option{charset});
557 wakaba 1.5 $s{value};
558     } else {
559     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
560     my %s = &{$yourself->{option}->{hook_decode_string}}
561 wakaba 1.7 ($yourself, $qtext, type => $option{type}.'/quoted',
562     charset => $option{charset});
563 wakaba 1.5 $s{value};
564     }
565     }goex;
566     $quoted_string;
567     }
568    
569 wakaba 1.4 =item Message::Util::encode_qcontent ($yourself, $string)
570    
571     Encodes (by C<hook_encode_string> of C<$yourself-E<gt>{option}>)
572     C<qcontent> (content of C<quoted-string>) within C<$string>.
573    
574     =cut
575    
576     sub encode_qcontent ($$) {
577     my $yourself = shift;
578     my $quoted_strings = shift;
579     $quoted_strings =~ s{$REG{M_quoted_string}}{
580     my ($qtext) = ($1);
581     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
582     my %s = &{$yourself->{option}->{hook_encode_string}} ($yourself, $qtext,
583     type => 'phrase/quoted');
584 wakaba 1.14 $s{value} =~ s/([\x0D\x22\x5C])([\x20-\xFF])?/"\x5C$1".($2?"\x5C$2":'')/ges;
585 wakaba 1.4 '"'.$s{value}.'"';
586     }goex;
587     $quoted_strings;
588     }
589    
590     =item Message::Util::decode_qcontent ($yourself, $string)
591    
592     Decodes (by C<hook_decode_string> of C<$yourself-E<gt>{option}>)
593     C<qcontent> (content of C<quoted-string>) within C<$string>.
594    
595     =cut
596    
597     sub decode_qcontent ($$) {
598     my $yourself = shift;
599     my $quoted_string = shift;
600     $quoted_string =~ s{$REG{M_quoted_string}}{
601     my ($qtext) = ($1);
602     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
603     my %s = &{$yourself->{option}->{hook_decode_string}} ($yourself, $qtext,
604     type => 'phrase/quoted');
605     $s{value} =~ s/([\x22\x5C])([\x20-\xFF])?/"\x5C$1".($2?"\x5C$2":'')/ge;
606     '"'.$s{value}.'"';
607     }goex;
608     $quoted_string;
609     }
610    
611     =item @comments = Message::Util::comment_to_array ($youtself, $comments)
612    
613     Replaces C<comment>s to C< > (a SP), decodes C<ccontent>s,
614     and returns them as array.
615    
616     =cut
617    
618     sub comment_to_array ($$) {
619     my $yourself = shift;
620     my $body = shift;
621     my @r = ();
622     $body =~ s{($REG{quoted_string}|$REG{domain_literal})|$REG{M_comment}}{
623     my ($o, $c) = ($1, $2);
624     if ($o) {$o}
625     else {
626     push @r, decode_ccontent ($yourself, $c);
627     ' ';
628     }
629     }gex;
630     @r;
631     }
632    
633 wakaba 1.8 sub delete_comment_to_array ($$;%) {
634 wakaba 1.7 my $yourself = shift;
635     my $body = shift;
636 wakaba 1.8 my %option = @_;
637     my $areg = ''; $areg = '|'.$REG{angle_quoted} if $option{-use_angle_quoted};
638 wakaba 1.7 my @r = ();
639 wakaba 1.8 $body =~ s{($REG{quoted_string}|$REG{domain_literal}$areg)|$REG{M_comment}}{
640 wakaba 1.7 my ($o, $c) = ($1, $2);
641     if ($o) {$o}
642     else {
643     push @r, decode_ccontent ($yourself, $c);
644     ' ';
645     }
646     }gex;
647     ($body, @r);
648     }
649    
650 wakaba 1.4 =item Message::Util::encode_ccontent ($yourself, $ccontent)
651    
652     Encodes C<ccontent> (content of C<comment>).
653    
654     =cut
655    
656     sub encode_ccontent ($$) {
657     my $yourself = shift;
658     my $ccontent = shift;
659     my %f = &{$yourself->{option}->{hook_encode_string}} ($yourself,
660     $ccontent, type => 'ccontent');
661 wakaba 1.9 $f{value} =~ s/([\x28\x29\x5C]|\x3D\x3F)([\x21-\x7E])?/"\x5C$1".(defined $2?"\x5C$2":'')/ge;
662 wakaba 1.4 $f{value};
663     }
664    
665     =item Message::Util::decode_ccontent ($yourself, $ccontent)
666    
667     Decodes C<ccontent> (content of C<comment>).
668    
669     =cut
670    
671     sub decode_ccontent ($$) {
672 wakaba 1.12 Message::MIME::EncodedWord::decode_ccontent (@_);
673 wakaba 1.4 }
674    
675 wakaba 1.13 sub sprintxf ($;\%) {
676     my $format = shift;
677     my $gparam = shift;
678     $format =~ s{%([A-Za-z0-9_]+)(?:\(([^\x29]*)\))?;}{
679     my ($f, $a) = ($1, $2);
680     my $function = $gparam->{fmt2str}->{$f} || $FMT2STR{$f};
681     if (ref $function) {
682     my %a;
683     for (split /[\x09\x20]*,[\x09\x20]*/, $a) {
684     if (/^([^=]*[^\x09\x20=])[\x09\x20]*=>[\x09\x20]*([^\x09\x20].*)$/) {
685     $a{ unquote_if_quoted_string ($1) } = unquote_if_quoted_string ($2);
686     } else {
687     $a{ unquote_if_quoted_string ($_) } = 1;
688     }
689     }
690     my $r = &$function (\%a, $gparam);
691     length $r? $a{prefix}.$r.$a{suffix}: '';
692     } elsif (length $function) {
693     $function;
694     } else {
695     "[$f: undef]";
696     }
697     }gex;
698     $format;
699     }
700 wakaba 1.10
701 wakaba 1.14 sub decide_newline ($) {
702     my $s = shift;
703     my $nl = "\x0D\x0A";
704     my $crlf = $s =~ s/\x0D\x0A/\x0D\x0A/gs;
705     my $lfcr = $s =~ s/\x0A\x0D/\x0A\x0D/gs;
706     my $cr = $s =~ s/\x0D(?!\x0A)/\x0D/gs;
707     my $lf = $s =~ s/(?<!\x0D)\x0A/\x0A/gs;
708     if ($crlf >= $cr && $crlf >= $lf && $crlf >= $lfcr ) { $nl = "\x0D\x0A" }
709     elsif ($lfcr >= $cr && $lfcr >= $lf) { $nl = "\x0A\x0D" }
710     elsif ($cr >= $lf) { $nl = "\x0D" }
711     else { $nl = "\x0A" }
712     $nl;
713     }
714    
715 wakaba 1.17 =item $fqdn = Message::Util::get_host_fqdn
716    
717     Returns FQDN of THIS host. If it is unable to get the FQDN,
718     returns undef.
719    
720     =over 3
721    
722     =item $Message::Util::OPTION{use_Net_Domain} = 1/0
723    
724     Whether using Net::Domain module to get FQDN or not.
725    
726     =item $Message::Util::OPTION{use_Sys_Hostname} = 1/0
727    
728     Whether using Sys::Hostnamen module to get FQDN or not.
729    
730     =item $Message::Util::OPTION{use_Sys_Hostname_Long} = 1/0
731    
732     Whether using Sys::Hostnamen::Long module to get FQDN or not.
733    
734     =back
735    
736     Note that the value returned by Sys::Hostname::hostname
737     usually does not match with the FQDN. This module is prepared
738     as the last way to get. If you want not to get non-FQDN,
739     set 0 to use_Sys_Hostname. Sys::Hostname is bundled with
740     Perl. This is why its default is 1.
741    
742     =cut
743    
744     $OPTION{use_Net_Domain} = 1;
745     $OPTION{use_Sys_Hostname} = 1;
746     $OPTION{use_Sys_Hostname_Long} = 1;
747     $OPTION{use_cache_host_fqdn} = 1;
748     $OPTION{__cache_host_fqdn} = undef;
749    
750     sub get_host_fqdn () {
751     my $f = undef;
752     return $OPTION{__cache_host_fqdn}
753     if $OPTION{use_cache_host_fqdn} && $OPTION{__cache_host_fqdn};
754     if ($OPTION{use_Net_Domain}) {
755     eval q{require Net::Domain;
756     $f = &Net::Domain::hostfqdn;
757     } or Carp::carp "get_host_fqdn: get by Net::Domain: $@";
758     if ($f) {
759     $OPTION{__cache_host_fqdn} = $f;
760     return $f;
761     }
762     }
763     if ($OPTION{use_Sys_Hostname_Long}) {
764     eval q{require Sys::Hostname::Long;
765     $f = &Sys::Hostname::Long::hostname_long;
766     } or Carp::carp "get_host_fqdn: get by Sys::Hostname::Long: $@";
767     if ($f) {
768     $OPTION{__cache_host_fqdn} = $f;
769     return $f;
770     }
771     }
772     if ($OPTION{use_Sys_Hostname}) {
773     eval q{require Sys::Hostname;
774     $f = &Sys::Hostname::hostname;
775     } or Carp::carp "get_host_fqdn: get by Sys::Hostname: $@";
776     if ($f) {
777     $OPTION{__cache_host_fqdn} = $f;
778     return $f;
779     }
780     }
781     undef;
782     }
783    
784 wakaba 1.21 sub is_utf8 ($) {
785     my $s = shift;
786     return Encode::is_utf8 ($s) if $Encode::VERSION;
787     0;
788     }
789    
790 wakaba 1.1 =head1 LICENSE
791    
792     Copyright 2002 wakaba E<lt>[email protected]<gt>.
793    
794     This program is free software; you can redistribute it and/or modify
795     it under the terms of the GNU General Public License as published by
796     the Free Software Foundation; either version 2 of the License, or
797     (at your option) any later version.
798    
799     This program is distributed in the hope that it will be useful,
800     but WITHOUT ANY WARRANTY; without even the implied warranty of
801     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
802     GNU General Public License for more details.
803    
804     You should have received a copy of the GNU General Public License
805     along with this program; see the file COPYING. If not, write to
806     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
807     Boston, MA 02111-1307, USA.
808    
809     =head1 CHANGE
810    
811     See F<ChangeLog>.
812 wakaba 1.21 $Date: 2002/07/26 12:42:00 $
813 wakaba 1.1
814     =cut
815    
816     1;

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24