Commit 4a8e3d64 authored by preed%sigkill.com's avatar preed%sigkill.com

Bug 124174 - make processmail a package (Bugzilla::BugMail), r=gerv, r=jth, a=justdave

parent 731b5775
#!/usr/bonsaitools/bin/perl -wT
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
......@@ -24,27 +23,23 @@
# Alan Raetz <al_raetz@yahoo.com>
# Jacob Steenhagen <jake@actex.net>
# Matthew Tuck <matty@chariot.net.au>
# Bradley Baetz <bbaetz@student.usyd.edu.au>
# J. Paul Reed <preed@sigkill.com>
use strict;
use lib ".";
require "globals.pl";
package Bugzilla::BugMail;
use RelationSet;
# Shut up misguided -w warnings about "used only once".
sub processmail_sillyness {
my $zz;
$zz = $::unconfirmedstate;
}
$| = 1;
umask(0);
# This code is really ugly. It was a commandline interface, then it was moved
# There are package-global variables which we rely on ProcessOneBug to clean
# up each time, and other sorts of fun.
# This really needs to be cleaned at some point.
my $nametoexclude = "";
my %nomail;
my $last_changed;
my @excludedAddresses = ();
......@@ -52,16 +47,33 @@ my @excludedAddresses = ();
my $enableSendMail = 1;
my %force;
@{$force{'QAcontact'}} = ();
@{$force{'Owner'}} = ();
@{$force{'Reporter'}} = ();
@{$force{'CClist'}} = ();
@{$force{'Voter'}} = ();
my %seen;
my @sentlist;
# I got sick of adding &:: to everything.
# However, 'Yuck!'
# I can't require, cause that pulls it in only once, so it won't then be
# in the global package, and these aren't modules, so I can't use globals.pl
# Remove this evilness once our stuff uses real packages.
sub AUTOLOAD {
no strict 'refs';
use vars qw($AUTOLOAD);
my $subName = $AUTOLOAD;
$subName =~ s/.*::/::/; # remove package name
*$AUTOLOAD = \&$subName;
goto &$AUTOLOAD;
}
# This is run when we load the package
if (open(NOMAIL, "<data/nomail")) {
while (<NOMAIL>) {
$nomail{trim($_)} = 1;
}
close(NOMAIL);
}
sub FormatTriple {
my ($a, $b, $c) = (@_);
$^A = "";
......@@ -82,9 +94,52 @@ END
; # This semicolon appeases my emacs editor macros. :-)
return $^A;
}
sub ProcessOneBug {
# This is a bit of a hack, basically keeping the old system()
# cmd line interface. Should clean this up at some point.
#
# args: bug_id, and an optional hash ref which may have keys for:
# changer, owner, qa, reporter, cc
# Optional hash contains values of people which will be forced to those
# roles when the email is sent.
# All the names are email addresses, not userids
# values are scalars, except for cc, which is a list
sub Send($;$) {
my ($id, $recipients) = (@_);
# This doesn't work if its not in a sub. Probably something to do with the
# require abuse we do.
GetVersionTable();
# Since any email recipients must be rederived if the user has not
# been rederived since the most recent group change, figure out when that
# is once and determine the need to rederive users using the same DB
# access that gets the user's email address each time a person is
# processed.
SendSQL("SELECT MAX(last_changed) FROM groups");
($last_changed) = FetchSQLData();
# Make sure to clean up _all_ package vars here. Yuck...
$nametoexclude = $recipients->{'changer'} || "";
@{$force{'CClist'}} = (exists $recipients->{'cc'} &&
scalar($recipients->{'cc'}) > 0) ? map(trim($_),
@{$recipients->{'cc'}}) : ();
@{$force{'Owner'}} = $recipients->{'owner'} ?
(trim($recipients->{'owner'})) : ();
@{$force{'QAcontact'}} = $recipients->{'qacontact'} ?
(trim($recipients->{'qacontact'})) : ();
@{$force{'Reporter'}} = $recipients->{'reporter'} ?
(trim($recipients->{'reporter'})) : ();
@{$force{'Voter'}} = ();
%seen = ();
@excludedAddresses = ();
@sentlist = ();
return ProcessOneBug($id);
}
sub ProcessOneBug($) {
my ($id) = (@_);
my @headerlist;
......@@ -356,19 +411,8 @@ sub ProcessOneBug {
# Filter the exclude list for dupes one last time
@excludedAddresses = filterExcludeList(\@excludedAddresses,
\@sentlist);
if (@sentlist) {
print "<b>Email sent to:</b> " . join(", ", @sentlist) ."<br>\n";
} else {
print "<b>Email sent to:</b> no one<br>\n";
}
if (@excludedAddresses) {
print "<b>Excluding:</b> " . join(", ", @excludedAddresses) . "\n";
}
print "<br><center>If you wish to tweak the kinds of mail Bugzilla sends you, you can";
print " <a href=\"userprefs.cgi?tab=email\">change your preferences</a></center>\n";
return { sent => \@sentlist, excluded => \@excludedAddresses };
}
# When one person is in different fields on one bug, they may be
......@@ -480,7 +524,7 @@ sub getEmailAttributes (\%\@$) {
# effect filtering later and we're already in the loop.
if ($fieldName eq 'AssignedTo') {
push (@{$force{'Owner'}}, $new);
} elsif ($fieldName eq 'QAContact') {
} elsif ($fieldName eq 'QAcontact') {
push (@{$force{'QAcontact'}}, $new);
} elsif ($fieldName eq 'CC') {
my @added = split (/[ ,]/, $new);
......@@ -667,8 +711,8 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) {
}
SendSQL("SELECT userid, (refreshed_when > " . SqlQuote($::last_changed) . ") " .
"FROM profiles WHERE login_name = " . SqlQuote($person));
SendSQL("SELECT userid, (refreshed_when > " . SqlQuote($last_changed) .
") FROM profiles WHERE login_name = " . SqlQuote($person));
my ($userid, $current) = (FetchSQLData());
$seen{$person} = 1;
......@@ -822,107 +866,14 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) {
}
if ($enableSendMail == 1) {
open(SENDMAIL, "|/usr/lib/sendmail $sendmailparam -t -i") ||
die "Can't open sendmail";
open(SENDMAIL, "|/usr/lib/sendmail $sendmailparam -t -i") ||
die "Can't open sendmail";
print SENDMAIL trim($msg) . "\n";
close SENDMAIL;
print SENDMAIL trim($msg) . "\n";
close SENDMAIL;
}
push(@sentlist, $person);
return 1;
}
# Code starts here
ConnectToDatabase();
GetVersionTable();
if (open(FID, "<data/nomail")) {
while (<FID>) {
$nomail{trim($_)} = 1;
}
close FID;
}
# Since any email recipients must be rederived if the user has not
# been rederived since the most recent group change, figure out when that
# is once and determine the need to rederive users using the same DB access
# that gets the user's email address each time a person is processed.
#
SendSQL("SELECT MAX(last_changed) FROM groups");
($::last_changed) = FetchSQLData();
if ($#ARGV >= 0 && $ARGV[0] eq "regenerate") {
print "Regenerating is no longer required or supported\n";
exit;
}
if ($#ARGV >= 0 && $ARGV[0] eq "-forcecc") {
shift(@ARGV);
foreach my $i (split(/,/, shift(@ARGV))) {
push(@{$force{'CClist'}}, trim($i));
}
}
if ($#ARGV >= 0 && $ARGV[0] eq "-forceowner") {
shift(@ARGV);
@{$force{'Owner'}} = (trim(shift(@ARGV)));
}
if ($#ARGV >= 0 && $ARGV[0] eq "-forceqacontact") {
shift(@ARGV);
@{$force{'QAcontact'}} = (trim(shift(@ARGV)));
}
if ($#ARGV >= 0 && $ARGV[0] eq "-forcereporter") {
shift(@ARGV);
@{$force{'Reporter'}} = trim(shift(@ARGV));
}
if (($#ARGV < 0) || ($#ARGV > 1)) {
print "Usage:\n processmail {bugid} {nametoexclude} " .
"[-forcecc list,of,users]\n [-forceowner name] " .
"[-forceqacontact name]\n [-forcereporter name]\nor\n" .
" processmail rescanall\n";
exit;
}
if ($#ARGV == 1) {
$nametoexclude = lc($ARGV[1]);
}
if ($ARGV[0] eq "rescanall") {
print "Collecting bug ids...\n";
SendSQL("select bug_id, lastdiffed, delta_ts from bugs where lastdiffed < delta_ts AND delta_ts < date_sub(now(), INTERVAL 30 minute) order by bug_id");
my @list;
while (my @row = FetchSQLData()) {
my $time = $row[2];
if ($time =~ /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) {
$time = "$1-$2-$3 $4:$5:$6";
}
print STDERR "Bug $row[0] has unsent mail. lastdiffed is $row[1], delta_ts is $time.\n";
push @list, $row[0];
}
if (scalar(@list) > 0) {
print STDERR scalar(@list) . " bugs found with possibly unsent mail\n";
print STDERR "Updating bugs, sending mail if required\n";
} else {
print "All appropriate mail appears to have been sent\n"
}
foreach my $id (@list) {
if (detaint_natural($id)) {
ProcessOneBug($id);
}
}
} else {
my $bugnum;
if ($ARGV[0] =~ m/^([1-9][0-9]*)$/) {
$bugnum = $1;
} else {
print "Error calling processmail (bug id is not an integer)<br>\n";
exit;
}
ProcessOneBug($bugnum);
}
exit;
1;
......@@ -259,6 +259,13 @@ sub create {
# UserInGroup - you probably want to cache this
'UserInGroup' => \&::UserInGroup,
# SendBugMail - sends mail about a bug, using Bugzilla::BugMail.pm
'SendBugMail' => sub {
my ($id, $mailrecipients) = (@_);
require Bugzilla::BugMail;
Bugzilla::BugMail::Send($id, $mailrecipients);
},
# SyncAnyPendingShadowChanges
# - called in the footer to sync the shadowdb
'SyncAnyPendingShadowChanges' => \&::SyncAnyPendingShadowChanges,
......
......@@ -755,10 +755,7 @@ sub CheckIfVotedConfirmed {
$vars->{'type'} = "votes";
$vars->{'id'} = $id;
$vars->{'mail'} = "";
open(PMAIL, "-|") or exec('./processmail', $id);
$vars->{'mail'} .= $_ while <PMAIL>;
close(PMAIL);
$vars->{'mailrecipients'} = { 'changer' => $who };
$template->process("bug/process/results.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
......
......@@ -574,23 +574,11 @@ sub insert
}
}
# Send mail to let people know the attachment has been created. Uses a
# special syntax of the "open" and "exec" commands to capture the output of
# "processmail", which "system" doesn't allow, without running the command
# through a shell, which backticks (``) do.
#system ("./processmail", $bugid , $::userid);
#my $mailresults = `./processmail $bugid $::userid`;
my $mailresults = '';
open(PMAIL, "-|") or exec('./processmail', '-forcecc', $forcecc,
$::FORM{'bugid'}, $::COOKIE{'Bugzilla_login'});
$mailresults .= $_ while <PMAIL>;
close(PMAIL);
# Define the variables and functions that will be passed to the UI template.
$vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'} };
$vars->{'bugid'} = $::FORM{'bugid'};
$vars->{'attachid'} = $attachid;
$vars->{'description'} = $description;
$vars->{'mailresults'} = $mailresults;
$vars->{'contenttypemethod'} = $::FORM{'contenttypemethod'};
$vars->{'contenttype'} = $::FORM{'contenttype'};
......@@ -791,21 +779,10 @@ sub update
}
# Send mail to let people know the bug has changed. Uses a special syntax
# of the "open" and "exec" commands to capture the output of "processmail",
# which "system" doesn't allow, without running the command through a shell,
# which backticks (``) do.
#system ("./processmail", $bugid , $::userid);
#my $mailresults = `./processmail $bugid $::userid`;
my $mailresults = '';
open(PMAIL, "-|") or exec('./processmail', $bugid, DBID_to_name($::userid));
$mailresults .= $_ while <PMAIL>;
close(PMAIL);
# Define the variables and functions that will be passed to the UI template.
$vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'} };
$vars->{'attachid'} = $::FORM{'id'};
$vars->{'bugid'} = $bugid;
$vars->{'mailresults'} = $mailresults;
# Return the appropriate HTTP response headers.
print "Content-Type: text/html\n\n";
......
......@@ -457,30 +457,16 @@ if (UserInGroup("editbugs")) {
}
}
# Assemble the -force* strings so this counts as "Added to this capacity"
my @ARGLIST = ();
if (@cc) {
push (@ARGLIST, "-forcecc", join(",", @cc));
}
push (@ARGLIST, "-forceowner", DBID_to_name($::FORM{assigned_to}));
# Email everyone the details of the new bug
$vars->{'mailrecipients'} = { 'cc' => \@cc,
'owner' => DBID_to_name($::FORM{'assigned_to'}),
'reporter' => $::COOKIE{'Bugzilla_login'},
'changer' => $::COOKIE{'Bugzilla_login'} };
if (defined $::FORM{'qa_contact'}) {
push (@ARGLIST, "-forceqacontact", DBID_to_name($::FORM{'qa_contact'}));
$vars->{'mailrecipients'}->{'qa'} = DBID_to_name($::FORM{'qa_contact'});
}
push (@ARGLIST, "-forcereporter", DBID_to_name($::userid));
push (@ARGLIST, $id, $::COOKIE{'Bugzilla_login'});
# Send mail to let people know the bug has been created.
# See attachment.cgi for explanation of why it's done this way.
my $mailresults = '';
open(PMAIL, "-|") or exec('./processmail', @ARGLIST);
$mailresults .= $_ while <PMAIL>;
close(PMAIL);
# Tell the user all about it
$vars->{'id'} = $id;
my $bug = new Bug($id, $::userid);
$vars->{'bug'} = $bug;
......@@ -491,19 +477,10 @@ $vars->{'sentmail'} = [];
push (@{$vars->{'sentmail'}}, { type => 'created',
id => $id,
mail => $mailresults
});
foreach my $i (@all_deps) {
my $mail = "";
open(PMAIL, "-|") or exec('./processmail', $i, $::COOKIE{'Bugzilla_login'});
$mail .= $_ while <PMAIL>;
close(PMAIL);
push (@{$vars->{'sentmail'}}, { type => 'dep',
id => $i,
mail => $mail
});
push (@{$vars->{'sentmail'}}, { type => 'dep', id => $i, });
}
my @bug_list;
......
......@@ -1360,8 +1360,8 @@ foreach my $id (@idlist) {
LogActivityEntry($id, "bug_group", $groupDelNames, $groupAddNames,
$whoid, $timestamp);
$bug_changed = 1;
my $removedCcString = "";
my @ccRemoved = ();
if (defined $::FORM{newcc} || defined $::FORM{removecc} || defined $::FORM{masscc}) {
# Get the current CC list for this bug
my %oncc;
......@@ -1387,8 +1387,6 @@ foreach my $id (@idlist) {
$oncc{$pid} = 0;
}
}
# Save off the removedCcString so it can be fed to processmail
$removedCcString = join (",", @removed);
# If any changes were found, record it in the activity log
if (scalar(@removed) || scalar(@added)) {
......@@ -1397,6 +1395,7 @@ foreach my $id (@idlist) {
LogActivityEntry($id,"cc",$removed,$added,$whoid,$timestamp);
$bug_changed = 1;
}
@ccRemoved = @removed;
}
# We need to run processmail for dependson/blocked bugs if the dependencies
......@@ -1660,28 +1659,10 @@ foreach my $id (@idlist) {
}
SendSQL("UNLOCK TABLES");
my @ARGLIST = ();
if ( $removedCcString ne "" ) {
push @ARGLIST, ("-forcecc", $removedCcString);
}
if ( $origOwner ne "" ) {
push @ARGLIST, ("-forceowner", $origOwner);
}
if ( $origQaContact ne "") {
push @ARGLIST, ( "-forceqacontact", $origQaContact);
}
push @ARGLIST, ($id, $::COOKIE{'Bugzilla_login'});
# Send mail to let people know the bug has been changed. Uses
# a special syntax of the "open" and "exec" commands to capture
# the output "processmail", which "system" doesn't allow
# (i.e. "system ('./processmail', $bugid , $::userid);"), without
# the insecurity of running the command through a shell via backticks
# (i.e. "my $mailresults = `./processmail $bugid $::userid`;").
$vars->{'mail'} = "";
open(PMAIL, "-|") or exec('./processmail', @ARGLIST);
$vars->{'mail'} .= $_ while <PMAIL>;
close(PMAIL);
$vars->{'mailrecipients'} = { 'cc' => \@ccRemoved,
'owner' => $origOwner,
'qa' => $origQaContact,
'changer' => $::COOKIE{'Bugzilla_login'} };
$vars->{'id'} = $id;
......@@ -1710,11 +1691,9 @@ foreach my $id (@idlist) {
CheckFormFieldDefined(\%::FORM,'comment');
SendSQL("INSERT INTO duplicates VALUES ($duplicate, $::FORM{'id'})");
$vars->{'mail'} = "";
open(PMAIL, "-|") or exec('./processmail', $duplicate, $::COOKIE{'Bugzilla_login'});
$vars->{'mail'} .= $_ while <PMAIL>;
close(PMAIL);
$vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'}
};
$vars->{'id'} = $duplicate;
$vars->{'type'} = "dupe";
......@@ -1725,12 +1704,7 @@ foreach my $id (@idlist) {
if ($check_dep_bugs) {
foreach my $k (keys(%dependencychanged)) {
$vars->{'mail'} = "";
open(PMAIL, "-|")
or exec('./processmail', $k, $::COOKIE{'Bugzilla_login'});
$vars->{'mail'} .= $_ while <PMAIL>;
close(PMAIL);
$vars->{'mailrecipients'} = { 'changer' => $::COOKIE{'Bugzilla_login'} };
$vars->{'id'} = $k;
$vars->{'type'} = "dep";
......
......@@ -150,6 +150,31 @@ if (exists $::FORM{'cleangroupsnow'}) {
"- reduced from $before records to $after records");
}
if (exists $::FORM{'rescanallBugMail'}) {
require Bugzilla::BugMail;
Status("OK, now attempting to send unsent mail");
SendSQL("SELECT bug_id FROM bugs WHERE lastdiffed < delta_ts AND
delta_ts < date_sub(now(), INTERVAL 30 minute) ORDER BY bug_id");
my @list;
while (MoreSQLData()) {
push (@list, FetchOneColumn());
}
Status(scalar(@list) . ' bugs found with possibly unsent mail.');
foreach my $bugid (@list) {
Bugzilla::BugMail::Send($bugid);
}
if (scalar(@list) > 0) {
Status("Unsent mail has been sent.");
}
PutFooter();
exit;
}
print "OK, now running sanity checks.<p>\n";
###########################################################################
......@@ -683,7 +708,7 @@ while (@row = FetchSQLData()) {
if (@badbugs > 0) {
Alert("Bugs that have changes but no mail sent for at least half an hour: " .
join (", ", @badbugs));
print("Run <code>processmail rescanall</code> to fix this<p>\n");
print qq{<a href="sanitycheck.cgi?rescanallBugMail=1">Send these mails</a>.<p>\n};
}
###########################################################################
......
......@@ -26,7 +26,6 @@
# contenttype: string. The Content Type we attached it as.
# contenttypemethod: string. How we got the content type of the attachment.
# Possible values: autodetect, list, manual.
# mailresults: string. who was mailed, and who wasn't.
#%]
[% PROCESS global/header.html.tmpl
......@@ -42,7 +41,7 @@
to <a href="show_bug.cgi?id=[% bugid %]">Bug #[% bugid %]</a> Created
</h2>
[% mailresults %]
[% PROCESS "bug/process/bugmail.html.tmpl" mailing_bugid = bugid %]
[% IF contenttypemethod == 'autodetect' %]
<p>
......
......@@ -23,7 +23,6 @@
[%# INTERFACE:
# bugid: integer. ID of the bug we are updating.
# attachid: integer. ID of the attachment we just attached.
# mailresults: string. Who was mailed and who wasn't.
#%]
[% PROCESS global/header.html.tmpl
......@@ -40,7 +39,7 @@
<a href="attachment.cgi?id=[% attachid %]&amp;action=edit">attachment [% attachid %]</a>
of bug [% bugid %] submitted
</h2>
[% mailresults %]
[% PROCESS "bug/process/bugmail.html.tmpl" mailing_bugid = bugid %]
</td>
<td>
......
<!-- 1.0@bugzilla.org -->
[%# 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): Bradley Baetz <bbaetz@student.usyd.edu.au>
# J. Paul Reed <preed@sigkill.com>
#%]
[%# INTERFACE:
# mailing_bugid: string. ID of the bug this mail is concerning.
# mailrecipients: hash. People involved in this change. Hash has up to five
# elements:
# changer: string. The login name of the user who made the
# change.
#
# For bug changes where people need to be notified:
# owner: string. The login name of the bug assignee.
# reporter: string. The login name of the bug reporter.
# qacontact: string. The login name of the bug's QA contact.
# Optional.
# cc: list of strings. The login names of those on the CC
# list.
#%]
[% mail = SendBugMail(mailing_bugid, mailrecipients) %]
[% PROCESS emails
description = "Email sent to"
names = mail.sent
%]
<br>
[% PROCESS emails
description = "Excluding"
names = mail.excluded
%]
<br>
<center>
If you wish to tweak the kinds of mail Bugzilla sends you, you can
<a href="userprefs.cgi?tab=email">change your preferences</a>.
</center>
[%############################################################################%]
[%# Block for a set of email addresses #%]
[%############################################################################%]
[% BLOCK emails %]
<b>[% description %]:</b>
[% IF names.size > 0 %]
[%+ FOREACH name = names %]
[% name %][% ", " UNLESS loop.last() %]
[% END %]
[% ELSE %]
no one
[% END %]
[% END %]
......@@ -25,6 +25,8 @@
# type: string; the type of change/check that was made: "bug" when a bug
# is changed, "dupe" when a duplication notation is added to a bug,
# and "dep" when a bug is checked for changes to its dependencies.
#
# mailrecipients: hash; BugMail recipient params. Optional.
#%]
[% DEFAULT type="bug" %]
......@@ -43,7 +45,7 @@
<tr>
<td>
<h2>[% title.$type %]</h2>
[% mail %]
[% PROCESS "bug/process/bugmail.html.tmpl" mailing_bugid = id %]
</td>
<td>
<a href="show_bug.cgi?id=[% id %]">Back To BUG# [% id %]</a>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment