From 2b9f5bf80911872ca809061bd46d8fc7f64c2929 Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Fri, 2 Jun 2006 17:50:15 +0000 Subject: Bug 339862: Move Bugzilla::BugMail::MessageToMTA() in a separate module - Patch by Frédéric Buclin r=mkanat a=justdave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bugzilla/BugMail.pm | 167 +---------------------------------------- Bugzilla/Flag.pm | 4 +- Bugzilla/Mailer.pm | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Bugzilla/Token.pm | 10 +-- editproducts.cgi | 6 +- importxml.pl | 5 +- process_bug.cgi | 6 +- relogin.cgi | 4 +- whine.pl | 4 +- whineatnews.pl | 4 +- 10 files changed, 234 insertions(+), 187 deletions(-) create mode 100644 Bugzilla/Mailer.pm diff --git a/Bugzilla/BugMail.pm b/Bugzilla/BugMail.pm index 74e1145a7..ab8ca2f6f 100644 --- a/Bugzilla/BugMail.pm +++ b/Bugzilla/BugMail.pm @@ -39,15 +39,10 @@ use Bugzilla::Config qw(:DEFAULT $datadir); use Bugzilla::Util; use Bugzilla::Bug; use Bugzilla::Component; +use Bugzilla::Mailer; use Date::Parse; use Date::Format; -use Mail::Mailer; -use Mail::Header; -use MIME::Base64; -use MIME::QuotedPrint; -use MIME::Parser; -use Mail::Address; use constant BIT_DIRECT => 1; use constant BIT_WATCHING => 2; @@ -670,166 +665,6 @@ sub sendMail { return 1; } -sub MessageToMTA { - my ($msg) = (@_); - return if (Param('mail_delivery_method') eq "none"); - - my ($header, $body) = $msg =~ /(.*?\n)\n(.*)/s ? ($1, $2) : ('', $msg); - my $headers; - - if (Param('utf8') and (!is_7bit_clean($header) or !is_7bit_clean($body))) { - ($headers, $body) = encode_message($msg); - } else { - my @header_lines = split(/\n/, $header); - $headers = new Mail::Header \@header_lines, Modify => 0; - } - - # Use trim to remove any whitespace (incl. newlines) - my $from = trim($headers->get('from')); - - if (Param("mail_delivery_method") eq "sendmail" && $^O =~ /MSWin32/i) { - my $cmd = '|' . SENDMAIL_EXE . ' -t -i'; - if ($from) { - # We're on Windows, thus no danger of command injection - # via $from. In other words, it is safe to embed $from. - $cmd .= qq# -f"$from"#; - } - open(SENDMAIL, $cmd) || - die "Failed to execute " . SENDMAIL_EXE . ": $!\n"; - print SENDMAIL $headers->as_string; - print SENDMAIL "\n"; - print SENDMAIL $body; - close SENDMAIL; - return; - } - - my @args; - if (Param("mail_delivery_method") eq "sendmail") { - push @args, "-i"; - if ($from) { - push(@args, "-f$from"); - } - } - if (Param("mail_delivery_method") eq "sendmail" && !Param("sendmailnow")) { - push @args, "-ODeliveryMode=deferred"; - } - if (Param("mail_delivery_method") eq "smtp") { - push @args, Server => Param("smtpserver"); - if ($from) { - $ENV{'MAILADDRESS'} = $from; - } - } - my $mailer = new Mail::Mailer Param("mail_delivery_method"), @args; - if (Param("mail_delivery_method") eq "testfile") { - $Mail::Mailer::testfile::config{outfile} = "$datadir/mailer.testfile"; - } - - $mailer->open($headers->header_hashref); - print $mailer $body; - $mailer->close; -} - -sub encode_qp_words { - my ($line) = (@_); - my @encoded; - foreach my $word (split / /, $line) { - if (!is_7bit_clean($word)) { - push @encoded, '=?UTF-8?Q?_' . encode_qp($word, '') . '?='; - } else { - push @encoded, $word; - } - } - return join(' ', @encoded); -} - -sub encode_message { - my ($msg) = @_; - - my $parser = MIME::Parser->new; - $parser->output_to_core(1); - $parser->tmp_to_core(1); - my $entity = $parser->parse_data($msg); - $entity = encode_message_entity($entity); - - my @header_lines = split(/\n/, $entity->header_as_string); - my $head = new Mail::Header \@header_lines, Modify => 0; - - my $body = $entity->body_as_string; - - return ($head, $body); -} - -sub encode_message_entity { - my ($entity) = @_; - - my $head = $entity->head; - - # encode the subject - - my $subject = $head->get('subject'); - if (defined $subject && !is_7bit_clean($subject)) { - $subject =~ s/[\r\n]+$//; - $head->replace('subject', encode_qp_words($subject)); - } - - # encode addresses - - foreach my $field (qw(from to cc reply-to sender errors-to)) { - my $high = $head->count($field) - 1; - foreach my $index (0..$high) { - my $value = $head->get($field, $index); - my @addresses; - my $changed = 0; - foreach my $addr (Mail::Address->parse($value)) { - my $phrase = $addr->phrase; - if (is_7bit_clean($phrase)) { - push @addresses, $addr->format; - } else { - push @addresses, encode_qp_phrase($phrase) . - ' <' . $addr->address . '>'; - $changed = 1; - } - } - $changed && $head->replace($field, join(', ', @addresses), $index); - } - } - - # process the body - - if (scalar($entity->parts)) { - my $newparts = []; - foreach my $part ($entity->parts) { - my $newpart = encode_message_entity($part); - push @$newparts, $newpart; - } - $entity->parts($newparts); - } - else { - # Extract the body from the entity, for examination - # At this point, we can rely on MIME::Tools to do our encoding for us! - my $bodyhandle = $entity->bodyhandle; - my $body = $bodyhandle->as_string; - if (!is_7bit_clean($body)) { - # count number of 7-bit chars, and use quoted-printable if more - # than half the message is 7-bit clean - my $count = ($body =~ tr/\x20-\x7E\x0A\x0D//); - if ($count > length($body) / 2) { - $head->mime_attr('Content-Transfer-Encoding' => 'quoted-printable'); - } else { - $head->mime_attr('Content-Transfer-Encoding' => 'base64'); - } - } - - # Set the content/type and charset of the part, if not set - $head->mime_attr('Content-Type' => 'text/plain') - unless defined $head->mime_attr('content-type'); - $head->mime_attr('Content-Type.charset' => 'UTF-8'); - } - - $head->fold(75); - return $entity; -} - # Send the login name and password of the newly created account to the user. sub MailPassword { my ($login, $password) = (@_); diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 1d4f1ebe9..ef8ecd867 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -65,7 +65,7 @@ use Bugzilla::Config; use Bugzilla::Util; use Bugzilla::Error; use Bugzilla::Attachment; -use Bugzilla::BugMail; +use Bugzilla::Mailer; use Bugzilla::Constants; use Bugzilla::Field; @@ -903,7 +903,7 @@ sub notify { ThrowTemplateError($template->error()); } - Bugzilla::BugMail::MessageToMTA($message); + MessageToMTA($message); } } diff --git a/Bugzilla/Mailer.pm b/Bugzilla/Mailer.pm new file mode 100644 index 000000000..05ef82149 --- /dev/null +++ b/Bugzilla/Mailer.pm @@ -0,0 +1,211 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Bug Tracking System. +# +# The Initial Developer of the Original Code is Netscape Communications +# Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): Terry Weissman , +# Bryce Nesbitt +# Dan Mosedale +# Alan Raetz +# Jacob Steenhagen +# Matthew Tuck +# Bradley Baetz +# J. Paul Reed +# Gervase Markham +# Byron Jones +# Frédéric Buclin + +package Bugzilla::Mailer; + +use strict; + +use base qw(Exporter); +@Bugzilla::Mailer::EXPORT = qw(MessageToMTA); + +use Bugzilla::Constants; +use Bugzilla::Config qw(:DEFAULT $datadir); +use Bugzilla::Util; + +use Mail::Header; +use Mail::Mailer; +use Mail::Address; +use MIME::Parser; +use MIME::QuotedPrint; +use MIME::Base64; + + +sub MessageToMTA { + my ($msg) = (@_); + return if (Param('mail_delivery_method') eq "none"); + + my ($header, $body) = $msg =~ /(.*?\n)\n(.*)/s ? ($1, $2) : ('', $msg); + my $headers; + + if (Param('utf8') and (!is_7bit_clean($header) or !is_7bit_clean($body))) { + ($headers, $body) = encode_message($msg); + } else { + my @header_lines = split(/\n/, $header); + $headers = new Mail::Header \@header_lines, Modify => 0; + } + + # Use trim to remove any whitespace (incl. newlines) + my $from = trim($headers->get('from')); + + if (Param("mail_delivery_method") eq "sendmail" && $^O =~ /MSWin32/i) { + my $cmd = '|' . SENDMAIL_EXE . ' -t -i'; + if ($from) { + # We're on Windows, thus no danger of command injection + # via $from. In other words, it is safe to embed $from. + $cmd .= qq# -f"$from"#; + } + open(SENDMAIL, $cmd) || + die "Failed to execute " . SENDMAIL_EXE . ": $!\n"; + print SENDMAIL $headers->as_string; + print SENDMAIL "\n"; + print SENDMAIL $body; + close SENDMAIL; + return; + } + + my @args; + if (Param("mail_delivery_method") eq "sendmail") { + push @args, "-i"; + if ($from) { + push(@args, "-f$from"); + } + } + if (Param("mail_delivery_method") eq "sendmail" && !Param("sendmailnow")) { + push @args, "-ODeliveryMode=deferred"; + } + if (Param("mail_delivery_method") eq "smtp") { + push @args, Server => Param("smtpserver"); + if ($from) { + $ENV{'MAILADDRESS'} = $from; + } + } + my $mailer = new Mail::Mailer Param("mail_delivery_method"), @args; + if (Param("mail_delivery_method") eq "testfile") { + $Mail::Mailer::testfile::config{outfile} = "$datadir/mailer.testfile"; + } + + $mailer->open($headers->header_hashref); + print $mailer $body; + $mailer->close; +} + +sub encode_message { + my ($msg) = @_; + + my $parser = MIME::Parser->new; + $parser->output_to_core(1); + $parser->tmp_to_core(1); + my $entity = $parser->parse_data($msg); + $entity = encode_message_entity($entity); + + my @header_lines = split(/\n/, $entity->header_as_string); + my $head = new Mail::Header \@header_lines, Modify => 0; + + my $body = $entity->body_as_string; + + return ($head, $body); +} + +sub encode_message_entity { + my ($entity) = @_; + + my $head = $entity->head; + + # encode the subject + + my $subject = $head->get('subject'); + if (defined $subject && !is_7bit_clean($subject)) { + $subject =~ s/[\r\n]+$//; + $head->replace('subject', encode_qp_words($subject)); + } + + # encode addresses + + foreach my $field (qw(from to cc reply-to sender errors-to)) { + my $high = $head->count($field) - 1; + foreach my $index (0..$high) { + my $value = $head->get($field, $index); + my @addresses; + my $changed = 0; + foreach my $addr (Mail::Address->parse($value)) { + my $phrase = $addr->phrase; + if (is_7bit_clean($phrase)) { + push @addresses, $addr->format; + } else { + push @addresses, encode_qp_phrase($phrase) . + ' <' . $addr->address . '>'; + $changed = 1; + } + } + $changed && $head->replace($field, join(', ', @addresses), $index); + } + } + + # process the body + + if (scalar($entity->parts)) { + my $newparts = []; + foreach my $part ($entity->parts) { + my $newpart = encode_message_entity($part); + push @$newparts, $newpart; + } + $entity->parts($newparts); + } + else { + # Extract the body from the entity, for examination + # At this point, we can rely on MIME::Tools to do our encoding for us! + my $bodyhandle = $entity->bodyhandle; + my $body = $bodyhandle->as_string; + if (!is_7bit_clean($body)) { + # count number of 7-bit chars, and use quoted-printable if more + # than half the message is 7-bit clean + my $count = ($body =~ tr/\x20-\x7E\x0A\x0D//); + if ($count > length($body) / 2) { + $head->mime_attr('Content-Transfer-Encoding' => 'quoted-printable'); + } else { + $head->mime_attr('Content-Transfer-Encoding' => 'base64'); + } + } + + # Set the content/type and charset of the part, if not set + $head->mime_attr('Content-Type' => 'text/plain') + unless defined $head->mime_attr('content-type'); + $head->mime_attr('Content-Type.charset' => 'UTF-8'); + } + + $head->fold(75); + return $entity; +} + +sub encode_qp_words { + my ($line) = (@_); + my @encoded; + foreach my $word (split / /, $line) { + if (!is_7bit_clean($word)) { + push @encoded, '=?UTF-8?Q?_' . encode_qp($word, '') . '?='; + } else { + push @encoded, $word; + } + } + return join(' ', @encoded); +} + +1; diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index cd03f9747..3cb4d6ffc 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -31,7 +31,7 @@ package Bugzilla::Token; use Bugzilla::Config; use Bugzilla::Error; -use Bugzilla::BugMail; +use Bugzilla::Mailer; use Bugzilla::Util; use Date::Format; @@ -76,7 +76,7 @@ sub IssueEmailChangeToken { $template->process("account/email/change-old.txt.tmpl", $vars, \$message) || ThrowTemplateError($template->error()); - Bugzilla::BugMail::MessageToMTA($message); + MessageToMTA($message); $vars->{'token'} = $newtoken; $vars->{'emailaddress'} = $new_email . Param('emailsuffix'); @@ -85,7 +85,7 @@ sub IssueEmailChangeToken { $template->process("account/email/change-new.txt.tmpl", $vars, \$message) || ThrowTemplateError($template->error()); - Bugzilla::BugMail::MessageToMTA($message); + MessageToMTA($message); } # Generates a random token, adds it to the tokens table, and sends it @@ -125,7 +125,7 @@ sub IssuePasswordToken { $vars, \$message) || ThrowTemplateError($template->error()); - Bugzilla::BugMail::MessageToMTA($message); + MessageToMTA($message); } sub IssueSessionToken { @@ -213,7 +213,7 @@ sub Cancel { $template->process("account/cancel-token.txt.tmpl", $vars, \$message) || ThrowTemplateError($template->error()); - Bugzilla::BugMail::MessageToMTA($message); + MessageToMTA($message); # Delete the token from the database. DeleteToken($token); diff --git a/editproducts.cgi b/editproducts.cgi index 942375688..8fc074ef2 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -39,7 +39,7 @@ require "globals.pl"; use Bugzilla::Bug; use Bugzilla::Series; use Bugzilla::Config qw(:DEFAULT $datadir); -use Bugzilla::BugMail; +use Bugzilla::Mailer; use Bugzilla::Product; use Bugzilla::Classification; use Bugzilla::Milestone; @@ -905,7 +905,7 @@ if ($action eq 'update') { "has changed;\nyou had too many votes " . "for a single bug."); foreach my $msg (@$msgs) { - Bugzilla::BugMail::MessageToMTA($msg); + MessageToMTA($msg); } my $name = DBID_to_name($who); @@ -958,7 +958,7 @@ if ($action eq 'update') { "too many\ntotal votes, so all " . "votes have been removed."); foreach my $msg (@$msgs) { - Bugzilla::BugMail::MessageToMTA($msg); + MessageToMTA($msg); } my $name = DBID_to_name($who); diff --git a/importxml.pl b/importxml.pl index 08be03e5a..c1cdc56b6 100755 --- a/importxml.pl +++ b/importxml.pl @@ -81,6 +81,7 @@ use Bugzilla::Milestone; use Bugzilla::FlagType; use Bugzilla::Config qw(:DEFAULT $datadir); use Bugzilla::BugMail; +use Bugzilla::Mailer; use Bugzilla::User; use Bugzilla::Util; use Bugzilla::Constants; @@ -153,7 +154,7 @@ sub MailMessage { $header .= "From: Bugzilla <$from>\n"; $header .= "Subject: $subject\n\n"; my $sendmessage = $header . $message . "\n"; - Bugzilla::BugMail::MessageToMTA($sendmessage); + MessageToMTA($sendmessage); } } @@ -1198,7 +1199,7 @@ Debug( "Reading xml", DEBUG_LEVEL ); local ($/); $xml = <>; -# If the email was encoded (BugMail::MessageToMTA() does it when using UTF-8), +# If the email was encoded (Mailer::MessageToMTA() does it when using UTF-8), # we have to decode it first, else the XML parsing will fail. my $parser = MIME::Parser->new; $parser->output_to_core(1); diff --git a/process_bug.cgi b/process_bug.cgi index 7c52254ee..9773dce6d 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -52,7 +52,7 @@ require "globals.pl"; use Bugzilla; use Bugzilla::Constants; use Bugzilla::Bug; -use Bugzilla::BugMail; +use Bugzilla::Mailer; use Bugzilla::User; use Bugzilla::Util; use Bugzilla::Field; @@ -743,7 +743,7 @@ if ($action eq Param('move-button-text')) { || ThrowTemplateError($template->error()); $msg .= "\n"; - Bugzilla::BugMail::MessageToMTA($msg); + MessageToMTA($msg); # End the response page. $template->process("bug/navigate.html.tmpl", $vars) @@ -2137,7 +2137,7 @@ foreach my $id (@idlist) { # Now is a good time to send email to voters. foreach my $msg (@$msgs) { - Bugzilla::BugMail::MessageToMTA($msg); + MessageToMTA($msg); } if ($duplicate) { diff --git a/relogin.cgi b/relogin.cgi index 0385b8e2f..418ed3555 100755 --- a/relogin.cgi +++ b/relogin.cgi @@ -28,7 +28,7 @@ use lib qw(.); require "globals.pl"; use Bugzilla; -use Bugzilla::BugMail; +use Bugzilla::Mailer; use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::Token; @@ -163,7 +163,7 @@ elsif ($action eq 'begin-sudo') { $template->process('email/sudo.txt.tmpl', { reason => $reason }, \$message); - Bugzilla::BugMail::MessageToMTA($message); + MessageToMTA($message); $vars->{'message'} = 'sudo_started'; $vars->{'target'} = $target_user->login; diff --git a/whine.pl b/whine.pl index 7e4ec5285..9f5a9acfe 100755 --- a/whine.pl +++ b/whine.pl @@ -33,7 +33,7 @@ use Bugzilla::Config qw(:DEFAULT $datadir); use Bugzilla::Constants; use Bugzilla::Search; use Bugzilla::User; -use Bugzilla::BugMail; +use Bugzilla::Mailer; use Bugzilla::Util; # create some handles that we'll need @@ -406,7 +406,7 @@ sub mail { $template->process("whine/multipart-mime.txt.tmpl", $args, \$msg) or die($template->error()); - Bugzilla::BugMail::MessageToMTA($msg); + MessageToMTA($msg); delete $args->{'boundary'}; delete $args->{'alternatives'}; diff --git a/whineatnews.pl b/whineatnews.pl index 62e6388ac..c3f0a7677 100755 --- a/whineatnews.pl +++ b/whineatnews.pl @@ -33,7 +33,7 @@ use lib '.'; require "globals.pl"; use Bugzilla; -use Bugzilla::BugMail; +use Bugzilla::Mailer; use Bugzilla::Util; # Whining is disabled if whinedays is zero @@ -83,7 +83,7 @@ foreach my $email (sort (keys %bugs)) { $msg .= " -> ${urlbase}show_bug.cgi?id=$i\n"; } - Bugzilla::BugMail::MessageToMTA($msg); + MessageToMTA($msg); print "$email " . join(" ", @{$bugs{$email}}) . "\n"; } -- cgit v1.2.3-24-g4f1b