=head1 NAME Message::Field::Subject -- Perl module for Internet message header C field body =cut package Message::Field::Subject; use strict; use vars qw(%DEFAULT @ISA %REG $VERSION); $VERSION=do{my @r=(q$Revision: 1.13 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r}; require Message::Field::Structured; push @ISA, q(Message::Field::Structured); %REG = %Message::Util::REG; $REG{news_control} = qr/^cmsg$REG{WSP}+/; $REG{prefix_fwd} = qr/(?i)(?:Fwd?|Forward)/; $REG{prefix_list} = qr/[(\[][A-Za-z0-9._-]+[\x20:-]\d+[)\]]/; $REG{M_prefix_list} = qr/[(\[]([A-Za-z0-9._-]+)[\x20:-](\d+)[)\]]/; $REG{M_was_subject} = qr/\([Ww][Aa][Ss][:\x09\x20]$REG{FWS}(.+?)$REG{FWS}\)$REG{FWS}$/; $REG{message_from_subject} = qr/^$REG{FWS}(?i)Message from \S+$REG{FWS}$/; if ($^V gt v5.7.2) { $REG{prefix_re} = q/(?i)Re|Sv|Odp |\x{8FD4} ## Hen /; $REG{prefix_advertisement} = qr/ (?i)ADV?: |[!\x{FF01}] $REG{FWS} \x{5E83}[\x{543F}\x{544A}] $REG{FWS} [!\x{FF01}] ## ! kou koku ! |[!\x{FF01}] $REG{FWS} [\x{9023}\x{F99A}]\x{7D61}\x{65B9}\x{6CD5}\x{7121}\x{3057}? $REG{FWS} [!\x{FF01}] ## ! ren raku hou hou nashi ! |[\x{672A}\x{672B}] [\x09\x0A\x0D\x20\x{3000}]* \x{627F} [\x09\x0A\x0D\x20\x{3000}]* \x{8AFE} [\x09\x0A\x0D\x20\x{3000}]* \x{5E83} [\x09\x0A\x0D\x20\x{3000}]* [\x{543F}\x{544A}] [\x09\x0A\x0D\x20\x{3000}]* [\x{203B}\x{0FBF}] ## mi shou daku kou koku * /x; } else { $REG{prefix_re} = qr/(?i)Re|Sv|Odp/; $REG{prefix_advertisement} = qr/(?i)ADV?:/; } $REG{prefix_general} = qr/((?:$REG{prefix_re}|$REG{prefix_fwd})\^?[\[\(]?\d*[\]\)]?[:>]$REG{FWS})+/x; $REG{prefix_general_list} = qr/($REG{prefix_general}|$REG{FWS}$REG{prefix_list}$REG{FWS})+/x; %DEFAULT = ( -_MEMBERS => [qw/is list_count list_name news_control was_subject/], -_METHODS => [qw/as_plain_string is list_count list_name news_control was_subject value value_type/], #encoding_after_encode #encoding_before_decode -format_news_control => 'cmsg %s', -format_prefix_fwd => 'Fwd: %s', -format_prefix_list => '[%s:%05d] %s', -format_prefix_re => 'Re: %s', -format_was_subject => '%s (was: %s)', #field_param_name #field_name #field_ns #format #header_default_charset #header_default_charset_input #hook_encode_string #hook_decode_string -output_general_prefix => 1, -output_list_prefix => 0, -output_news_control => 1, -output_was_subject => 1, ## ["-"] 1*DIGIT #parse_all -parse_was_subject => 1, -use_general_prefix => 1, -use_list_prefix => 1, -use_message_from_subject => 0, -use_news_control => 1, -use_was_subject => 1, #value_type ); =head1 CONSTRUCTORS The following methods construct new objects: =over 4 =cut ## Initialize of this class -- called by constructors sub _init ($;%) { my $self = shift; my %options = @_; $self->SUPER::_init (%DEFAULT, %options); my $fname = $self->{option}->{field_name}; if ($fname =~ /^x-.subject$/) { $self->{option}->{use_list_prefix} = 0 unless defined $options{-use_list_prefix}; $self->{option}->{use_news_control} = 0 unless defined $options{-use_news_control}; $self->{option}->{use_message_from_subject} = 0 unless defined $options{-use_message_from_subject}; } #$self->{option}->{value_type}->{news_control} = ['Message::Field::UsenetControl',{}, [qw//]]; $self->{option}->{value_type}->{was_subject} = ['Message::Field::Subject',{}, [qw/format_news_control format_prefix_fwd format_prefix_re format_was_subject output_general_prefix output_list_prefix output_news_control output_was_subject parse_was_subject use_general_prefix use_list_prefix use_news_control use_was_subject/]]; } =item $subject = Message::Field::Subject->new ([%options]) Constructs a new C object. You might pass some options as parameters to the constructor. =cut ## Inherited =item $subject = Message::Field::Subject->parse ($field-body, [%options]) Constructs a new C object with given field body. You might pass some options as parameters to the constructor. =cut sub parse ($$;%) { my $class = shift; my $self = bless {}, $class; my $body = shift; $self->_init (@_); my $option = $self->{option}; if ($option->{use_news_control} && $body =~ s/$REG{news_control}//) { $self->{news_control} = $body; return $self; } my $value = ''; my %s = &{$self->{option}->{hook_decode_string}} ($self, $body, type => 'text', charset => $option->{encoding_before_decode}, ); if ($s{charset}) { ## Convertion failed $self->{_charset} = $s{charset}; $self->{value} = $s{value}; return $self; } elsif (!$s{success}) { $self->{_charset} = $self->{option}->{header_default_charset_input}; $self->{value} = $s{value}; return $self; } $value = $s{value}; #if (!$option->{parse_all}) { # $self->{value} = $value; # return $self; #} if ($option->{use_general_prefix}) { if ($option->{use_list_prefix} && $value =~ s/^($REG{prefix_general_list})//x) { my $prefix = $1; $self->{is}->{reply} = 1 if $prefix =~ /$REG{prefix_re}/x; $self->{is}->{foward} = 1 if $prefix =~ /$REG{prefix_fwd}/x; ($self->{list_name}, $self->{list_count}) = ($1, $2) if $prefix =~ /$REG{M_prefix_list}/x; } elsif ($value =~ s/^($REG{prefix_general})//x) { my $prefix = $1; $self->{is}->{reply} = 1 if $prefix =~ /$REG{prefix_re}/x; $self->{is}->{foward} = 1 if $prefix =~ /$REG{prefix_fwd}/x; } } elsif ($option->{use_list_prefix} && $value =~ s/^$REG{FWS}$REG{M_prefix_list}(?:$REG{FWS}$REG{prefix_list})*$REG{FWS}//x) { ($self->{list_name}, $self->{list_count}) = ($1, $2); } if ($option->{use_was_subject} && $value =~ s/$REG{M_was_subject}//) { my $was = $1; if ($option->{parse_was_subject}) { my %option; for (keys %$option) { $option{ '-'.$_ } = Message::Util::make_clone ($option->{ $_ }); } $self->{was_subject} = ref ($self)->parse ($was, -hook_decode_string => sub { shift; (value => shift, @_) }, %option); } else { $self->{was_subject} = $was; } } if ($option->{use_message_from_subject} && $value =~ s/$REG{message_from_subject}//) { $self->{is}->{message_from_subject} = 1; } $self->{value} = $value; $self; } =back =head1 METHODS =over 4 =cut sub value ($;$) { my $self = shift; my $v = shift; if (defined $v) { $self->{value} = $v; } $self->{value}; } sub list_name ($) { $_[0]->{list_name} } sub list_count ($) { $_[0]->{list_count} } =item $body = $subject->stringify Retruns subject field body as string. String is encoded for message if necessary. =cut sub stringify ($;%) { my $self = shift; my %o = @_; my %option = %{$self->{option}}; for (grep {/^-/} keys %o) {$option{substr ($_, 1)} = $o{$_}} if ($option{use_news_control} && $option{output_news_control} && $self->{news_control}) { my $c = $self->{news_control}; return '' unless length $c; return sprintf $option{format_news_control}, $c; } if ($self->{_charset}) { return $self->{value}; } else { my $value = $self->{value}; if ($option{use_general_prefix} && $option{output_general_prefix}) { $value = sprintf $option{format_prefix_re}, $value if $self->{is}->{reply}; $value = sprintf $option{format_prefix_fwd}, $value if $self->{is}->{foward}; } if ($option{use_list_prefix} && $option{output_list_prefix}) { $value = sprintf $option{format_prefix_list}, $self->{list_name}, $self->{list_count}, $value if length $self->{list_name} && defined $self->{list_count}; } if ($option{use_was_subject} && $option{output_was_subject} > 0) { my $was; if (ref $self->{was_subject}) { my %opt = @_; $opt{-output_was_subject} = $opt{output_was_subject} unless defined $opt{-output_was_subject}; $opt{-output_was_subject}--; $was = $self->{was_subject}->as_plain_string (%opt); } elsif (length $self->{was_subject}) { $was = $self->{was_subject}; } $value = sprintf $option{format_was_subject}, $value, $was if defined $was; } my (%e) = &{$option{hook_encode_string}} ($self, $value, charset => $option{encoding_after_encode}, current_charset => $option{internal_charset}, type => 'text', ); return $e{value}; } } *as_string = \&stringify; =item $body = $subject->as_plain_string Returns subject field body as string. Unlike C, retrun string of this method is not encoded (i.e. returned in internal code). =cut sub as_plain_string ($;%) { my $self = shift; $self->stringify ( -hook_encode_string => sub { shift; (value => shift, @_) }, @_, ); } =item $bool = $subject->is ($attribute [=> $bool]) Set/gets attribute value. Example: $isreply = $subject->is ('re'); ## Strictly, this checks whether start with "Re: " or not. $subject->is (foward => 1, re => 0); =cut sub is ($@) { my $self = shift; if (@_ == 1) { my $query = shift; if ($query eq 'advertisement') { return $self->{value} =~ /$REG{prefix_advertisement}/x? 1:0; } else { return $self->{is}->{ $_[0] }; } } while (my ($name, $value) = splice (@_, 0, 2)) { $self->{is}->{ $name } = $value; } } =item $old_subject = $subject->was_subject Returns I subject. =cut sub was_subject ($) { my $self = shift; $self->{was_subject} = $self->_parse_all (was => $self->{was_subject}) if $self->{option}->{parse_all}; $self->{was_subject}; } sub news_control ($) { my $self = shift; $self->{news_control} = $self->_parse_all (was => $self->{news_control}) if $self->{option}->{parse_all}; $self->{news_control}; } =item $clone = $subject->clone () Returns a copy of the object. =cut ## Inherited =head1 LICENSE Copyright 2002 wakaba Ew@suika.fam.cxE. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. =head1 CHANGE See F. $Date: 2002/12/28 08:45:50 $ =cut 1;