Commit 73b03bd8 authored by sikeda's avatar sikeda
Browse files

[dev] Refactoring: Sympa::Mail::mail_file() is deprecated; instead, use...

[dev] Refactoring: Sympa::Mail::mail_file() is deprecated; instead, use Sympa::Message->new_from_template() and Sympa::Mail::sending().

Known bug:
Command messages sent via wwsympa.fcgi by users don't have sufficient authentication level.  "md5" level is required.


git-svn-id: https://subversion.renater.fr/sympa/branches/sympa-6.2-branch@11286 05aa8bb8-cd2b-0410-b1d7-8918dfa770ce
parent f7cb738e
......@@ -9242,19 +9242,24 @@ sub do_distribute {
. $id);
}
 
#FIXME: commands should be injected into incoming spool directly.
my $data = {
'headers' => {
'Message-ID' => tools::get_message_id($robot),
'X-Sympa-NoWrap' => 'yes'
},
'from' => $param->{'user'}{'email'},
'not_auto_submitted' => 1,
'body' => join("\n", @mail_command),
};
#FIXME: Commands should be injected into incoming spool directly.
#FIXME: Message should have sufficient authentication level.
my $message =
Sympa::Message->new(sprintf("\n\n%s\n", join("\n", @mail_command)),
context => $robot);
$message->{envelope_sender} = Conf::get_robot_conf($robot, 'request');
$message->add_header('Message-ID', tools::get_message_id($robot));
$message->add_header('From', $param->{'user'}{'email'});
$message->add_header('To', Conf::get_robot_conf($robot, 'sympa'));
$message->add_header('Content-Type', 'text/plain; Charset=utf-8');
unless (
Sympa::Mail::mail_file(
$robot, '', Conf::get_robot_conf($robot, 'sympa'), $data
$message
and defined Sympa::Mail::sending(
'message' => $message,
'rcpt' => Conf::get_robot_conf($robot, 'sympa'),
'from' => Conf::get_robot_conf($robot, 'request'),
'robot' => $robot,
'priority' => Conf::get_robot_conf($robot, 'sympa_priority'),
)
) {
Sympa::Report::reject_report_web(
......@@ -21111,20 +21116,25 @@ sub do_remind {
$mail_command = sprintf "REMIND %s", $param->{'list'};
}
 
#FIXME: Commands should be injected into incoming spool directly.
#FIXME: Message should have sufficient authentication level.
my $time = time;
my $data = {
'headers' => {
'Message-ID' => '<' . $time . '@wwsympa>',
'X-Sympa-NoWrap' => 'yes'
},
'from' => $param->{'user'}{'email'},
'body' => $mail_command
};
my $message = Sympa::Message->new(sprintf("\n\n%s\n", $mail_command),
context => $robot);
$message->{envelope_sender} = Conf::get_robot_conf($robot, 'request');
$message->add_header('Message-ID', sprintf '<%s@wwsympa>', $time);
$message->add_header('From', $param->{'user'}{'email'});
$message->add_header('To', Conf::get_robot_conf($robot, 'sympa'));
$message->add_header('Content-Type', 'text/plain; Charset=utf-8');
 
$data->{'not_auto_submitted'} = 1;
unless (
Sympa::Mail::mail_file(
$robot, '', Conf::get_robot_conf($robot, 'sympa'), $data
$message
and defined Sympa::Mail::sending(
'message' => $message,
'rcpt' => Conf::get_robot_conf($robot, 'sympa'),
'from' => Conf::get_robot_conf($robot, 'request'),
'robot' => $robot,
'priority' => Conf::get_robot_conf($robot, 'sympa_priority'),
)
) {
Sympa::Report::reject_report_web(
......@@ -22325,21 +22335,35 @@ sub do_send_mail {
}
}
 
my $data = {
'headers' => {'Message-ID' => $in{'message_id'}},
'subject' => $in{'subject'},
'return_path' => Conf::get_robot_conf($robot, 'sympa'),
'to' => $to,
'body' => "From: $from\n" . $in{'body'},
'sign_mode' => '',
'header_possible' => '1'
};
$data->{'headers'}{'In-Reply-To'} = $in{'in_reply_to'}
if (($in{'in_reply_to'}) && $in{'in_reply_to'} ne '<>');
$data->{'not_auto_submitted'} = 1;
$data->{'not_a_list'} = 1 if $in{'sub_action'} eq 'sendmailtome';
unless (Sympa::Mail::mail_file($robot, '', $to, $data)) {
#FIXME: Message should have sufficient authentication level.
my $message = Sympa::Message->new_from_template(
$list, '', $to,
{ 'headers' => {
'Message-ID' => $in{'message_id'},
( ($in{'in_reply_to'} and $in{'in_reply_to'} ne '<>')
? ('In-Reply-To' => $in{'in_reply_to'})
: ()
),
},
'subject' => $in{'subject'},
'return_path' => Conf::get_robot_conf($robot, 'sympa'),
'to' => $to,
'from' => $from,
'body' => $in{'body'},
'not_auto_submitted' => 1,
'not_a_list' => ($in{'sub_action'} eq 'sendmailtome'), #not used?
},
);
unless (
$message
and defined Sympa::Mail::sending(
'message' => $message,
'rcpt' => $to,
'from' => Conf::get_robot_conf($robot, 'sympa'),
'robot' => $robot,
'priority' => Conf::get_robot_conf($robot, 'sympa_priority'),
)
) {
Sympa::Report::reject_report_web(
'intern',
'cannot_send_mail',
......@@ -22525,18 +22549,6 @@ sub do_tag_topic_by_sender {
my $filetopic =
$list->tag_topic($in{'message_id'}, $list_topics, 'sender');
 
## CONFIRM
my $time = time;
my $data = {
'headers' => {
'Message-ID' => '<' . $time . '@wwsympa>',
'X-Sympa-NoWrap' => 'yes'
},
'from' => $sender
};
$data->{'body'} = sprintf("QUIET CONFIRM %s\n", $in{'authkey'});
my $queueauth = Conf::get_robot_conf($robot, 'queueauth');
my $filemsg = "$queueauth/$list->{'name'}_$in{'authkey'}";
 
......@@ -22555,10 +22567,27 @@ sub do_tag_topic_by_sender {
return undef;
}
 
$data->{'not_auto_submitted'} = 1;
## CONFIRM
#FIXME: Commands should be injected into incoming spool directly.
#FIXME: Message should have sufficient authentication level.
my $time = time;
my $message =
Sympa::Message->new(sprintf("\n\nQUIET CONFIRM %s\n", $in{'authkey'}),
context => $robot);
$message->{envelope_sender} = Conf::get_robot_conf($robot, 'request');
$message->add_header('Message-ID', sprintf '<%s@wwsympa>', $time);
$message->add_header('From', $sender);
$message->add_header('To', Conf::get_robot_conf($robot, 'sympa'));
$message->add_header('Content-Type', 'text/plain; Charset=utf-8');
unless (
Sympa::Mail::mail_file(
$robot, '', Conf::get_robot_conf($robot, 'sympa'), $data
$message
and defined Sympa::Mail::sending(
'message' => $message,
'rcpt' => Conf::get_robot_conf($robot, 'sympa'),
'from' => Conf::get_robot_conf($robot, 'request'),
'robot' => $robot,
'priority' => Conf::get_robot_conf($robot, 'sympa_priority'),
)
) {
Sympa::Report::reject_report_web(
......
......@@ -2467,7 +2467,6 @@ sub send_file {
my ($self, $tpl, $who, $robot, $context) = @_;
Log::do_log('debug2', '(%s, %s, %s)', $tpl, $who, $robot);
my $name = $self->{'name'};
my $sign_mode;
my $data = tools::dup_var($context);
......@@ -2574,7 +2573,7 @@ sub send_file {
$data->{'sender'} ||= $who;
$data->{'list'}{'lang'} = $self->{'admin'}{'lang'};
$data->{'list'}{'name'} = $name;
$data->{'list'}{'name'} = $self->{'name'};
$data->{'list'}{'domain'} = $data->{'robot_domain'} = $robot;
$data->{'list'}{'host'} = $self->{'admin'}{'host'};
$data->{'list'}{'subject'} = $self->{'admin'}{'subject'};
......@@ -2618,8 +2617,23 @@ sub send_file {
# to support Sympa server on a machine without any MTA service
$data->{'use_bulk'} = 1
unless ($data->{'alarm'});
unless (Sympa::Mail::mail_file($self->{'domain'}, $filename, $who, $data))
{
my $message =
Sympa::Message->new_from_template($self, $filename, $who, $data);
unless (
$message
and defined Sympa::Mail::sending(
'message' => $message,
'rcpt' => $who,
'from' => $data->{'return_path'},
'robot' => $robot,
'listname' => $self->{'name'},
'priority' => Conf::get_robot_conf($robot, 'sympa_priority'),
'sign_mode' => $data->{'sign_mode'},
'use_bulk' => $data->{'use_bulk'},
'dkim' => $data->{'dkim'},
)
) {
Log::do_log('err', 'Could not send template %s to %s',
$filename, $who);
return undef;
......@@ -2654,7 +2668,6 @@ sub send_msg {
my $apply_tracking = $param{'apply_tracking'};
my $original_message_id = $message->{'message_id'};
my $name = $self->{'name'};
my $robot = $self->{'domain'};
my $admin = $self->{'admin'};
my $total = $self->get_total('nocache');
......@@ -2664,7 +2677,7 @@ sub send_msg {
}
unless ($total > 0) {
Log::do_log('info', 'No subscriber in list %s', $name);
Log::do_log('info', 'No subscriber in list %s', $self);
return 0;
}
......@@ -2843,7 +2856,6 @@ sub get_recipients_per_mode {
my $message = shift;
my %options = @_;
my $name = $self->{'name'};
my $robot = $self->{'domain'};
my $sender_line = $message->as_entity->head->get('From');
......
......@@ -80,273 +80,8 @@ sub set_send_spool {
$send_spool = $spool;
}
####################################################
# public mail_file
####################################################
# send a tt2 file
#
#
# IN : -$filename(+) : tt2 filename (with .tt2) | ''
# -$rcpt(+) : SCALAR |ref(ARRAY) : SMTP "RCPT To:" field
# -$data(+) : used to parse tt2 file, ref(HASH) with keys :
# -return_path(+) : SMTP "MAIL From:" field if send by smtp,
# "X-Sympa-From:" field if send by spool
# -to : "To:" header field
# -lang : tt2 language if $filename
# -list : ref(HASH) if $sign_mode = 'smime', keys are :
# -name
# -dir
# -from : "From:" field if not a full msg
# -subject : "Subject:" field if not a full msg
# -replyto : "Reply-to:" field if not a full msg
# -body : body message if not $filename
# -headers : ref(HASH) with keys are headers mail
# -dkim : a set of parameters for appying DKIM signature
# -d : d=tag
# -i : i=tag (optionnal)
# -selector : dkim dns selector
# -key : the RSA private key
# -$robot(+)
# -$sign_mode :'smime' | '' | undef
#
# OUT : 1 | undef
####################################################
sub mail_file {
Log::do_log('debug2', '(%s, %s, %s, %s, %s)', @_);
my $robot = shift || '*';
my $filename = shift;
my $rcpt = shift;
my $data = shift;
my $return_message_as_string = shift;
my $header_possible = $data->{'header_possible'};
my $sign_mode = $data->{'sign_mode'};
my ($to, $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 a recepients
if (ref($rcpt)) {
unless (ref($rcpt) eq 'ARRAY') {
Log::do_log('notice', 'Wrong type of reference for rcpt');
return undef;
}
}
## Charset for encoding
$data->{'charset'} ||= tools::lang2charset($data->{'lang'});
## TT2 file parsing
#FIXME: Check TT2 parse error
if ($filename =~ /\.tt2$/) {
my $output;
my @path = split /\//, $filename;
tt2::parse_tt2($data, $path[$#path], \$output);
$message_as_string .= join('', $output);
$header_possible = 1;
} else { # or not
$message_as_string .= $data->{'body'};
}
# Does the message include headers ?
if ($data->{'headers'}) {
foreach my $field (keys %{$data->{'headers'}}) {
$field =~ tr/A-Z/a-z/;
$header_ok{$field} = 1;
}
}
if ($header_possible) {
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 && ($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));
}
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'}) {
# 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'}) {
if ( !defined $data->{'from'}
or $data->{'from'} eq 'sympa'
or $data->{'from'} eq $data->{'conf'}{'sympa'}) {
$headers .= 'From: '
. tools::addrencode(
$data->{'conf'}{'sympa'},
$data->{'conf'}{'gecos'},
$data->{'charset'}
) . "\n";
} else {
$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 http://www.tools.ietf.org/html/draft-palme-autosub-01
## the header filed can have one of the following values : auto-generated,
## auto-replied, auto-forwarded
## The header should not be set when wwsympa sends a command/mail to
## sympa.pl through its spool
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 $listname = '';
if (ref($data->{'list'}) eq "HASH") { # compatibility
$listname = $data->{'list'}{'name'};
} elsif (ref($data->{'list'}) eq 'Sympa::List') {
$listname = $data->{'list'}->{'name'};
} elsif ($data->{'list'}) {
$listname = $data->{'list'};
}
my $message = Sympa::Message->new(
$headers . $message_as_string,
context => $robot, #FIXME: no list context
);
return undef unless $message;
unless ($message->reformat_utf8_message(\@msgs, $data->{'charset'})) {
Log::do_log('err', 'Failed to reformat message');
}
## Set it in case it was not set
$data->{'return_path'} ||= Conf::get_robot_conf($robot, 'request');
return $message->as_string if $return_message_as_string;
## SENDING
return undef
unless defined sending(
'message' => $message,
'rcpt' => $rcpt,
'from' => $data->{'return_path'},
'robot' => $robot,
'listname' => $listname,
'priority' => Conf::get_robot_conf($robot, 'sympa_priority'),
'sign_mode' => $sign_mode,
'use_bulk' => $data->{'use_bulk'},
'dkim' => $data->{'dkim'},
);
return 1;
}
#sub mail_file($robot, $filename, $rcpt, $data, $return_message_as_string);
##DEPRECATED: Use Sympa::Message->new_from_template() & sending().
####################################################
# public mail_message
......
......@@ -350,6 +350,294 @@ sub _get_message_id {
=over
=item new_from_template ( $that, $filename, $rcpt, $data )
I<Constructor>.
XXX
Parameters:
=over
=item $that
Content: Sympa::List, robot or '*'.
=item $filename
tt2 filename (with .tt2) or C<''>.
=item $rcpt
Scalar or arrayref: SMTP "RCPT To:" field.
=item $data
Hashref used to parse tt2 file, with keys:
=over
=item return_path
SMTP "MAIL From:" field if send by smtp, "X-Sympa-From:" field if send by spool
=item to
"To:" header field
=item lang
tt2 language if $filename
=item from
"From:" field if not a full msg
=item subject
"Subject:" field if not a full msg
=item replyto
"Reply-to:" field if not a full msg
=item body
Body message if $filename is C<''>.
=item headers
Hashref with keys are headers mail
=back
=item $robot
=back
Returns:
New L<Sympa::Message> instance, of C<undef> if something went wrong.
=back
=cut
# Old names: (part of) mail::mail_file(), mail::parse_tt2_messageasstring().
sub new_from_template {
Log::do_log('debug2', '(%s, %s, %s, %s, %s)', @_);
my $class = shift;
my $that = shift || '*';
my $filename = shift;
my $rcpt = shift;
my $data = shift;
my $robot_id =
(ref $that eq 'Sympa::List') ? $that->{'domain'}
: ($that and $that ne '*') ? $that
: '*';
my $header_possible = $data->{'header_possible'};
my ($to, $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
if (ref($rcpt)) {
unless (ref($rcpt) eq 'ARRAY') {
Log::do_log('notice', 'Wrong type of reference for rcpt');
return undef;
}
}
## Charset for encoding
$data->{'charset'} ||= tools::lang2charset($data->{'lang'});
## TT2 file parsing
#FIXME: Check TT2 parse error
if ($filename =~ /\.tt2$/) {
my $output;
my @path = split /\//, $filename;