Commit b06c2ef8 authored by sikeda's avatar sikeda
Browse files

[dev] Separating Sympa::Message::Template from Sympa::Message.


git-svn-id: https://subversion.renater.fr/sympa/branches/sympa-6.2-branch@12593 05aa8bb8-cd2b-0410-b1d7-8918dfa770ce
parent 488f897f
......@@ -65,6 +65,7 @@ nobase_modules_DATA = \
Sympa/Message.pm \
Sympa/Message/Plugin.pm \
Sympa/Message/Plugin/FixEncoding.pm \
Sympa/Message/Template.pm \
Sympa/ModDef.pm \
Sympa/Process.pm \
Sympa/Regexps.pm \
......
......@@ -47,6 +47,7 @@ use warnings;
use DateTime;
use Digest::MD5;
use English qw(-no_match_vars);
use Scalar::Util qw();
use Sympa::Alarm;
use Sympa::Auth;
......@@ -55,7 +56,7 @@ use Conf;
use Sympa::Constants;
use Sympa::Language;
use Sympa::Log;
use Sympa::Message;
use Sympa::Message::Template;
use tools;
use Sympa::Tools::Data;
use Sympa::Tools::Text;
......@@ -604,7 +605,8 @@ sub send_dsn {
my $status = shift;
my $diag = shift;
unless (ref $message eq 'Sympa::Message') {
unless (Scalar::Util::blessed($message)
and $message->isa('Sympa::Message')) {
$log->syslog('err', 'object %s is not Message', $message);
return undef;
}
......@@ -658,11 +660,12 @@ sub send_dsn {
(eval { DateTime->now(time_zone => 'local') } || DateTime->now)
->strftime('%a, %{day} %b %Y %H:%M:%S %z');
my $dsn_message = Sympa::Message->new_from_template(
$that,
'delivery_status_notification',
$sender,
{ %$param,
my $dsn_message = Sympa::Message::Template->new(
context => $that,
template => 'delivery_status_notification',
rcpt => $sender,
data => {
%$param,
'to' => $sender,
'date' => $date,
'msg' => $msg_string,
......@@ -720,9 +723,13 @@ sub send_file {
my $context = shift || {};
my %options = @_;
my $message =
Sympa::Message->new_from_template($that, $tpl, $who, $context,
%options);
my $message = Sympa::Message::Template->new(
context => $that,
template => $tpl,
rcpt => $who,
data => $context,
%options
);
unless ($message and defined Sympa::Bulk->new->store($message, $who)) {
$log->syslog('err', 'Could not send template %s to %s', $tpl, $who);
......@@ -848,9 +855,12 @@ sub send_notify_to_listmaster {
or $operation eq 'no_db'
or $operation eq 'db_restored');
my $notif_message =
Sympa::Message->new_from_template($that,
'listmaster_notification', $email, $ts->{'data'});
my $notif_message = Sympa::Message::Template->new(
context => $that,
template => 'listmaster_notification',
rcpt => $email,
data => $ts->{'data'}
);
unless (
$notif_message
......
......@@ -31,7 +31,7 @@ use Sympa::Bulk;
use Conf;
use Sympa::Log;
use Sympa::Mailer;
use Sympa::Message;
use Sympa::Message::Template;
use tools;
use base qw(Class::Singleton);
......@@ -145,10 +145,12 @@ sub flush {
or $operation eq 'no_db'
or $operation eq 'db_restored';
my $message =
Sympa::Message->new_from_template($robot_id,
'listmaster_groupednotifications',
$rcpt, $param);
my $message = Sympa::Message::Template->new(
context => $robot_id,
template => 'listmaster_groupednotifications',
rcpt => $rcpt,
data => $param
);
unless ($message) {
$log->syslog(
'notice',
......
......@@ -52,7 +52,7 @@ use Sympa::Language;
use Sympa::ListDef;
use Sympa::LockedFile;
use Sympa::Log;
use Sympa::Message;
use Sympa::Message::Template;
use Sympa::Regexps;
use Sympa::Robot;
use Sympa::Scenario;
......@@ -2185,7 +2185,12 @@ sub send_probe_to_user {
my $type = shift;
my $who = shift;
my $message = Sympa::Message->new_from_template($self, $type, $who, {});
my $message = Sympa::Message::Template->new(
context => $self,
template => $type,
rcpt => $who,
data => {}
);
if ($message) {
# Shelve VERP for welcome or remind message if necessary
if ( $self->{'admin'}{'welcome_return_path'} eq 'unique'
......
......@@ -59,7 +59,7 @@ sub _new_instance {
#DEPRECATED: No longer used.
#sub mail_file($robot, $filename, $rcpt, $data, $return_message_as_string);
##DEPRECATED: Use Sympa::Message->new_from_template() & send_message().
##DEPRECATED: Use Sympa::Message::Template::new() & send_message().
#sub mail_message($message, $rcpt, [tag_as_last => 1]);
# DEPRECATED: this is now a subroutine of Sympa::List::distribute_msg().
......
......@@ -303,405 +303,8 @@ sub _get_message_id {
# Old names: (part of) mail::mail_file(), mail::parse_tt2_messageasstring(),
# List::send_file(), List::send_global_file().
sub new_from_template {
$log->syslog('debug2', '(%s, %s, %s, %s, %s, ...)', @_);
my $class = shift;
my $that = shift;
my $tpl = shift;
my $who = shift;
my $context = shift;
my %options = @_;
die 'Parameter $tpl is not defined'
unless defined $tpl and length $tpl;
my ($list, $robot_id);
if (ref $that eq 'Sympa::List') {
$robot_id = $that->{'domain'};
$list = $that;
} elsif ($that and $that ne '*') {
$robot_id = $that;
} else {
$robot_id = '*';
}
my $data = Sympa::Tools::Data::dup_var($context);
## Any recipients
if (not $who or (ref $who and !@$who)) {
$log->syslog('err', 'No recipient for sending %s', $tpl);
return undef;
}
## Unless multiple recipients
unless (ref $who) {
unless ($data->{'user'}) {
$data->{'user'} = Sympa::User->new($who);
}
if ($list) {
# FIXME: Don't overwrite date & update_date. Format datetime on
# the template.
my $subscriber =
Sympa::Tools::Data::dup_var($list->get_list_member($who));
if ($subscriber) {
$data->{'subscriber'}{'date'} =
$language->gettext_strftime("%d %b %Y",
localtime($subscriber->{'date'}));
$data->{'subscriber'}{'update_date'} =
$language->gettext_strftime("%d %b %Y",
localtime($subscriber->{'update_date'}));
if ($subscriber->{'bounce'}) {
$subscriber->{'bounce'} =~
/^(\d+)\s+(\d+)\s+(\d+)(\s+(.*))?$/;
$data->{'subscriber'}{'first_bounce'} =
$language->gettext_strftime("%d %b %Y", localtime $1);
}
}
}
unless ($data->{'user'}{'password'}) {
$data->{'user'}{'password'} =
Sympa::Tools::Password::tmp_passwd($who);
}
}
# Lang
$language->push_lang(
$data->{'lang'},
$data->{'user'}{'lang'},
($list ? $list->{'admin'}{'lang'} : undef),
Conf::get_robot_conf($robot_id, 'lang'), 'en'
);
$data->{'lang'} = $language->get_lang;
$language->pop_lang;
if ($list) {
# Trying to use custom_vars
if (defined $list->{'admin'}{'custom_vars'}) {
$data->{'custom_vars'} = {};
foreach my $var (@{$list->{'admin'}{'custom_vars'}}) {
$data->{'custom_vars'}{$var->{'name'}} = $var->{'value'};
}
}
}
foreach my $p (
'email', 'gecos', 'host', 'sympa',
'request', 'listmaster', 'wwsympa_url', 'title',
'listmaster_email'
) {
$data->{'conf'}{$p} = Conf::get_robot_conf($robot_id, $p);
}
$data->{'conf'}{'version'} = Sympa::Constants::VERSION();
$data->{'sender'} ||= $who;
if ($list) {
$data->{'list'}{'lang'} = $list->{'admin'}{'lang'};
$data->{'list'}{'name'} = $list->{'name'};
$data->{'list'}{'domain'} = $data->{'robot_domain'} = $robot_id;
$data->{'list'}{'host'} = $list->{'admin'}{'host'};
$data->{'list'}{'subject'} = $list->{'admin'}{'subject'};
$data->{'list'}{'owner'} = [$list->get_admins('owner')];
$data->{'list'}{'dir'} = $list->{'dir'}; #FIXME: Required?
$data->{'list'}{'family'} = {name => $list->get_family->{'name'}}
if $list->get_family;
}
# Sign mode
my $smime_sign = Sympa::Tools::SMIME::find_keys($that, 'sign');
if ($list) {
# if the list have it's private_key and cert sign the message
# . used only for the welcome message, could be useful in other case?
# . a list should have several certificates and use if possible a
# certificate issued by the same CA as the recipient CA if it exists
if ($smime_sign) {
$data->{'fromlist'} = $list->get_list_address();
$data->{'replyto'} = $list->get_list_address('owner');
} else {
$data->{'fromlist'} = $list->get_list_address('owner');
}
} else {
$data->{'robot_domain'} = Conf::get_robot_conf($robot_id, 'domain');
}
$data->{'boundary'} = '----------=_' . tools::get_message_id($robot_id)
unless $data->{'boundary'};
my $self = $class->_new_from_template($that, $tpl . '.tt2', $who, $data);
return undef unless $self;
# Shelve S/MIME signing.
$self->{shelved}{smime_sign} = 1
if $smime_sign;
# Shelve DKIM signing.
if (Conf::get_robot_conf($robot_id, 'dkim_feature') eq 'on') {
my $dkim_add_signature_to =
Conf::get_robot_conf($robot_id, 'dkim_add_signature_to');
if ($list and $dkim_add_signature_to =~ /list/
or not $list and $dkim_add_signature_to =~ /robot/) {
$self->{shelved}{dkim_sign} = 1;
}
}
# Set default envelope sender.
if ($list) {
$self->{envelope_sender} = $list->get_list_address('return_path');
} else {
$self->{envelope_sender} = Conf::get_robot_conf($robot_id, 'request');
}
# Set default delivery date.
$self->{date} = (exists $options{date}) ? $options{date} : time;
# Assign unique ID and log it.
my $marshalled =
Sympa::Spool::marshal_metadata($self, '%s@%s.%ld.%ld,%d',
[qw(localpart domainpart date PID RAND)]);
$self->{messagekey} = $marshalled;
$log->syslog(
'notice',
'Processing %s; message_id=%s; recipients=%s; sender=%s; template=%s; %s',
$self,
$self->{message_id},
$who,
$self->{sender},
$tpl,
join('; ',
map { $data->{$_} ? ("$_=$data->{$_}") : () }
qw(type action reason status))
);
return $self;
}
#TODO: This would be merged in new_from_template() because used only by it.
sub _new_from_template {
$log->syslog('debug2', '(%s, %s, %s, %s, %s)', @_);
my $class = shift;
my $that = shift || '*';
my $filename = shift;
my $rcpt = shift;
my $data = shift;
my ($list, $robot_id);
if (ref $that eq 'Sympa::List') {
$list = $that;
$robot_id = $list->{'domain'};
} elsif ($that and $that ne '*') {
$robot_id = $that;
} else {
$robot_id = '*';
}
my $message_as_string;
my %header_ok; # hash containing no missing headers
my $existing_headers = 0; # the message already contains headers
## We may receive a list of recipients
die sprintf 'Wrong type of reference for $rcpt: %s', ref $rcpt
if ref $rcpt and ref $rcpt ne 'ARRAY';
## Charset for encoding
$data->{'charset'} ||= tools::lang2charset($data->{'lang'});
# Template file parsing
# If context is List, add list directory and list archives to get the
# 'info' file and last message.
my $template = Sympa::Template->new(
$that,
subdir => 'mail_tt2',
lang => $data->{'lang'},
include_path =>
($list ? [$list->{'dir'}, $list->{'dir'} . '/archives'] : [])
);
unless ($template->parse($data, $filename, \$message_as_string)) {
$log->syslog(
'err', 'Can\'t parse template %s: %s',
$filename, $template->{last_error}
);
return undef;
}
# Does the message include headers ?
if ($data->{'headers'}) {
foreach my $field (keys %{$data->{'headers'}}) {
$field =~ tr/A-Z/a-z/;
$header_ok{$field} = 1;
}
}
foreach my $line (split /\n/, $message_as_string) {
last if ($line =~ /^\s*$/);
if ($line =~ /^[\w-]+:\s*/) {
## A header field
$existing_headers = 1;
} elsif ($existing_headers and $line =~ /^\s/) {
## Following of a header field
next;
} else {
last;
}
foreach my $header (
qw(message-id date to from subject reply-to
mime-version content-type content-transfer-encoding)
) {
if ($line =~ /^$header\s*:/i) {
$header_ok{$header} = 1;
last;
}
}
}
## ADD MISSING HEADERS
my $headers = "";
unless ($header_ok{'message-id'}) {
$headers .=
sprintf("Message-Id: %s\n", tools::get_message_id($robot_id));
}
unless ($header_ok{'date'}) {
# Format current time.
# If setting local timezone fails, fallback to UTC.
my $date =
(eval { DateTime->now(time_zone => 'local') } || DateTime->now)
->strftime('%a, %{day} %b %Y %H:%M:%S %z');
$headers .= sprintf "Date: %s\n", $date;
}
unless ($header_ok{'to'}) {
my $to;
# Currently, bare e-mail address is assumed. Complex ones such as
# "phrase" <email> won't be allowed.
if (ref($rcpt)) {
if ($data->{'to'}) {
$to = $data->{'to'};
} else {
$to = join(",\n ", @{$rcpt});
}
} else {
$to = $rcpt;
}
$headers .= "To: $to\n";
}
unless ($header_ok{'from'}) {
unless (defined $data->{'from'}) {
$headers .= sprintf "From: %s\n",
tools::addrencode(
Conf::get_robot_conf($robot_id, 'sympa'),
Conf::get_robot_conf($robot_id, 'gecos'),
$data->{'charset'}
);
} elsif ($data->{'from'} eq 'sympa'
or $data->{'from'} eq $data->{'conf'}{'sympa'}) {
#XXX NOTREACHED: $data->{'from'} was obsoleted.
$headers .= 'From: '
. tools::addrencode(
$data->{'conf'}{'sympa'},
$data->{'conf'}{'gecos'},
$data->{'charset'}
) . "\n";
} else {
#XXX NOTREACHED: $data->{'from'} was obsoleted.
$headers .= "From: "
. MIME::EncWords::encode_mimewords(
Encode::decode('utf8', $data->{'from'}),
'Encoding' => 'A',
'Charset' => $data->{'charset'},
'Field' => 'From'
) . "\n";
}
}
unless ($header_ok{'subject'}) {
$headers .= "Subject: "
. MIME::EncWords::encode_mimewords(
Encode::decode('utf8', $data->{'subject'}),
'Encoding' => 'A',
'Charset' => $data->{'charset'},
'Field' => 'Subject'
) . "\n";
}
unless ($header_ok{'reply-to'}) {
$headers .= "Reply-to: "
. MIME::EncWords::encode_mimewords(
Encode::decode('utf8', $data->{'replyto'}),
'Encoding' => 'A',
'Charset' => $data->{'charset'},
'Field' => 'Reply-to'
)
. "\n"
if ($data->{'replyto'});
}
if ($data->{'headers'}) {
foreach my $field (keys %{$data->{'headers'}}) {
$headers .=
$field . ': '
. MIME::EncWords::encode_mimewords(
Encode::decode('utf8', $data->{'headers'}{$field}),
'Encoding' => 'A',
'Charset' => $data->{'charset'},
'Field' => $field
) . "\n";
}
}
unless ($header_ok{'mime-version'}) {
$headers .= "MIME-Version: 1.0\n";
}
unless ($header_ok{'content-type'}) {
$headers .=
"Content-Type: text/plain; charset=" . $data->{'charset'} . "\n";
}
unless ($header_ok{'content-transfer-encoding'}) {
$headers .= "Content-Transfer-Encoding: 8bit\n";
}
# Determine what value the Auto-Submitted header field should take.
# See RFC 3834. The header field can have one of the following keywords:
# "auto-generated", "auto-replied".
# The header should not be set when WWSympa sends a command to sympa.pl
# through its spool.
# n.b. The keyword "auto-forwarded" was abandoned.
unless ($data->{'not_auto_submitted'} || $header_ok{'auto_submitted'}) {
## Default value is 'auto-generated'
my $header_value = $data->{'auto_submitted'} || 'auto-generated';
$headers .= "Auto-Submitted: $header_value\n";
}
unless ($existing_headers) {
$headers .= "\n";
}
## All these data provide mail attachements in service messages
my @msgs = ();
if (ref($data->{'msg_list'}) eq 'ARRAY') {
@msgs =
map { $_->{'msg'} || $_->{'full_msg'} } @{$data->{'msg_list'}};
} elsif ($data->{'spool'}) {
@msgs = @{$data->{'spool'}};
} elsif ($data->{'msg'}) {
push @msgs, $data->{'msg'};
} elsif ($data->{'msg_path'} and open IN, '<' . $data->{'msg_path'}) {
push @msgs, join('', <IN>);
close IN;
} elsif ($data->{'file'} and open IN, '<' . $data->{'file'}) {
push @msgs, join('', <IN>);
close IN;
}
my $self = $class->new($headers . $message_as_string, context => $that);
return undef unless $self;
unless ($self->reformat_utf8_message(\@msgs, $data->{'charset'})) {
$log->syslog('err', 'Failed to reformat message');
}
return $self;
}
# Moved to: Sympa::Message::Template::new().
#sub new_from_template;
sub dup {
my $self = shift;
......@@ -2453,7 +2056,8 @@ sub _fix_utf8_parts {
}
$entity->parts([$data]);
} else {
if (ref $data eq 'Sympa::Message') {
if (Scalar::Util::blessed($data)
and $data->isa('Sympa::Message')) {
$data = $data->as_string;
} elsif (ref $data) {
die sprintf 'Unsupported type for attachment: %s', ref $data;
......@@ -3714,87 +3318,6 @@ Returns:
A new L<Sympa::Message> object, or I<undef>, if something went wrong.
=item new_from_template ( $that, $filename, $rcpt, $data )
I<Constructor>.
Creates L<Sympa::Message> object from template.
Parameters:
=over
=item $that
Content: Sympa::List, robot or '*'.
=item $filename
Template filename (without extension).
=item $rcpt
Scalar or arrayref: SMTP "RCPT TO:" field.
If it is a scalar, trys to retrieve information of the user
(See also L<Sympa::User>.
=item $data
Hashref used to parse template file, with keys:
=over
=item return_path
SMTP "MAIL FROM:" field if sent by SMTP (see L<Sympa::Mailer>),
"Return-Path:" field if sent by spool.
Note: This parameter is obsoleted. Currently, {envelope_sender} attribute of
object is taken from the context.
=item to
"To:" header field
=item lang