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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.9 - (hide annotations) (download)
Thu Aug 1 06:42:38 2002 UTC (22 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.8: +197 -156 lines
2002-08-01  Wakaba <w@suika.fam.cx>

	* Unstructured.pm: Rewritten.
	* Subject.pm: Likewise.  Support Japanese government's
	spam mail prefix if Perl has defined $^V (=~ has UTF-8 support).

1 wakaba 1.1
2     =head1 NAME
3    
4 wakaba 1.5 Message::Field::Subject -- Perl module for Internet
5     message header C<Subject:> field body
6 wakaba 1.1
7     =cut
8    
9     package Message::Field::Subject;
10     use strict;
11 wakaba 1.9 use vars qw(%DEFAULT @ISA %REG $VERSION);
12     $VERSION=do{my @r=(q$Revision: 1.8 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
13     require Message::Field::Structured;
14     push @ISA, q(Message::Field::Structured);
15    
16     %REG = %Message::Util::REG;
17     $REG{news_control} = qr/^cmsg$REG{WSP}+/;
18     $REG{prefix_fwd} = qr/(?i)Fwd?/;
19     $REG{prefix_list} = qr/[(\[][A-Za-z0-9._-]+[\x20:-]\d+[)\]]/;
20     $REG{M_prefix_list} = qr/[(\[]([A-Za-z0-9._-]+)[\x20:-](\d+)[)\]]/;
21     $REG{M_was_subject} = qr/\([Ww][Aa][Ss][:\x09\x20]$REG{FWS}(.+?)$REG{FWS}\)$REG{FWS}$/;
22     $REG{message_from_subject} = qr/^$REG{FWS}(?i)Message from \S+$REG{FWS}$/;
23     if (defined $^V) {
24     $REG{prefix_re} = qr/(?i)Re|Sv|Odp
25     |\x{8FD4} ## Hen
26     /x;
27     $REG{prefix_advertisement} = qr/
28     (?i)ADV?:
29     |[!\x{FF01}] $REG{FWS} \x{5E83}[\x{543F}\x{544A}] $REG{FWS} [!\x{FF01}]
30     ## ! kou koku !
31     |[!\x{FF01}] $REG{FWS} [\x{9023}\x{F99A}]\x{7D61}\x{65B9}\x{6CD5}\x{7121}\x{3057}? $REG{FWS} [!\x{FF01}]
32     ## ! ren raku hou hou nashi !
33     |\x{672A}\x{627F}\x{8AFE}\x{5E83}[\x{543F}\x{544A}][\x{203B}\x{0FBF}]
34     ## mi shou daku kou koku *
35     /x;
36     } else {
37     $REG{prefix_re} = qr/(?i)Re|Sv/;
38     $REG{prefix_advertisement} = qr/(?i)ADV?:/;
39     }
40     $REG{prefix_general} = qr/((?:$REG{prefix_re}|$REG{prefix_fwd})\^?[\[\(]?\d*[\]\)]?[:>]$REG{FWS})+/x;
41     $REG{prefix_general_list} = qr/($REG{prefix_general}|$REG{FWS}$REG{prefix_list}$REG{FWS})+/x;
42    
43     ## Initialize of this class -- called by constructors
44     %DEFAULT = (
45     -_MEMBERS => [qw/is list_count list_name news_control was_subject/],
46     -_METHODS => [qw/as_plain_string is list_count list_name news_control was_subject value value_type/],
47     #encoding_after_encode
48     #encoding_before_decode
49     -format_news_control => 'cmsg %s',
50     -format_prefix_fwd => 'Fwd: %s',
51     -format_prefix_re => 'Re: %s',
52     -format_was_subject => '%s (was: %s)',
53     #field_param_name
54     #field_name
55     #field_ns
56     #format
57     #header_default_charset
58     #header_default_charset_input
59     #hook_encode_string
60     #hook_decode_string
61     -output_general_prefix => 1,
62     -output_list_prefix => 1,
63     -output_news_control => 1,
64     -output_was_subject => 1, ## ["-"] 1*DIGIT
65     #parse_all
66     -parse_was_subject => 1,
67     -use_general_prefix => 1,
68     -use_list_prefix => 0,
69     -use_message_from_subject => 0,
70     -use_news_control => 1,
71     -use_was_subject => 1,
72     #value_type
73     );
74 wakaba 1.1
75 wakaba 1.5 =head1 CONSTRUCTORS
76 wakaba 1.1
77 wakaba 1.9 The following methods construct new objects:
78 wakaba 1.1
79 wakaba 1.5 =over 4
80 wakaba 1.1
81     =cut
82    
83 wakaba 1.5 ## Initialize of this class -- called by constructors
84     sub _init ($;%) {
85     my $self = shift;
86     my %options = @_;
87     $self->SUPER::_init (%DEFAULT, %options);
88    
89 wakaba 1.9 #$self->{option}->{value_type}->{news_control} = ['Message::Field::UsenetControl',{}, [qw//]];
90     $self->{option}->{value_type}->{was_subject} = ['Message::Field::Subject',{},
91     [qw/format_news_control format_prefix_fwd format_prefix_re
92     format_was_subject output_general_prefix output_list_prefix
93     output_news_control output_was_subject parse_was_subject
94     use_general_prefix use_list_prefix use_news_control use_was_subject/]];
95 wakaba 1.1 }
96    
97 wakaba 1.5 =item $subject = Message::Field::Subject->new ([%options])
98 wakaba 1.1
99 wakaba 1.5 Constructs a new C<Message::Field::Subject> object. You might pass some
100     options as parameters to the constructor.
101    
102     =cut
103    
104     ## Inherited
105    
106     =item $subject = Message::Field::Subject->parse ($field-body, [%options])
107    
108     Constructs a new C<Message::Field::Subject> object with
109     given field body. You might pass some options as parameters to the constructor.
110 wakaba 1.1
111     =cut
112    
113     sub parse ($$;%) {
114     my $class = shift;
115 wakaba 1.5 my $self = bless {}, $class;
116 wakaba 1.9 my $body = shift;
117 wakaba 1.5 $self->_init (@_);
118 wakaba 1.9 my $option = $self->{option};
119     if ($option->{use_news_control} && $body =~ s/$REG{news_control}//) {
120     $self->{news_control} = $body;
121 wakaba 1.1 return $self;
122     }
123 wakaba 1.9 my $value = '';
124     my %s = &{$self->{option}->{hook_decode_string}} ($self,
125     $body,
126     type => 'text',
127     charset => $option->{encoding_before_decode},
128     );
129     if ($s{charset}) { ## Convertion failed
130     $self->{_charset} = $s{charset};
131     $self->{value} = $s{value};
132     return $self;
133     } elsif (!$s{success}) {
134     $self->{_charset} = $self->{option}->{header_default_charset_input};
135     $self->{value} = $s{value};
136     return $self;
137 wakaba 1.1 }
138 wakaba 1.9 $value = $s{value};
139     #if (!$option->{parse_all}) {
140     # $self->{value} = $value;
141     # return $self;
142     #}
143     if ($option->{use_general_prefix}) {
144     if ($option->{use_list_prefix} && $value =~ s/^($REG{prefix_general_list})//x) {
145     my $prefix = $1;
146     $self->{is}->{reply} = 1 if $prefix =~ /$REG{prefix_re}/x;
147     $self->{is}->{foward} = 1 if $prefix =~ /$REG{prefix_fwd}/x;
148     ($self->{list_name}, $self->{list_count}) = ($1, $2)
149     if $prefix =~ /$REG{M_prefix_list}/x;
150     } elsif ($value =~ s/^($REG{prefix_general})//x) {
151     my $prefix = $1;
152     $self->{is}->{reply} = 1 if $prefix =~ /$REG{prefix_re}/x;
153     $self->{is}->{foward} = 1 if $prefix =~ /$REG{prefix_fwd}/x;
154     }
155     } elsif ($option->{use_list_prefix} && $value =~ s/^$REG{FWS}$REG{M_prefix_list}(?:$REG{FWS}$REG{prefix_list})*$REG{FWS}//x) {
156     ($self->{list_name}, $self->{list_count}) = ($1, $2);
157     }
158     if ($option->{use_was_subject} && $value =~ s/$REG{M_was_subject}//) {
159 wakaba 1.5 my $was = $1;
160 wakaba 1.9 if ($option->{parse_was_subject}) {
161     my %option;
162     for (keys %$option) {
163     $option{ '-'.$_ } = Message::Util::make_clone ($option->{ $_ });
164     }
165     $self->{was_subject} = ref ($self)->parse ($was,
166     -hook_decode_string => sub { shift; (value => shift, @_) },
167     %option);
168 wakaba 1.5 } else {
169 wakaba 1.9 $self->{was_subject} = $was;
170 wakaba 1.5 }
171 wakaba 1.9 }
172     if ($option->{use_message_from_subject} && $value =~ s/$REG{message_from_subject}//) {
173     $self->{is}->{message_from_subject} = 1;
174     }
175     $self->{value} = $value;
176 wakaba 1.1 $self;
177     }
178    
179 wakaba 1.5 =back
180    
181     =head1 METHODS
182    
183     =over 4
184    
185 wakaba 1.9 =cut
186    
187     sub value ($;$) {
188     my $self = shift;
189     my $v = shift;
190     if (defined $v) {
191     $self->{value} = $v;
192     }
193     $self->{value};
194     }
195    
196     sub list_name ($) { $_[0]->{list_name} }
197     sub list_count ($) { $_[0]->{list_count} }
198    
199 wakaba 1.5 =item $body = $subject->stringify
200    
201     Retruns subject field body as string. String is encoded
202     for message if necessary.
203    
204     =cut
205    
206 wakaba 1.1 sub stringify ($;%) {
207 wakaba 1.9 my $self = shift;
208     my %o = @_; my %option = %{$self->{option}};
209 wakaba 1.5 for (grep {/^-/} keys %o) {$option{substr ($_, 1)} = $o{$_}}
210 wakaba 1.9 if ($option{use_news_control} && $option{output_news_control}
211     && $self->{news_control}) {
212     my $c = $self->{news_control};
213     return '' unless length $c;
214     return sprintf $option{format_news_control}, $c;
215 wakaba 1.5 }
216 wakaba 1.9 if ($self->{_charset}) {
217     return $self->{value};
218     } else {
219     my $value = $self->{value};
220     if ($option{use_general_prefix} && $option{output_general_prefix}) {
221     $value = sprintf $option{format_prefix_re}, $value if $self->{is}->{reply};
222     $value = sprintf $option{format_prefix_fwd}, $value if $self->{is}->{foward};
223     }
224     if ($option{use_was_subject} && $option{output_was_subject} > 0) {
225     my $was;
226     if (ref $self->{was_subject}) {
227     my %opt = @_;
228     $opt{-output_was_subject} = $opt{output_was_subject}
229     unless defined $opt{-output_was_subject};
230     $opt{-output_was_subject}--;
231     $was = $self->{was_subject}->as_plain_string (%opt);
232     } elsif (length $self->{was_subject}) {
233     $was = $self->{was_subject};
234     }
235     $value = sprintf $option{format_was_subject}, $value, $was if defined $was;
236     }
237     my (%e) = &{$option{hook_encode_string}} ($self,
238     $value,
239     charset => $option{encoding_after_encode},
240     current_charset => $option{internal_charset},
241     type => 'text',
242     );
243     return $e{value};
244 wakaba 1.5 }
245 wakaba 1.1 }
246 wakaba 1.5 *as_string = \&stringify;
247    
248     =item $body = $subject->as_plain_string
249    
250     Returns subject field body as string. Unlike C<stringify>,
251     retrun string of this method is not encoded (i.e. returned
252     in internal code).
253    
254     =cut
255 wakaba 1.1
256 wakaba 1.3 sub as_plain_string ($;%) {
257 wakaba 1.1 my $self = shift;
258 wakaba 1.9 $self->stringify (
259     -hook_encode_string => sub { shift; (value => shift, @_) },
260     @_,
261     );
262 wakaba 1.8 }
263 wakaba 1.5
264    
265 wakaba 1.1
266 wakaba 1.5 =item $bool = $subject->is ($attribute [=> $bool])
267    
268     Set/gets attribute value.
269    
270     Example:
271    
272     $isreply = $subject->is ('re');
273     ## Strictly, this checks whether start with "Re: " or not.
274    
275     $subject->is (foward => 1, re => 0);
276    
277     =cut
278    
279     sub is ($@) {
280 wakaba 1.1 my $self = shift;
281 wakaba 1.5 if (@_ == 1) {
282 wakaba 1.9 my $query = shift;
283     if ($query eq 'advertisement') {
284     return $self->{value} =~ /$REG{prefix_advertisement}/x? 1:0;
285     } else {
286     return $self->{is}->{ $_[0] };
287     }
288 wakaba 1.5 }
289     while (my ($name, $value) = splice (@_, 0, 2)) {
290 wakaba 1.9 $self->{is}->{ $name } = $value;
291 wakaba 1.1 }
292     }
293    
294 wakaba 1.9 =item $old_subject = $subject->was_subject
295 wakaba 1.5
296     Returns I<was: > subject.
297    
298     =cut
299    
300 wakaba 1.9 sub was_subject ($) {
301 wakaba 1.1 my $self = shift;
302 wakaba 1.9 $self->{was_subject} = $self->_parse_all (was => $self->{was_subject})
303     if $self->{option}->{parse_all};
304     $self->{was_subject};
305     }
306    
307     sub news_control ($) {
308     my $self = shift;
309     $self->{news_control} = $self->_parse_all (was => $self->{news_control})
310     if $self->{option}->{parse_all};
311     $self->{news_control};
312 wakaba 1.1 }
313    
314 wakaba 1.5 =item $clone = $subject->clone ()
315    
316     Returns a copy of the object.
317    
318     =cut
319    
320 wakaba 1.9 ## Inherited
321 wakaba 1.1
322     =head1 LICENSE
323    
324     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
325    
326     This program is free software; you can redistribute it and/or modify
327     it under the terms of the GNU General Public License as published by
328     the Free Software Foundation; either version 2 of the License, or
329     (at your option) any later version.
330    
331     This program is distributed in the hope that it will be useful,
332     but WITHOUT ANY WARRANTY; without even the implied warranty of
333     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
334     GNU General Public License for more details.
335    
336     You should have received a copy of the GNU General Public License
337     along with this program; see the file COPYING. If not, write to
338     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
339     Boston, MA 02111-1307, USA.
340    
341     =head1 CHANGE
342    
343     See F<ChangeLog>.
344 wakaba 1.9 $Date: 2002/07/17 00:33:29 $
345 wakaba 1.1
346     =cut
347    
348     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24