mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
395 lines
11 KiB
Perl
Executable File
395 lines
11 KiB
Perl
Executable File
#!/usr/bonsaitools/bin/perl -w
|
|
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
|
#
|
|
# The contents of this file are subject to the Mozilla Public License
|
|
# Version 1.0 (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 <terry@mozilla.org>,
|
|
# Bryce Nesbitt <bryce-mozilla@nextbus.com>
|
|
|
|
# To recreate the shadow database, run "processmail regenerate" .
|
|
|
|
use diagnostics;
|
|
use strict;
|
|
|
|
require "globals.pl";
|
|
|
|
$| = 1;
|
|
|
|
umask(0);
|
|
|
|
$::lockcount = 0;
|
|
my $regenerate = 0;
|
|
my $nametoexclude = "";
|
|
|
|
sub Lock {
|
|
if ($::lockcount <= 0) {
|
|
$::lockcount = 0;
|
|
if (!open(LOCKFID, ">>data/maillock")) {
|
|
mkdir "data", 0777;
|
|
chmod 0777, "data";
|
|
open(LOCKFID, ">>data/maillock") || die "Can't open lockfile.";
|
|
}
|
|
my $val = flock(LOCKFID,2);
|
|
if (!$val) { # '2' is magic 'exclusive lock' const.
|
|
print "Lock failed: $val\n";
|
|
}
|
|
chmod 0666, "data/maillock";
|
|
}
|
|
$::lockcount++;
|
|
}
|
|
|
|
sub Unlock {
|
|
$::lockcount--;
|
|
if ($::lockcount <= 0) {
|
|
flock(LOCKFID,8); # '8' is magic 'unlock' const.
|
|
close LOCKFID;
|
|
}
|
|
}
|
|
|
|
sub FileSize {
|
|
my ($filename) = (@_);
|
|
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
|
|
$atime,$mtime,$ctime,$blksize,$blocks)
|
|
= stat($filename);
|
|
if (defined $size) {
|
|
return $size;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
sub Different {
|
|
my ($file1, $file2) = (@_);
|
|
my $size1 = FileSize($file1);
|
|
my $size2 = FileSize($file2);
|
|
if ($size1 != $size2) {
|
|
return 1;
|
|
}
|
|
open(FID1, "<$file1") || die "Can't open $file1";
|
|
open(FID2, "<$file2") || die "Can't open $file2";
|
|
my $d1;
|
|
my $d2;
|
|
if (read(FID1, $d1, $size1) ne $size1) {
|
|
die "Can't read $size1 bytes from $file1";
|
|
}
|
|
if (read(FID2, $d2, $size2) ne $size2) {
|
|
die "Can't read $size2 bytes from $file2";
|
|
}
|
|
close FID1;
|
|
close FID2;
|
|
return ($d1 ne $d2);
|
|
}
|
|
|
|
|
|
sub DescCC {
|
|
my ($cclist) = (@_);
|
|
if (scalar(@$cclist) <= 0) {
|
|
return "";
|
|
}
|
|
return "Cc: " . join(", ", @$cclist) . "\n";
|
|
}
|
|
|
|
|
|
sub DescDependencies {
|
|
my ($id) = (@_);
|
|
if (!Param("usedependencies")) {
|
|
return "";
|
|
}
|
|
my $result = "";
|
|
my $me = "blocked";
|
|
my $target = "dependson";
|
|
my $title = "BugsThisDependsOn";
|
|
for (1..2) {
|
|
SendSQL("select $target from dependencies where $me = $id order by $target");
|
|
my @list;
|
|
while (MoreSQLData()) {
|
|
push(@list, FetchOneColumn());
|
|
}
|
|
if (@list) {
|
|
my @verbose;
|
|
foreach my $i (@list) {
|
|
SendSQL("select bug_status, resolution from bugs where bug_id = $i");
|
|
my ($bug_status, $resolution) = (FetchSQLData());
|
|
if ($resolution ne "") {
|
|
$resolution = "/$resolution";
|
|
}
|
|
push(@verbose, $i . "[$bug_status$resolution]");
|
|
}
|
|
$result .= "$title: " . join(', ', @verbose) . "\n";
|
|
}
|
|
my $tmp = $me;
|
|
$me = $target;
|
|
$target = $tmp;
|
|
$title = "OtherBugsDependingOnThis";
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
|
|
|
|
sub GetBugText {
|
|
my ($id) = (@_);
|
|
undef %::bug;
|
|
|
|
my @collist = ("bug_id", "product", "version", "rep_platform", "op_sys",
|
|
"bug_status", "resolution", "priority", "bug_severity",
|
|
"assigned_to", "reporter", "bug_file_loc",
|
|
"short_desc", "component", "qa_contact", "target_milestone",
|
|
"status_whiteboard", "groupset");
|
|
|
|
my $query = "select " . join(", ", @collist) .
|
|
" from bugs where bug_id = $id";
|
|
|
|
SendSQL($query);
|
|
|
|
my @row;
|
|
if (!(@row = FetchSQLData())) {
|
|
return "";
|
|
}
|
|
foreach my $field (@collist) {
|
|
$::bug{$field} = shift @row;
|
|
if (!defined $::bug{$field}) {
|
|
$::bug{$field} = "";
|
|
}
|
|
}
|
|
|
|
$::bug{'assigned_to'} = DBID_to_name($::bug{'assigned_to'});
|
|
$::bug{'reporter'} = DBID_to_name($::bug{'reporter'});
|
|
my $qa_contact = "";
|
|
my $target_milestone = "";
|
|
my $status_whiteboard = "";
|
|
if (Param('useqacontact') && $::bug{'qa_contact'} > 0) {
|
|
$::bug{'qa_contact'} = DBID_to_name($::bug{'qa_contact'});
|
|
$qa_contact = "QAContact: $::bug{'qa_contact'}\n";
|
|
} else {
|
|
$::bug{'qa_contact'} = "";
|
|
}
|
|
if (Param('usetargetmilestone') && $::bug{'target_milestone'} ne "") {
|
|
$target_milestone = "TargetMilestone: $::bug{'target_milestone'}\n";
|
|
}
|
|
if (Param('usestatuswhiteboard') && $::bug{'status_whiteboard'} ne "") {
|
|
$status_whiteboard = "StatusWhiteboard: $::bug{'status_whiteboard'}\n";
|
|
}
|
|
|
|
$::bug{'long_desc'} = GetLongDescription($id);
|
|
|
|
my @cclist;
|
|
@cclist = split(/,/, ShowCcList($id));
|
|
my @voterlist;
|
|
SendSQL("select profiles.login_name from votes, profiles where votes.bug_id = $id and profiles.userid = votes.who");
|
|
while (MoreSQLData()) {
|
|
my $v = FetchOneColumn();
|
|
push(@voterlist, $v);
|
|
}
|
|
$::bug{'cclist'} = join(',', @cclist);
|
|
$::bug{'voterlist'} = join(',', @voterlist);
|
|
|
|
|
|
return "Bug\#: $id
|
|
Product: $::bug{'product'}
|
|
Version: $::bug{'version'}
|
|
Platform: $::bug{'rep_platform'}
|
|
OS/Version: $::bug{'op_sys'}
|
|
Status: $::bug{'bug_status'}
|
|
Resolution: $::bug{'resolution'}
|
|
Severity: $::bug{'bug_severity'}
|
|
Priority: $::bug{'priority'}
|
|
Component: $::bug{'component'}
|
|
AssignedTo: $::bug{'assigned_to'}
|
|
ReportedBy: $::bug{'reporter'}
|
|
$qa_contact$target_milestone${status_whiteboard}URL: $::bug{'bug_file_loc'}
|
|
" . DescCC(\@cclist) . "Summary: $::bug{'short_desc'}
|
|
" . DescDependencies($id) . "
|
|
$::bug{'long_desc'}
|
|
";
|
|
|
|
}
|
|
|
|
|
|
my $didexclude = 0;
|
|
my %seen;
|
|
sub fixaddresses {
|
|
my ($field, $list) = (@_);
|
|
my @result;
|
|
foreach my $i (@$list) {
|
|
if (!defined $i || $i eq "") {
|
|
next;
|
|
}
|
|
SendSQL("select emailnotification, groupset & $::bug{'groupset'} from profiles where login_name = " .
|
|
SqlQuote($i));
|
|
my ($emailnotification, $groupset) = (FetchSQLData());
|
|
if ($groupset ne $::bug{'groupset'}) {
|
|
next;
|
|
}
|
|
if ($emailnotification eq "CConly") {
|
|
if ($field ne "cc") {
|
|
next;
|
|
}
|
|
}
|
|
if ($emailnotification eq "ExcludeSelfChanges" &&
|
|
(lc($i) eq $nametoexclude)) {
|
|
$didexclude = 1;
|
|
next;
|
|
}
|
|
|
|
if (!defined $::nomail{$i} && !defined $seen{$i}) {
|
|
push(@result, $i . Param('emailsuffix'));
|
|
$seen{$i} = 1;
|
|
}
|
|
}
|
|
return join(", ", @result);
|
|
}
|
|
|
|
|
|
sub Log {
|
|
my ($str) = (@_);
|
|
Lock();
|
|
open(FID, ">>data/maillog") || die "Can't write to data/maillog";
|
|
print FID time2str("%D %H:%M", time()) . ": $str\n";
|
|
close FID;
|
|
Unlock();
|
|
}
|
|
|
|
sub ProcessOneBug {
|
|
my $i = $_[0];
|
|
my $old = "shadow/$i";
|
|
my $new = "shadow/$i.tmp.$$";
|
|
my $diffs = "shadow/$i.diffs.$$";
|
|
my $verb = "Changed";
|
|
if (!stat($old)) {
|
|
mkdir "shadow", 0777;
|
|
chmod 0777, "shadow";
|
|
open(OLD, ">$old") || die "Couldn't create null $old";
|
|
close OLD;
|
|
$verb = "New";
|
|
}
|
|
my $text = GetBugText($i);
|
|
if ($text eq "") {
|
|
die "Couldn't find bug $i.";
|
|
}
|
|
open(FID, ">$new") || die "Couldn't create $new";
|
|
print FID $text;
|
|
close FID;
|
|
if (Different($old, $new)) {
|
|
system("diff -c -b $old $new > $diffs");
|
|
my $tolist = fixaddresses("to",
|
|
[$::bug{'assigned_to'}, $::bug{'reporter'},
|
|
$::bug{'qa_contact'}]);
|
|
my @combinedcc;
|
|
foreach my $v (split(/,/, "$::bug{'cclist'},$::bug{'voterlist'}")) {
|
|
push @combinedcc, $v;
|
|
}
|
|
my $cclist = fixaddresses("cc", \@combinedcc);
|
|
my $logstr = "Bug $i $verb";
|
|
if ($tolist ne "" || $cclist ne "") {
|
|
my %substs;
|
|
|
|
$substs{"to"} = $tolist;
|
|
$substs{"cc"} = $cclist;
|
|
$substs{"bugid"} = $i;
|
|
$substs{"diffs"} = "";
|
|
open(DIFFS, "<$diffs") || die "Can't open $diffs";
|
|
while (<DIFFS>) {
|
|
$substs{"diffs"} .= $_;
|
|
}
|
|
close DIFFS;
|
|
$substs{"neworchanged"} = $verb;
|
|
$substs{"summary"} = $::bug{'short_desc'};
|
|
my $msg = PerformSubsts(Param("changedmail"), \%substs);
|
|
|
|
if (!$regenerate) {
|
|
# Note: fixaddresses may result in a Cc: only. This seems harmless.
|
|
open(SENDMAIL, "|/usr/lib/sendmail -t") ||
|
|
die "Can't open sendmail";
|
|
print SENDMAIL $msg;
|
|
close SENDMAIL;
|
|
$logstr = "$logstr; mail sent to $tolist, $cclist";
|
|
print "<B>Email sent to:</B> $tolist $cclist\n";
|
|
if ($didexclude) {
|
|
print "<B>Excluding:</B> $nametoexclude (<a href=changepassword.cgi>change your preferences</a> if you wish not to be excluded)\n";
|
|
}
|
|
}
|
|
}
|
|
unlink($diffs);
|
|
Log($logstr);
|
|
}
|
|
rename($new, $old) || die "Can't rename $new to $old";
|
|
chmod 0666, $old;
|
|
if ($regenerate) {
|
|
print "$i ";
|
|
}
|
|
%seen = ();
|
|
}
|
|
|
|
# Code starts here
|
|
|
|
ConnectToDatabase();
|
|
Lock();
|
|
|
|
if (open(FID, "<data/nomail")) {
|
|
while (<FID>) {
|
|
$::nomail{trim($_)} = 1;
|
|
}
|
|
close FID;
|
|
}
|
|
|
|
if (($#ARGV < 0) || ($#ARGV > 1)) {
|
|
print "Usage error: processmail {bugid} {nametoexclude}\nOr: processmail regenerate\n";
|
|
exit;
|
|
}
|
|
|
|
# To recreate the shadow database, run "processmail regenerate" .
|
|
if ($ARGV[0] eq "regenerate") {
|
|
$regenerate = 1;
|
|
shift @ARGV;
|
|
SendSQL("select bug_id from bugs order by bug_id");
|
|
my @regenerate_list;
|
|
while (my @row = FetchSQLData()) {
|
|
push @regenerate_list, $row[0];
|
|
}
|
|
foreach my $i (@regenerate_list) {
|
|
ProcessOneBug($i);
|
|
Unlock();
|
|
Lock();
|
|
}
|
|
print("\n");
|
|
exit;
|
|
}
|
|
|
|
if ($#ARGV == 1) {
|
|
$nametoexclude = lc($ARGV[1]);
|
|
}
|
|
|
|
if ($ARGV[0] eq "rescanall") {
|
|
print "<br> Collecting bug ids...\n";
|
|
SendSQL("select bug_id from bugs where to_days(now()) - to_days(delta_ts) <= 2 order by bug_id");
|
|
my @list;
|
|
while (my @row = FetchSQLData()) {
|
|
push @list, $row[0];
|
|
}
|
|
foreach my $id (@list) {
|
|
$ARGV[0] = $id;
|
|
print "<br> Doing bug $id\n";
|
|
ProcessOneBug($ARGV[0]);
|
|
}
|
|
} else {
|
|
ProcessOneBug($ARGV[0]);
|
|
}
|
|
|
|
exit;
|