# -*-Perl-*-
################################################################
###
### Recipient.pm
###
### Author: Internet Message Group
### Created: Apr 27, 1997
### Revised: Jul 4, 2004
###
my $PM_VERSION = "IM::Recipient.pm version 20031028(IM146)";
package IM::Recipient;
require 5.003;
require Exporter;
use IM::Util;
use IM::Address qw(extract_addr fetch_addr);
use IM::Alias qw(alias_lookup hosts_completion);
use IM::Config qw(expand_path);
# use FileHandle;
use integer;
use strict;
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(add_to_rcpt parse_rcpt rcpt_pickup);
use vars qw($Alias_match_count $Include_count %Include_Files);
##### ADD AN ADDRESS TO RECIPIENT LIST #####
#
# add_to_rcpt(bcc_flag, addr)
# bcc_flag: register with "bcc" tag
# addr: an address to be registered
# return value: none
# 0: success
# -1: failure
#
sub add_to_rcpt($$) {
my($bcc_flag, $addr) = @_;
my($rec, $a);
$addr = &extract_addr($addr);
return -1 if ($addr eq '');
return 0 unless ($addr);
if ($addr =~ /^\@MYSELF\@/i || $addr =~ /^\@ME\@/i) {
$addr = $main::Sender;
}
if ($addr !~ /[\@%!:]/o) {
if (($a = alias_lookup($addr)) ne '') {
if ($Alias_match_count++ == 100) {
&im_warn("May be alias loop: $addr\n");
return -1;
}
if (&parse_rcpt($bcc_flag, $a, 1) < 0) {
return -1;
} else {
return 0;
}
} elsif (!$main::Obey_MTA_domain && $main::Default_to_domain_name) {
$addr .= "\@$main::Default_to_domain_name";
}
} elsif ($a = hosts_completion($addr, $main::Cmpl_with_gethostbyname)) {
$addr = $a;
}
# duplicate surpression
foreach $rec (@main::Recipients) {
if ($bcc_flag) {
return 0 if ($rec eq $addr);
} else {
return 0 if ($rec eq "<$addr>");
}
}
im_debug("adding to recipients: <$addr>\n") if (&debug("rcpt"));
$addr = "<$addr>" unless ($bcc_flag);
push (@main::Recipients, $addr);
return 0;
}
##### PARSE RECIPIENT LIST #####
#
# parse_rcpt(bcc_flag, addr_list, need_code_conversion)
# bcc_flag:
# 1 = BCC distination
# 0 = normal distination
# -1 = parse only
# addr_list: address list string (concatinated with ",")
# return value: number of addresses in the list (-1 if error)
#
sub parse_rcpt($$$) {
my($bcc_flag, $addr_list, $conv) = @_;
my($cnt, $addr);
$addr_list =~ s/^\s+//;
$addr_list =~ s/\n\s*//g; # XXX
return 0 if ($addr_list eq '');
## if ISO2022JP
if ($conv) {
if ($main::Iso2022jp_code_conversion) {
require IM::Japanese;
import IM::Japanese qw(conv_iso2022jp);
$addr_list = conv_iso2022jp($addr_list);
}
if ($main::Iso2022jp_header_mime_conv) {
require IM::Iso2022jp;
import IM::Iso2022jp qw(struct_iso2022jp_mimefy);
my $a = &struct_iso2022jp_mimefy($addr_list);
return -1 if ($a eq '');
$addr_list = $a;
}
}
## endif
$cnt = 0;
while ($addr_list ne '') {
($addr, $addr_list) = &fetch_addr($addr_list, 1);
return -1 if ($addr eq '');
if ($bcc_flag >= 0) {
if ($addr =~ /^.+:[^;]*;$/) { # YYY
return -1 if (&expn_group($bcc_flag, $addr) < 0);
} else {
return -1 if (&add_to_rcpt($bcc_flag, $addr) < 0);
}
}
$cnt++;
$addr_list =~ s/^\s*//;
}
return $cnt;
}
##### EXPAND GROUP/LIST SYNTAX #####
#
# expn_group(bcc_flag, group_syntax_address)
# bcc_flag: need "bcc" style message format if true
# group_syntax_address: an gruop:addrs; style address to be expanded
# return value:
# 0: success
# -1: failure
#
sub expn_group($$) {
my($bcc_flag, $group_expression) = @_;
my($rest, @mboxes, $rec);
# return if ($group_expression !~ /:.*;$/);
return if ($group_expression !~ /:[^;]*;/);
im_debug("expanding $group_expression\n") if (&debug('rcpt'));
# my($group_name);
# $group_name = $group_expression;
# $group_name =~ s/:.*/:;/;
$rest = $group_expression;
$rest =~ s/.+:([^;]*);$/$1/; # YYY
@mboxes = split (',', $rest);
foreach $rec (@mboxes) {
if ($rec !~ /\@/
&& ($rec =~ /^\// || $rec =~ /^\~/ || $rec =~ /^\w:\//)) {
&expn_rcpt_list($bcc_flag, $rec);
} else {
return -1 if (&add_to_rcpt($bcc_flag, $rec) < 0);
}
}
# return $group_name;
return 0;
}
##### INCLUDE RECIPIENT LIST FILE #####
#
# expn_rcpt_list(bcc_flag, include_file)
# bcc_flag: need "bcc" style message format if true
# include_file: path of an address list file to be included
# return value:
# 0: success
# -1: failure
#
sub expn_rcpt_list($$) {
my($bcc_flag, $include_file) = @_;
my($file) = &expand_path($include_file);
my($INCLUDE) = &include_open($file);
if ($INCLUDE) {
while (defined($_ = include_readline($INCLUDE))) {
chomp;
im_debug("reading $file: $_\n")
if (&debug('include'));
next if (/^#/);
s/\s#@#.*//;
s/\s*$//;
if (/^\s*:include:/) {
if ($Include_count++ == 100) {
im_err("May be include loop: $_\n");
return -1;
}
s/^\s*:include://;
# my $hdl = *INCLUDE; # save for reccursive
&expn_rcpt_list($bcc_flag, $_);
# *INCLUDE = $hdl;
} elsif (/^\s*\//) {
im_err("Mail to file not supported: $_\n");
return -1;
} elsif (/^\s*\|/ || /^\s*"\s*\|/) { #"
im_err("Mail to program not supported: $_\n");
return -1;
}
return -1 if (&parse_rcpt($bcc_flag, $_, 0) < 0);
}
include_close($INCLUDE);
} else {
im_err("List file $file not found\n");
return -1;
}
return 0;
}
##### PICK UP RECIPIENTS FROM HEADER #####
#
# rcpt_pickup(header, resend_flag, news_only_flag)
# resend_flag: pickup addresses for redistributing mode
# news_only_flag: do not pickup destination addresses for news mode
# return value:
# -1: failure
# 0: success
#
sub rcpt_pickup($$$) {
my($Header, $resend_flag, $news_only_flag) = @_;
my $line;
my $resend_prefix;
foreach $line (@$Header) {
if ($line =~ /^Fcc:(.*)/is) {
$main::Fcc_folder = $1;
$main::Fcc_folder =~ s/\s//g;
}
if ($news_only_flag) {
if ($line =~ /^Dcc:(.*)/is) {
return -1 if (&parse_rcpt(0, $1, 0) < 0);
} elsif ($line =~ /^Bcc:(.*)/is) {
return -1 if (&parse_rcpt($main::Mime_bcc, $1, 0) < 0);
}
} else {
$resend_prefix = $resend_flag ? "Resent-" : "";
if ($line =~ /^${resend_prefix}(To|Cc|Dcc):(.*)/is) {
return -1 if (&parse_rcpt(0, $2, 0) < 0);
} elsif ($line =~ /^${resend_prefix}Bcc:(.*)/is) {
return -1 if (&parse_rcpt($main::Mime_bcc, $1, 0) < 0);
}
}
}
return 0;
}
##### OPEN A INCLUDE FILE WITH NEW FILE HANDLE #####
#
# include_open(file_name, handle_name)
# return value: handle
#
no strict 'refs';
sub include_open($) {
my($file) = shift;
return undef if $Include_Files{$file};
im_open($file, $file) || return undef;
$Include_Files{$file} = 1;
return $file;
}
sub include_readline($) {
my $fh = shift;
return scalar(<$fh>);
}
sub include_close($) {
my($fh) = shift;
close($fh);
delete $Include_Files{$fh};
}
1;
__END__
=head1 NAME
IM::Recipient - mail/news recipient handler
=head1 SYNOPSIS
use IM::Recipient;
add_to_rcpt(bcc_flag, addr)
bcc_flag: register with "bcc" tag
addr: an address to be registered
return value: none
0: success
-1: failure
parse_rcpt(bcc_flag, addr_list, need_code_conversion)
bcc_flag:
1 = BCC distination
0 = normal distination
-1 = parse only
addr_list: address list string (concatinated with ",")
return value: number of addresses in the list (-1 if error)
rcpt_pickup(header, resend_flag, news_only_flag)
resend_flag: pickup addresses for redistributing mode
news_only_flag: do not pickup destination addresses for news mode
return value:
-1: failure
0: success
=head1 DESCRIPTION
The I module handles recipient of mail/news message.
This modules is provided by IM (Internet Message).
=head1 COPYRIGHT
IM (Internet Message) is copyrighted by IM developing team.
You can redistribute it and/or modify it under the modified BSD
license. See the copyright file for more details.
=cut
### Copyright (C) 1997, 1998, 1999 IM developing team
### All rights reserved.
###
### Redistribution and use in source and binary forms, with or without
### modification, are permitted provided that the following conditions
### are met:
###
### 1. Redistributions of source code must retain the above copyright
### notice, this list of conditions and the following disclaimer.
### 2. Redistributions in binary form must reproduce the above copyright
### notice, this list of conditions and the following disclaimer in the
### documentation and/or other materials provided with the distribution.
### 3. Neither the name of the team nor the names of its contributors
### may be used to endorse or promote products derived from this software
### without specific prior written permission.
###
### THIS SOFTWARE IS PROVIDED BY THE TEAM AND CONTRIBUTORS ``AS IS'' AND
### ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
### IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
### PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TEAM OR CONTRIBUTORS BE
### LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
### CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
### SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
### BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
### WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
### OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
### IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.