#-*- perl -*- # # Copyright (C) 2001 Ken'ichi Fukamachi # All rights reserved. This program is free software; you can # redistribute it and/or modify it under the same terms as Perl itself. # # $FML: Header.pm,v 1.36 2001/05/19 14:15:20 fukachan Exp $ # package FML::Header; use strict; use vars qw(@ISA @EXPORT @EXPORT_OK $AUTOLOAD); use Carp; use Mail::Header; use FML::Log qw(Log LogWarn LogError); =head1 NAME FML::Header - header manipulators =head1 SYNOPSIS $header = use FML::Header \@header; $header->add('X-ML-Info', "mailing list name"); $header->delete('Return-Receipt-To'); $header->replace(field, value); =head1 DESCRIPTION C is an adapter for C class (See C for more details). C is the base class. =head1 METHODS Methods defined in C are available. For example, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C. CAUTION: Pay attention! C overloads C to remove the trailing "\n". =head2 C forward the request up to superclass C. =cut @ISA = qw(Mail::Header); # Descriptions: forward new() request to the base class # Arguments: $self $args # Side Effects: none # Return Value: none sub new { my ($self, $args) = @_; # an adapter for Mail::Header::new() $self->SUPER::new($args); } sub DESTROY {} sub AUTOLOAD { my ($self, $args) = @_; Log("Error: $AUTOLOAD is not defined"); } =head2 C return the value of C but without the trailing "\n". =head2 C alias of C. =cut sub get { my ($self, @x) = @_; my $x = $self->SUPER::get(@x) || ''; $x =~ s/\n$//; $x; } sub set { my ($self, @x) = @_; $self->SUPER::set(@x); } =head2 C clean up given C
. It parse it by C and nuke < and >. =cut sub address_clean_up { my ($self, $addr) = @_; use Mail::Address; my @addrlist = Mail::Address->parse($addr); # only the first element in the @addrlist array is effective. $addr = $addrlist[0]->address; $addr =~ s/^\s*\s*$//; # return the result. return $addr; } =head2 C return the C defind in the header's Content-Type field. For example, C, C and et. al. =head2 C return the C defind in the header's Content-Type field. =cut # Descriptions: return the type defind in the header's Content-Type field. # Arguments: $self # Side Effects: extra spaces in the type to return is removed. # Return Value: none sub data_type { my ($header) = @_; my ($type) = split(/;/, $header->get('content-type')); if (defined $type) { $type =~ s/\s*//g; return $type; } undef; } # Descriptions: return boundary defined in Content-Type # Arguments: $self $args # Side Effects: none. # Return Value: none sub mime_boundary { my ($header) = @_; my $m = $header->get('content-type'); if ($m =~ /boundary=\"(.*)\"/) { return $1; } else { undef; } } ### ### FML specific functions ### =head1 FML SPECIFIC METHODS =head2 C add X-ML-Name: =head2 C add X-Mail-Count: =head2 C add X-ML-Count: =cut sub add_fml_ml_name { my ($header, $config, $args) = @_; $header->add('X-ML-Name', $config->{ x_ml_name }); } sub add_fml_traditional_article_id { my ($header, $config, $args) = @_; $header->add('X-Mail-Count', $args->{ id }); } sub add_fml_article_id { my ($header, $config, $args) = @_; $header->add('X-ML-Count', $args->{ id }); } =head2 C add X-MLServer: and List-Software:. C object as a $args->{ message } can be handled when $args->{type} is 'MIME::Lite'. =head2 C add List-* sereies defined in RFC2369 and RFC2919. C object as a $args->{ message } can be handled when $args->{type} is 'MIME::Lite'. =head2 C add X-Sequence. =cut sub add_software_info { my ($header, $config, $args) = @_; my $fml_version = $config->{ fml_version }; my $object_type = defined $args->{ type } ? $args->{ type } : ''; if ($fml_version) { if ($object_type eq 'MIME::Lite') { my $msg = $args->{ message }; $msg->attr('X-MLServer' => $fml_version); $msg->attr('List-Software' => $fml_version); } else { $header->add('X-MLServer', $fml_version); $header->add('List-Software', $fml_version); } } } sub add_rfc2369 { my ($header, $config, $args) = @_; my $object_type = defined $args->{ type } ? $args->{ type } : ''; # addresses my $post = $config->{ address_for_post }; my $command = $config->{ address_for_command }; my $maintainer = $config->{ maintainer }; # information for list-id my $ml_name = $config->{ ml_name }; my $id = "$ml_name mailing list <$post>"; $id =~ s/\@/./g; # See RFC2369 for more details if ($object_type eq 'MIME::Lite') { my $msg = $args->{ message }; $msg->attr('List-ID' => $id) if $id; $msg->attr('List-Post' => "") if $post; $msg->attr('List-Owner' => "") if $maintainer; if ($command) { $msg->attr('List-Help' => ""); $msg->attr('List-Subscribe' => ""); $msg->attr('List-UnSubscribe' => ""); } } else { $header->add('List-ID', $id) if $id; $header->add('List-Post', "") if $post; $header->add('List-Owner', "") if $maintainer; if ($command) { $header->add('List-Help', ""); $header->add('List-Subscribe', ""); $header->add('List-UnSubscribe', ""); } } } sub add_x_sequence { my ($header, $config, $args) = @_; $header->add('X-Sequence', "$config->{ x_ml_name } $args->{ id }"); } =head2 C add subject tag like [elena:00010]. The actual function definitions exist in C. =head2 C add or replace C. =cut sub rewrite_subject_tag { my ($header, $config, $args) = @_; my $pkg = "FML::Header::Subject"; eval qq{ require $pkg; $pkg->import();}; unless ($@) { $pkg->rewrite_subject_tag($header, $config, $args); } else { Log("Error: cannot load $pkg"); } } sub rewrite_reply_to { my ($header, $config, $args) = @_; my $reply_to = $header->get('reply-to') || ''; unless ($reply_to) { $header->add('reply-to', $config->{ address_for_post }); Log("(debug) rewrite reply-to to $config->{ address_for_post }"); } else { Log("(debug) not rewrite 'reply-to: $reply_to'"); } } =head2 C remove header fields defiend in C<$unsafe_header_fields>. C<$unsafe_header_fields> is a list of keys. The keys are space separeted. unsafe_header_fields = Return-Receipt-To =cut sub delete_unsafe_header_fields { my ($header, $config, $args) = @_; my (@fields) = split(/\s+/, $config->{ unsafe_header_fields }); for (@fields) { $header->delete($_);} } =head1 MISCELLANEOUS UTILITIES =head2 C remove subject tag like the string given as C<$string>. =head2 C return message-id list (ARRAY REFERENCE) extracted from the header (C<$self>). It extracts message-id(s) from In-Reply-To: and References: fields. =cut sub delete_subject_tag_like_string { my ($header, $str) = @_; $str =~ s/\W[-\w]+.\s*\d+\W//g; $str =~ s/\s+/ /g; $str =~ s/^\s*//g; $str; } sub extract_message_id_references { my ($header) = @_; my $buf = $header->get('in-reply-to') ."\n". $header->get('references'); use Mail::Address; my @addrs = Mail::Address->parse($buf); my @r = (); my %uniq = (); foreach my $addr (@addrs) { my $a = $addr->address; unless ($uniq{ $a }) { push(@r, $addr->address); $uniq{ $a } = 1; } } \@r; } =head1 FILTERING FUNCTIONS =head2 C check whether message-id is unique or not. If the message-id is found in the past message-id cache, the injected message must causes a mail loop. =head2 C The injected message loops if x-ml-info: has our own C address. =head2 C The injected message loops if list-post: has our own C address. =cut sub verify_message_id_uniqueness { my ($header, $config, $args) = @_; my $dir = $config->{ 'message_id_cache_dir' }, my $mid = $header->get('message-id'); my $dup = 0; $mid = $header->address_clean_up($mid); if ($mid) { use FML::Header::MessageID; my $xargs = { directory => $dir }; my $obj = FML::Header::MessageID->new->open_cache($xargs); if (defined $obj) { my $fh = $obj->open; # we can tind the $mid in the past message-id cache ? $dup = $obj->find($mid); Log( "message-id duplicated" ) if $dup; # save the current id print $fh $mid, "\t", $mid, "\n"; $fh->close; } } return $dup; } sub verify_x_ml_info_uniqueness { my ($header, $config, $args) = @_; my $buf = $header->get('x-ml-info') || undef; my $addr = $config->{ addr_for_post } || undef; if ($addr && $buf) { return ($buf =~ /$addr/) ? 1 : 0; } else { 0; } } sub verify_list_post_uniqueness { my ($header, $config, $args) = @_; my $buf = $header->get('list-post') || undef; my $addr = $config->{ addr_for_post } || undef; if ($addr && $buf) { return ($buf =~ /$addr/) ? 1 : 0; } else { 0; } 0; } =head1 SEE ALSO L =head1 AUTHOR Ken'ichi Fukamachi =head1 COPYRIGHT Copyright (C) 2001 Ken'ichi Fukamachi All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 HISTORY FML::Header appeared in fml5 mailing list driver package. See C for more details. =cut 1;