/[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.19 - (hide annotations) (download)
Mon Jul 22 02:49:45 2002 UTC (24 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.18: +13 -13 lines
2002-07-22  Wakaba <w@suika.fam.cx>

	* Util.pm: Some fix for new returned value style
	of Message::MIME::Charset.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24