it appeared as if you were going to edit the sanity checks if you clicked on it, when in fact you were going to run them. This patch rewords the links so Sanity check is in a group by itself in the block of links on the right. Patch by Matthew Tuck <matty@chariot.net.au> r= justdave x2 git-svn-id: svn://10.0.0.236/trunk@102371 18797224-902f-48f8-a5cc-f745e15eee43
1394 lines
48 KiB
Perl
1394 lines
48 KiB
Perl
# -*- 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 <terry@mozilla.org>
|
|
# Dan Mosedale <dmose@mozilla.org>
|
|
# Joe Robins <jmrobins@tgix.com>
|
|
# Dave Miller <justdave@syndicomm.com>
|
|
# Christopher Aillon <christopher@aillon.com>
|
|
|
|
# Contains some global routines used throughout the CGI scripts of Bugzilla.
|
|
|
|
use diagnostics;
|
|
use strict;
|
|
# use Carp; # for confess
|
|
# Shut up misguided -w warnings about "used only once". For some reason,
|
|
# "use vars" chokes on me when I try it here.
|
|
|
|
# commented out the following snippet of code. this tosses errors into the
|
|
# CGI if you are perl 5.6, and doesn't if you have perl 5.003.
|
|
# We want to check for the existence of the LDAP modules here.
|
|
# eval "use Mozilla::LDAP::Conn";
|
|
# my $have_ldap = $@ ? 0 : 1;
|
|
|
|
|
|
sub CGI_pl_sillyness {
|
|
my $zz;
|
|
$zz = %::FILENAME;
|
|
$zz = %::MFORM;
|
|
$zz = %::dontchange;
|
|
}
|
|
|
|
use CGI::Carp qw(fatalsToBrowser);
|
|
|
|
require 'globals.pl';
|
|
|
|
sub GeneratePersonInput {
|
|
my ($field, $required, $def_value, $extraJavaScript) = (@_);
|
|
$extraJavaScript ||= "";
|
|
if ($extraJavaScript ne "") {
|
|
$extraJavaScript = "onChange=\"$extraJavaScript\"";
|
|
}
|
|
return "<INPUT NAME=\"$field\" SIZE=32 $extraJavaScript VALUE=\"$def_value\">";
|
|
}
|
|
|
|
sub GeneratePeopleInput {
|
|
my ($field, $def_value) = (@_);
|
|
return "<INPUT NAME=\"$field\" SIZE=45 VALUE=\"$def_value\">";
|
|
}
|
|
|
|
|
|
|
|
|
|
# Implementations of several of the below were blatently stolen from CGI.pm,
|
|
# by Lincoln D. Stein.
|
|
|
|
|
|
# Get rid of all the %xx encoding and the like from the given URL.
|
|
|
|
sub url_decode {
|
|
my ($todecode) = (@_);
|
|
$todecode =~ tr/+/ /; # pluses become spaces
|
|
$todecode =~ s/%([0-9a-fA-F]{2})/pack("c",hex($1))/ge;
|
|
return $todecode;
|
|
}
|
|
|
|
|
|
# Quotify a string, suitable for putting into a URL.
|
|
|
|
sub url_quote {
|
|
my($toencode) = (@_);
|
|
$toencode=~s/([^a-zA-Z0-9_\-.])/uc sprintf("%%%02x",ord($1))/eg;
|
|
return $toencode;
|
|
}
|
|
|
|
|
|
sub ParseUrlString {
|
|
my ($buffer, $f, $m) = (@_);
|
|
undef %$f;
|
|
undef %$m;
|
|
|
|
my %isnull;
|
|
my $remaining = $buffer;
|
|
while ($remaining ne "") {
|
|
my $item;
|
|
if ($remaining =~ /^([^&]*)&(.*)$/) {
|
|
$item = $1;
|
|
$remaining = $2;
|
|
} else {
|
|
$item = $remaining;
|
|
$remaining = "";
|
|
}
|
|
|
|
my $name;
|
|
my $value;
|
|
if ($item =~ /^([^=]*)=(.*)$/) {
|
|
$name = $1;
|
|
$value = url_decode($2);
|
|
} else {
|
|
$name = $item;
|
|
$value = "";
|
|
}
|
|
if ($value ne "") {
|
|
if (defined $f->{$name}) {
|
|
$f->{$name} .= $value;
|
|
my $ref = $m->{$name};
|
|
push @$ref, $value;
|
|
} else {
|
|
$f->{$name} = $value;
|
|
$m->{$name} = [$value];
|
|
}
|
|
} else {
|
|
$isnull{$name} = 1;
|
|
}
|
|
}
|
|
if (%isnull) {
|
|
foreach my $name (keys(%isnull)) {
|
|
if (!defined $f->{$name}) {
|
|
$f->{$name} = "";
|
|
$m->{$name} = [];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
sub ProcessFormFields {
|
|
my ($buffer) = (@_);
|
|
return ParseUrlString($buffer, \%::FORM, \%::MFORM);
|
|
}
|
|
|
|
|
|
sub ProcessMultipartFormFields {
|
|
my ($boundary) = (@_);
|
|
$boundary =~ s/^-*//;
|
|
my $remaining = $ENV{"CONTENT_LENGTH"};
|
|
my $inheader = 1;
|
|
my $itemname = "";
|
|
# open(DEBUG, ">debug") || die "Can't open debugging thing";
|
|
# print DEBUG "Boundary is '$boundary'\n";
|
|
while ($remaining > 0 && ($_ = <STDIN>)) {
|
|
$remaining -= length($_);
|
|
# print DEBUG "< $_";
|
|
if ($_ =~ m/^-*$boundary/) {
|
|
# print DEBUG "Entered header\n";
|
|
$inheader = 1;
|
|
$itemname = "";
|
|
next;
|
|
}
|
|
|
|
if ($inheader) {
|
|
if (m/^\s*$/) {
|
|
$inheader = 0;
|
|
# print DEBUG "left header\n";
|
|
$::FORM{$itemname} = "";
|
|
}
|
|
if (m/^Content-Disposition:\s*form-data\s*;\s*name\s*=\s*"([^\"]+)"/i) {
|
|
$itemname = $1;
|
|
# print DEBUG "Found itemname $itemname\n";
|
|
if (m/;\s*filename\s*=\s*"([^\"]+)"/i) {
|
|
$::FILENAME{$itemname} = $1;
|
|
}
|
|
}
|
|
|
|
next;
|
|
}
|
|
$::FORM{$itemname} .= $_;
|
|
}
|
|
delete $::FORM{""};
|
|
# Get rid of trailing newlines.
|
|
foreach my $i (keys %::FORM) {
|
|
chomp($::FORM{$i});
|
|
$::FORM{$i} =~ s/\r$//;
|
|
}
|
|
}
|
|
|
|
|
|
# check and see if a given field exists, is non-empty, and is set to a
|
|
# legal value. assume a browser bug and abort appropriately if not.
|
|
# if $legalsRef is not passed, just check to make sure the value exists and
|
|
# is non-NULL
|
|
#
|
|
sub CheckFormField (\%$;\@) {
|
|
my ($formRef, # a reference to the form to check (a hash)
|
|
$fieldname, # the fieldname to check
|
|
$legalsRef # (optional) ref to a list of legal values
|
|
) = @_;
|
|
|
|
if ( !defined $formRef->{$fieldname} ||
|
|
trim($formRef->{$fieldname}) eq "" ||
|
|
(defined($legalsRef) &&
|
|
lsearch($legalsRef, $formRef->{$fieldname})<0) ){
|
|
|
|
print "A legal $fieldname was not set; ";
|
|
print Param("browserbugmessage");
|
|
PutFooter();
|
|
exit 0;
|
|
}
|
|
}
|
|
|
|
# check and see if a given field is defined, and abort if not
|
|
#
|
|
sub CheckFormFieldDefined (\%$) {
|
|
my ($formRef, # a reference to the form to check (a hash)
|
|
$fieldname, # the fieldname to check
|
|
) = @_;
|
|
|
|
if ( !defined $formRef->{$fieldname} ) {
|
|
print "$fieldname was not defined; ";
|
|
print Param("browserbugmessage");
|
|
PutFooter();
|
|
exit 0;
|
|
}
|
|
}
|
|
|
|
sub ValidateBugID {
|
|
# Validates and verifies a bug ID, making sure the number is a
|
|
# positive integer, that it represents an existing bug in the
|
|
# database, and that the user is authorized to access that bug.
|
|
|
|
my ($id) = @_;
|
|
|
|
# Make sure the bug number is a positive integer.
|
|
# Whitespace can be ignored because the SQL server will ignore it.
|
|
$id =~ /^\s*([1-9][0-9]*)\s*$/
|
|
|| DisplayError("The bug number is invalid.")
|
|
&& exit;
|
|
|
|
# Get the values of the usergroupset and userid global variables
|
|
# and write them to local variables for use within this function,
|
|
# setting those local variables to the default value of zero if
|
|
# the global variables are undefined.
|
|
|
|
# "usergroupset" stores the set of groups the user is a member of,
|
|
# while "userid" stores the user's unique ID. These variables are
|
|
# set globally by either confirm_login() or quietly_check_login(),
|
|
# one of which should be run before calling this function; otherwise
|
|
# this function will treat the user as if they were not logged in
|
|
# and throw an error if they try to access a bug that requires
|
|
# permissions/authorization to access.
|
|
my $usergroupset = $::usergroupset || 0;
|
|
my $userid = $::userid || 0;
|
|
|
|
# Query the database for the bug, retrieving a boolean value that
|
|
# represents whether or not the user is authorized to access the bug.
|
|
|
|
# Users are authorized to access bugs if they are a member of all
|
|
# groups to which the bug is restricted. User group membership and
|
|
# bug restrictions are stored as bits within bitsets, so authorization
|
|
# can be determined by comparing the intersection of the user's
|
|
# bitset with the bug's bitset. If the result matches the bug's bitset
|
|
# the user is a member of all groups to which the bug is restricted
|
|
# and is authorized to access the bug.
|
|
|
|
# A user is also authorized to access a bug if she is the reporter,
|
|
# assignee, QA contact, or member of the cc: list of the bug and the bug
|
|
# allows users in those roles to see the bug. The boolean fields
|
|
# reporter_accessible, assignee_accessible, qacontact_accessible, and
|
|
# cclist_accessible identify whether or not those roles can see the bug.
|
|
|
|
# Bit arithmetic is performed by MySQL instead of Perl because bitset
|
|
# fields in the database are 64 bits wide (BIGINT), and Perl installations
|
|
# may or may not support integers larger than 32 bits. Using bitsets
|
|
# and doing bitset arithmetic is probably not cross-database compatible,
|
|
# however, so these mechanisms are likely to change in the future.
|
|
|
|
# Get data from the database about whether or not the user belongs to
|
|
# all groups the bug is in, and who are the bug's reporter and qa_contact
|
|
# along with which roles can always access the bug.
|
|
SendSQL("SELECT ((groupset & $usergroupset) = groupset) , reporter , assigned_to , qa_contact ,
|
|
reporter_accessible , assignee_accessible , qacontact_accessible , cclist_accessible
|
|
FROM bugs
|
|
WHERE bug_id = $id");
|
|
|
|
# Make sure the bug exists in the database.
|
|
MoreSQLData()
|
|
|| DisplayError("Bug #$id does not exist.")
|
|
&& exit;
|
|
|
|
my ($isauthorized, $reporter, $assignee, $qacontact, $reporter_accessible,
|
|
$assignee_accessible, $qacontact_accessible, $cclist_accessible) = FetchSQLData();
|
|
|
|
# Finish validation and return if the user is a member of all groups to which the bug belongs.
|
|
return if $isauthorized;
|
|
|
|
# Finish validation and return if the user is in a role that has access to the bug.
|
|
if ($userid) {
|
|
return
|
|
if ($reporter_accessible && $reporter == $userid)
|
|
|| ($assignee_accessible && $assignee == $userid)
|
|
|| ($qacontact_accessible && $qacontact == $userid);
|
|
}
|
|
|
|
# Try to authorize the user one more time by seeing if they are on
|
|
# the cc: list. If so, finish validation and return.
|
|
if ( $cclist_accessible ) {
|
|
my @cclist;
|
|
SendSQL("SELECT cc.who
|
|
FROM bugs , cc
|
|
WHERE bugs.bug_id = $id
|
|
AND cc.bug_id = bugs.bug_id
|
|
");
|
|
while (my ($ccwho) = FetchSQLData()) {
|
|
# more efficient to just check the var here instead of
|
|
# creating a potentially huge array to grep against
|
|
return if ($userid == $ccwho);
|
|
}
|
|
|
|
}
|
|
|
|
# The user did not pass any of the authorization tests, which means they
|
|
# are not authorized to see the bug. Display an error and stop execution.
|
|
# The error the user sees depends on whether or not they are logged in
|
|
# (i.e. $userid contains the user's positive integer ID).
|
|
if ($userid) {
|
|
DisplayError("You are not authorized to access bug #$id.");
|
|
} else {
|
|
DisplayError(
|
|
qq|You are not authorized to access bug #$id. To see this bug, you
|
|
must first <a href="show_bug.cgi?id=$id&GoAheadAndLogIn=1">log in
|
|
to an account</a> with the appropriate permissions.|
|
|
);
|
|
}
|
|
exit;
|
|
|
|
}
|
|
|
|
# check and see if a given string actually represents a positive
|
|
# integer, and abort if not.
|
|
#
|
|
sub CheckPosInt($) {
|
|
my ($number) = @_; # the fieldname to check
|
|
|
|
if ( $number !~ /^[1-9][0-9]*$/ ) {
|
|
print "Received string \"$number\" when positive integer expected; ";
|
|
print Param("browserbugmessage");
|
|
PutFooter();
|
|
exit 0;
|
|
}
|
|
}
|
|
|
|
sub FormData {
|
|
my ($field) = (@_);
|
|
return $::FORM{$field};
|
|
}
|
|
|
|
sub html_quote {
|
|
my ($var) = (@_);
|
|
$var =~ s/\&/\&/g;
|
|
$var =~ s/</\</g;
|
|
$var =~ s/>/\>/g;
|
|
return $var;
|
|
}
|
|
|
|
sub value_quote {
|
|
my ($var) = (@_);
|
|
$var =~ s/\&/\&/g;
|
|
$var =~ s/</\</g;
|
|
$var =~ s/>/\>/g;
|
|
$var =~ s/"/\"/g;
|
|
# See bug http://bugzilla.mozilla.org/show_bug.cgi?id=4928 for
|
|
# explanaion of why bugzilla does this linebreak substitution.
|
|
# This caused form submission problems in mozilla (bug 22983, 32000).
|
|
$var =~ s/\r\n/\
/g;
|
|
$var =~ s/\n\r/\
/g;
|
|
$var =~ s/\r/\
/g;
|
|
$var =~ s/\n/\
/g;
|
|
return $var;
|
|
}
|
|
|
|
sub navigation_header {
|
|
if (defined $::COOKIE{"BUGLIST"} && $::COOKIE{"BUGLIST"} ne "" &&
|
|
defined $::FORM{'id'}) {
|
|
my @bugs = split(/:/, $::COOKIE{"BUGLIST"});
|
|
my $cur = lsearch(\@bugs, $::FORM{"id"});
|
|
print "<B>Bug List:</B> (@{[$cur + 1]} of @{[$#bugs + 1]})\n";
|
|
print "<A HREF=\"show_bug.cgi?id=$bugs[0]\">First</A>\n";
|
|
print "<A HREF=\"show_bug.cgi?id=$bugs[$#bugs]\">Last</A>\n";
|
|
if ($cur > 0) {
|
|
print "<A HREF=\"show_bug.cgi?id=$bugs[$cur - 1]\">Prev</A>\n";
|
|
} else {
|
|
print "<I><FONT COLOR=\#777777>Prev</FONT></I>\n";
|
|
}
|
|
if ($cur < $#bugs) {
|
|
$::next_bug = $bugs[$cur + 1];
|
|
print "<A HREF=\"show_bug.cgi?id=$::next_bug\">Next</A>\n";
|
|
} else {
|
|
print "<I><FONT COLOR=\#777777>Next</FONT></I>\n";
|
|
}
|
|
print qq{ <A HREF="buglist.cgi?regetlastlist=1">Show list</A>\n};
|
|
}
|
|
print " <A HREF=query.cgi>Query page</A>\n";
|
|
print " <A HREF=enter_bug.cgi>Enter new bug</A>\n"
|
|
}
|
|
|
|
sub make_checkboxes {
|
|
my ($src,$default,$isregexp,$name) = (@_);
|
|
my $last = "";
|
|
my $capitalized = "";
|
|
my $popup = "";
|
|
my $found = 0;
|
|
$default = "" if !defined $default;
|
|
|
|
if ($src) {
|
|
foreach my $item (@$src) {
|
|
if ($item eq "-blank-" || $item ne $last) {
|
|
if ($item eq "-blank-") {
|
|
$item = "";
|
|
}
|
|
$last = $item;
|
|
$capitalized = $item;
|
|
$capitalized =~ tr/A-Z/a-z/;
|
|
$capitalized =~ s/^(.?)(.*)/\u$1$2/;
|
|
if ($isregexp ? $item =~ $default : $default eq $item) {
|
|
$popup .= "<INPUT NAME=$name TYPE=CHECKBOX VALUE=\"$item\" CHECKED>$capitalized<br>";
|
|
$found = 1;
|
|
} else {
|
|
$popup .= "<INPUT NAME=$name TYPE=CHECKBOX VALUE=\"$item\">$capitalized<br>";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!$found && $default ne "") {
|
|
$popup .= "<INPUT NAME=$name TYPE=CHECKBOX CHECKED>$default";
|
|
}
|
|
return $popup;
|
|
}
|
|
|
|
#
|
|
# make_selection_widget: creates an HTML selection widget from a list of text strings.
|
|
# $groupname is the name of the setting (form value) that this widget will control
|
|
# $src is the list of options
|
|
# you can specify a $default value which is either a string or a regex pattern to match to
|
|
# identify the default value
|
|
# $capitalize lets you optionally capitalize the option strings; the default is the value
|
|
# of Param("capitalizelists")
|
|
# $multiple is 1 if several options are selectable (default), 0 otherwise.
|
|
# $size is used for lists to control how many items are shown. The default is 7. A list of
|
|
# size 1 becomes a popup menu.
|
|
# $preferLists is 1 if selection lists should be used in favor of radio buttons and
|
|
# checkboxes, and 0 otherwise. The default is the value of Param("preferlists").
|
|
#
|
|
# The actual widget generated depends on the parameter settings:
|
|
#
|
|
# MULTIPLE PREFERLISTS SIZE RESULT
|
|
# 0 (single) 0 =1 Popup Menu (normal for list of size 1)
|
|
# 0 (single) 0 >1 Radio buttons
|
|
# 0 (single) 1 =1 Popup Menu (normal for list of size 1)
|
|
# 0 (single) 1 n>1 List of size n, single selection
|
|
# 1 (multi) 0 n/a Check boxes; size ignored
|
|
# 1 (multi) 1 n/a List of size n, multiple selection, of size n
|
|
#
|
|
sub make_selection_widget {
|
|
my ($groupname,$src,$default,$isregexp,$multiple, $size, $capitalize, $preferLists) = (@_);
|
|
my $last = "";
|
|
my $popup = "";
|
|
my $found = 0;
|
|
my $displaytext = "";
|
|
$groupname = "" if !defined $groupname;
|
|
$default = "" if !defined $default;
|
|
$capitalize = Param("capitalizelists") if !defined $capitalize;
|
|
$multiple = 1 if !defined $multiple;
|
|
$preferLists = Param("preferlists") if !defined $preferLists;
|
|
$size = 7 if !defined $size;
|
|
my $type = "LIST";
|
|
if (!$preferLists) {
|
|
if ($multiple) {
|
|
$type = "CHECKBOX";
|
|
} else {
|
|
if ($size > 1) {
|
|
$type = "RADIO";
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($type eq "LIST") {
|
|
$popup .= "<SELECT NAME=\"$groupname\"";
|
|
if ($multiple) {
|
|
$popup .= " MULTIPLE";
|
|
}
|
|
$popup .= " SIZE=$size>\n";
|
|
}
|
|
if ($src) {
|
|
foreach my $item (@$src) {
|
|
if ($item eq "-blank-" || $item ne $last) {
|
|
if ($item eq "-blank-") {
|
|
$item = "";
|
|
}
|
|
$last = $item;
|
|
$displaytext = $item;
|
|
if ($capitalize) {
|
|
$displaytext =~ tr/A-Z/a-z/;
|
|
$displaytext =~ s/^(.?)(.*)/\u$1$2/;
|
|
}
|
|
|
|
if ($isregexp ? $item =~ $default : $default eq $item) {
|
|
if ($type eq "CHECKBOX") {
|
|
$popup .= "<INPUT NAME=$groupname type=checkbox VALUE=\"$item\" CHECKED>$displaytext<br>";
|
|
} elsif ($type eq "RADIO") {
|
|
$popup .= "<INPUT NAME=$groupname type=radio VALUE=\"$item\" check>$displaytext<br>";
|
|
} else {
|
|
$popup .= "<OPTION SELECTED VALUE=\"$item\">$displaytext";
|
|
}
|
|
$found = 1;
|
|
} else {
|
|
if ($type eq "CHECKBOX") {
|
|
$popup .= "<INPUT NAME=$groupname type=checkbox VALUE=\"$item\">$displaytext<br>";
|
|
} elsif ($type eq "RADIO") {
|
|
$popup .= "<INPUT NAME=$groupname type=radio VALUE=\"$item\">$displaytext<br>";
|
|
} else {
|
|
$popup .= "<OPTION VALUE=\"$item\">$displaytext";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!$found && $default ne "") {
|
|
if ($type eq "CHECKBOX") {
|
|
$popup .= "<INPUT NAME=$groupname type=checkbox CHECKED>$default";
|
|
} elsif ($type eq "RADIO") {
|
|
$popup .= "<INPUT NAME=$groupname type=radio checked>$default";
|
|
} else {
|
|
$popup .= "<OPTION SELECTED>$default";
|
|
}
|
|
}
|
|
if ($type eq "LIST") {
|
|
$popup .= "</SELECT>";
|
|
}
|
|
return $popup;
|
|
}
|
|
|
|
|
|
$::CheckOptionValues = 1;
|
|
|
|
sub make_options {
|
|
my ($src,$default,$isregexp) = (@_);
|
|
my $last = "";
|
|
my $popup = "";
|
|
my $found = 0;
|
|
$default = "" if !defined $default;
|
|
|
|
if ($src) {
|
|
foreach my $item (@$src) {
|
|
if ($item eq "-blank-" || $item ne $last) {
|
|
if ($item eq "-blank-") {
|
|
$item = "";
|
|
}
|
|
$last = $item;
|
|
if ($isregexp ? $item =~ $default : $default eq $item) {
|
|
$popup .= "<OPTION SELECTED VALUE=\"$item\">$item";
|
|
$found = 1;
|
|
} else {
|
|
$popup .= "<OPTION VALUE=\"$item\">$item";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!$found && $default ne "") {
|
|
if ( Param("strictvaluechecks") && $::CheckOptionValues &&
|
|
($default ne $::dontchange) && ($default ne "-All-") &&
|
|
($default ne "DUPLICATE") ) {
|
|
print "Possible bug database corruption has been detected. " .
|
|
"Please send mail to " . Param("maintainer") . " with " .
|
|
"details of what you were doing when this message " .
|
|
"appeared. Thank you.\n";
|
|
if (!$src) {
|
|
$src = ["???null???"];
|
|
}
|
|
print "<pre>src = " . value_quote(join(' ', @$src)) . "\n";
|
|
print "default = " . value_quote($default) . "</pre>";
|
|
PutFooter();
|
|
# confess "Gulp.";
|
|
exit 0;
|
|
|
|
} else {
|
|
$popup .= "<OPTION SELECTED>$default";
|
|
}
|
|
}
|
|
return $popup;
|
|
}
|
|
|
|
|
|
sub make_popup {
|
|
my ($name,$src,$default,$listtype,$onchange) = (@_);
|
|
my $popup = "<SELECT NAME=$name";
|
|
if ($listtype > 0) {
|
|
$popup .= " SIZE=5";
|
|
if ($listtype == 2) {
|
|
$popup .= " MULTIPLE";
|
|
}
|
|
}
|
|
if (defined $onchange && $onchange ne "") {
|
|
$popup .= " onchange=$onchange";
|
|
}
|
|
$popup .= ">" . make_options($src, $default,
|
|
($listtype == 2 && $default ne ""));
|
|
$popup .= "</SELECT>";
|
|
return $popup;
|
|
}
|
|
|
|
|
|
sub BuildPulldown {
|
|
my ($name, $valuelist, $default) = (@_);
|
|
|
|
my $entry = qq{<SELECT NAME="$name">};
|
|
foreach my $i (@$valuelist) {
|
|
my ($tag, $desc) = (@$i);
|
|
my $selectpart = "";
|
|
if ($tag eq $default) {
|
|
$selectpart = " SELECTED";
|
|
}
|
|
if (!defined $desc) {
|
|
$desc = $tag;
|
|
}
|
|
$entry .= qq{<OPTION$selectpart VALUE="$tag">$desc\n};
|
|
}
|
|
$entry .= qq{</SELECT>};
|
|
return $entry;
|
|
}
|
|
|
|
|
|
|
|
|
|
sub PasswordForLogin {
|
|
my ($login) = (@_);
|
|
SendSQL("select cryptpassword from profiles where login_name = " .
|
|
SqlQuote($login));
|
|
my $result = FetchOneColumn();
|
|
if (!defined $result) {
|
|
$result = "";
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
|
|
sub quietly_check_login() {
|
|
$::usergroupset = '0';
|
|
my $loginok = 0;
|
|
$::disabledreason = '';
|
|
$::userid = 0;
|
|
if (defined $::COOKIE{"Bugzilla_login"} &&
|
|
defined $::COOKIE{"Bugzilla_logincookie"}) {
|
|
ConnectToDatabase();
|
|
if (!defined $ENV{'REMOTE_HOST'}) {
|
|
$ENV{'REMOTE_HOST'} = $ENV{'REMOTE_ADDR'};
|
|
}
|
|
SendSQL("SELECT profiles.userid, profiles.groupset, " .
|
|
"profiles.login_name, " .
|
|
"profiles.login_name = " .
|
|
SqlQuote($::COOKIE{"Bugzilla_login"}) .
|
|
" AND profiles.cryptpassword = logincookies.cryptpassword " .
|
|
"AND logincookies.hostname = " .
|
|
SqlQuote($ENV{"REMOTE_HOST"}) .
|
|
", profiles.disabledtext " .
|
|
" FROM profiles, logincookies WHERE logincookies.cookie = " .
|
|
SqlQuote($::COOKIE{"Bugzilla_logincookie"}) .
|
|
" AND profiles.userid = logincookies.userid");
|
|
my @row;
|
|
if (@row = FetchSQLData()) {
|
|
my ($userid, $groupset, $loginname, $ok, $disabledtext) = (@row);
|
|
if ($ok) {
|
|
if ($disabledtext eq '') {
|
|
$loginok = 1;
|
|
$::userid = $userid;
|
|
$::usergroupset = $groupset;
|
|
$::COOKIE{"Bugzilla_login"} = $loginname; # Makes sure case
|
|
# is in
|
|
# canonical form.
|
|
} else {
|
|
$::disabledreason = $disabledtext;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
# if 'who' is passed in, verify that it's a good value
|
|
if ($::FORM{'who'}) {
|
|
my $whoid = DBname_to_id($::FORM{'who'});
|
|
delete $::FORM{'who'} unless $whoid;
|
|
}
|
|
if (!$loginok) {
|
|
delete $::COOKIE{"Bugzilla_login"};
|
|
}
|
|
return $loginok;
|
|
}
|
|
|
|
|
|
|
|
|
|
sub CheckEmailSyntax {
|
|
my ($addr) = (@_);
|
|
my $match = Param('emailregexp');
|
|
if ($addr !~ /$match/ || $addr =~ /[\\\(\)<>&,;:"\[\] \t\r\n]/) {
|
|
print "Content-type: text/html\n\n";
|
|
|
|
# For security, escape HTML special characters.
|
|
$addr = html_quote($addr);
|
|
|
|
PutHeader("Check e-mail syntax");
|
|
print "The e-mail address you entered\n";
|
|
print "(<b>$addr</b>) didn't match our minimal\n";
|
|
print "syntax checking for a legal email address.\n";
|
|
print Param('emailregexpdesc') . "\n";
|
|
print "It must also not contain any of these special characters: " .
|
|
"<tt>\\ ( ) & < > , ; : \" [ ]</tt> " .
|
|
"or any whitespace.\n";
|
|
print "<p>Please click <b>Back</b> and try again.\n";
|
|
PutFooter();
|
|
exit;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
sub MailPassword {
|
|
my ($login, $password) = (@_);
|
|
my $urlbase = Param("urlbase");
|
|
my $template = Param("passwordmail");
|
|
my $msg = PerformSubsts($template,
|
|
{"mailaddress" => $login . Param('emailsuffix'),
|
|
"login" => $login,
|
|
"password" => $password});
|
|
|
|
open SENDMAIL, "|/usr/lib/sendmail -t";
|
|
print SENDMAIL $msg;
|
|
close SENDMAIL;
|
|
|
|
print "The password for the e-mail address\n";
|
|
print "$login has been e-mailed to that address.\n";
|
|
print "<p>When the e-mail arrives, you can click <b>Back</b>\n";
|
|
print "and enter your password in the form there.\n";
|
|
}
|
|
|
|
|
|
sub confirm_login {
|
|
my ($nexturl) = (@_);
|
|
|
|
# Uncommenting the next line can help debugging...
|
|
# print "Content-type: text/plain\n\n";
|
|
|
|
ConnectToDatabase();
|
|
# I'm going to reorganize some of this stuff a bit. Since we're adding
|
|
# a second possible validation method (LDAP), we need to move some of this
|
|
# to a later section. -Joe Robins, 8/3/00
|
|
my $enteredlogin = "";
|
|
my $realcryptpwd = "";
|
|
|
|
# If the form contains Bugzilla login and password fields, use Bugzilla's
|
|
# built-in authentication to authenticate the user (otherwise use LDAP below).
|
|
if (defined $::FORM{"Bugzilla_login"} && defined $::FORM{"Bugzilla_password"}) {
|
|
# Make sure the user's login name is a valid email address.
|
|
$enteredlogin = $::FORM{"Bugzilla_login"};
|
|
CheckEmailSyntax($enteredlogin);
|
|
|
|
# Retrieve the user's ID and crypted password from the database.
|
|
my $userid;
|
|
SendSQL("SELECT userid, cryptpassword FROM profiles
|
|
WHERE login_name = " . SqlQuote($enteredlogin));
|
|
($userid, $realcryptpwd) = FetchSQLData();
|
|
|
|
# Make sure the user exists or throw an error (but do not admit it was a username
|
|
# error to make it harder for a cracker to find account names by brute force).
|
|
$userid
|
|
|| DisplayError("The username or password you entered is not valid.")
|
|
&& exit;
|
|
|
|
# If this is a new user, generate a password, insert a record
|
|
# into the database, and email their password to them.
|
|
if ( defined $::FORM{"PleaseMailAPassword"} && !$userid ) {
|
|
my $password = InsertNewUser($enteredlogin, "");
|
|
print "Content-Type: text/html\n\n";
|
|
PutHeader("Account Created");
|
|
MailPassword($enteredlogin, $password);
|
|
PutFooter();
|
|
exit;
|
|
}
|
|
|
|
# Otherwise, authenticate the user.
|
|
else {
|
|
# Get the salt from the user's crypted password.
|
|
my $salt = $realcryptpwd;
|
|
|
|
# Using the salt, crypt the password the user entered.
|
|
my $enteredCryptedPassword = crypt( $::FORM{"Bugzilla_password"} , $salt );
|
|
|
|
# Make sure the passwords match or throw an error.
|
|
($enteredCryptedPassword eq $realcryptpwd)
|
|
|| DisplayError("The username or password you entered is not valid.")
|
|
&& exit;
|
|
|
|
# If the user has successfully logged in, delete any password tokens
|
|
# lying around in the system for them.
|
|
use Token;
|
|
my $token = Token::HasPasswordToken($userid);
|
|
while ( $token ) {
|
|
Token::Cancel($token, "user logged in");
|
|
$token = Token::HasPasswordToken($userid);
|
|
}
|
|
}
|
|
|
|
} elsif (Param("useLDAP") &&
|
|
defined $::FORM{"LDAP_login"} &&
|
|
defined $::FORM{"LDAP_password"}) {
|
|
# If we're using LDAP for login, we've got an entirely different
|
|
# set of things to check.
|
|
|
|
# see comment at top of file near eval
|
|
# First, if we don't have the LDAP modules available to us, we can't
|
|
# do this.
|
|
# if(!$have_ldap) {
|
|
# print "Content-type: text/html\n\n";
|
|
# PutHeader("LDAP not enabled");
|
|
# print "The necessary modules for LDAP login are not installed on ";
|
|
# print "this machine. Please send mail to ".Param("maintainer");
|
|
# print " and notify him of this problem.\n";
|
|
# PutFooter();
|
|
# exit;
|
|
# }
|
|
|
|
# Next, we need to bind anonymously to the LDAP server. This is
|
|
# because we need to get the Distinguished Name of the user trying
|
|
# to log in. Some servers (such as iPlanet) allow you to have unique
|
|
# uids spread out over a subtree of an area (such as "People"), so
|
|
# just appending the Base DN to the uid isn't sufficient to get the
|
|
# user's DN. For servers which don't work this way, there will still
|
|
# be no harm done.
|
|
my $LDAPserver = Param("LDAPserver");
|
|
if ($LDAPserver eq "") {
|
|
print "Content-type: text/html\n\n";
|
|
PutHeader("LDAP server not defined");
|
|
print "The LDAP server for authentication has not been defined. ";
|
|
print "Please contact ".Param("maintainer")." ";
|
|
print "and notify him of this problem.\n";
|
|
PutFooter();
|
|
exit;
|
|
}
|
|
|
|
my $LDAPport = "389"; #default LDAP port
|
|
if($LDAPserver =~ /:/) {
|
|
($LDAPserver, $LDAPport) = split(":",$LDAPserver);
|
|
}
|
|
my $LDAPconn = new Mozilla::LDAP::Conn($LDAPserver,$LDAPport);
|
|
if(!$LDAPconn) {
|
|
print "Content-type: text/html\n\n";
|
|
PutHeader("Unable to connect to LDAP server");
|
|
print "I was unable to connect to the LDAP server for user ";
|
|
print "authentication. Please contact ".Param("maintainer");
|
|
print " and notify him of this problem.\n";
|
|
PutFooter();
|
|
exit;
|
|
}
|
|
|
|
# We've got our anonymous bind; let's look up this user.
|
|
my $dnEntry = $LDAPconn->search(Param("LDAPBaseDN"),"subtree","uid=".$::FORM{"LDAP_login"});
|
|
if(!$dnEntry) {
|
|
print "Content-type: text/html\n\n";
|
|
PutHeader("Login Failed");
|
|
print "The username or password you entered is not valid.\n";
|
|
print "Please click <b>Back</b> and try again.\n";
|
|
PutFooter();
|
|
exit;
|
|
}
|
|
|
|
# Now we get the DN from this search. Once we've got that, we're
|
|
# done with the anonymous bind, so we close it.
|
|
my $userDN = $dnEntry->getDN;
|
|
$LDAPconn->close;
|
|
|
|
# Now we attempt to bind as the specified user.
|
|
$LDAPconn = new Mozilla::LDAP::Conn($LDAPserver,$LDAPport,$userDN,$::FORM{"LDAP_password"});
|
|
if(!$LDAPconn) {
|
|
print "Content-type: text/html\n\n";
|
|
PutHeader("Login Failed");
|
|
print "The username or password you entered is not valid.\n";
|
|
print "Please click <b>Back</b> and try again.\n";
|
|
PutFooter();
|
|
exit;
|
|
}
|
|
|
|
# And now we're going to repeat the search, so that we can get the
|
|
# mail attribute for this user.
|
|
my $userEntry = $LDAPconn->search(Param("LDAPBaseDN"),"subtree","uid=".$::FORM{"LDAP_login"});
|
|
if(!$userEntry->exists(Param("LDAPmailattribute"))) {
|
|
print "Content-type: text/html\n\n";
|
|
PutHeader("LDAP authentication error");
|
|
print "I was unable to retrieve the ".Param("LDAPmailattribute");
|
|
print " attribute from the LDAP server. Please contact ";
|
|
print Param("maintainer")." and notify him of this error.\n";
|
|
PutFooter();
|
|
exit;
|
|
}
|
|
|
|
# Mozilla::LDAP::Entry->getValues returns an array for the attribute
|
|
# requested, even if there's only one entry.
|
|
$enteredlogin = ($userEntry->getValues(Param("LDAPmailattribute")))[0];
|
|
|
|
# We're going to need the cryptpwd for this user from the database
|
|
# so that we can set the cookie below, even though we're not going
|
|
# to use it for authentication.
|
|
$realcryptpwd = PasswordForLogin($enteredlogin);
|
|
|
|
# If we don't get a result, then we've got a user who isn't in
|
|
# Bugzilla's database yet, so we've got to add them.
|
|
if($realcryptpwd eq "") {
|
|
# We'll want the user's name for this.
|
|
my $userRealName = ($userEntry->getValues("displayName"))[0];
|
|
if($userRealName eq "") {
|
|
$userRealName = ($userEntry->getValues("cn"))[0];
|
|
}
|
|
InsertNewUser($enteredlogin, $userRealName);
|
|
$realcryptpwd = PasswordForLogin($enteredlogin);
|
|
}
|
|
} # end LDAP authentication
|
|
|
|
# And now, if we've logged in via either method, then we need to set
|
|
# the cookies.
|
|
if($enteredlogin ne "") {
|
|
$::COOKIE{"Bugzilla_login"} = $enteredlogin;
|
|
if (!defined $ENV{'REMOTE_HOST'}) {
|
|
$ENV{'REMOTE_HOST'} = $ENV{'REMOTE_ADDR'};
|
|
}
|
|
SendSQL("insert into logincookies (userid,cryptpassword,hostname) values (@{[DBNameToIdAndCheck($enteredlogin)]}, @{[SqlQuote($realcryptpwd)]}, @{[SqlQuote($ENV{'REMOTE_HOST'})]})");
|
|
SendSQL("select LAST_INSERT_ID()");
|
|
my $logincookie = FetchOneColumn();
|
|
|
|
$::COOKIE{"Bugzilla_logincookie"} = $logincookie;
|
|
print "Set-Cookie: Bugzilla_login=$enteredlogin ; path=/; expires=Sun, 30-Jun-2029 00:00:00 GMT\n";
|
|
print "Set-Cookie: Bugzilla_logincookie=$logincookie ; path=/; expires=Sun, 30-Jun-2029 00:00:00 GMT\n";
|
|
|
|
# This next one just cleans out any old bugzilla passwords that may
|
|
# be sitting around in the cookie files, from the bad old days when
|
|
# we actually stored the password there.
|
|
print "Set-Cookie: Bugzilla_password= ; path=/; expires=Sun, 30-Jun-80 00:00:00 GMT\n";
|
|
}
|
|
|
|
|
|
my $loginok = quietly_check_login();
|
|
|
|
if ($loginok != 1) {
|
|
if ($::disabledreason) {
|
|
print "Set-Cookie: Bugzilla_login= ; path=/; expires=Sun, 30-Jun-80 00:00:00 GMT
|
|
Set-Cookie: Bugzilla_logincookie= ; path=/; expires=Sun, 30-Jun-80 00:00:00 GMT
|
|
Set-Cookie: Bugzilla_password= ; path=/; expires=Sun, 30-Jun-80 00:00:00 GMT
|
|
Content-type: text/html
|
|
|
|
";
|
|
PutHeader("Your account has been disabled");
|
|
print $::disabledreason;
|
|
print "<HR>\n";
|
|
print "If you believe your account should be restored, please\n";
|
|
print "send email to " . Param("maintainer") . " explaining\n";
|
|
print "why.\n";
|
|
PutFooter();
|
|
exit();
|
|
}
|
|
print "Content-type: text/html\n\n";
|
|
PutHeader("Login");
|
|
if(Param("useLDAP")) {
|
|
print "I need a legitimate LDAP username and password to continue.\n";
|
|
} else {
|
|
print "I need a legitimate e-mail address and password to continue.\n";
|
|
}
|
|
if (!defined $nexturl || $nexturl eq "") {
|
|
# Sets nexturl to be argv0, stripping everything up to and
|
|
# including the last slash (or backslash on Windows).
|
|
$0 =~ m:[^/\\]*$:;
|
|
$nexturl = $&;
|
|
}
|
|
my $method = "POST";
|
|
# We always want to use POST here, because we're submitting a password and don't
|
|
# want to see it in the location bar in the browser in case a co-worker is looking
|
|
# over your shoulder. If you have cookies off and need to bookmark the query, you
|
|
# can bookmark it from the screen asking for your password, and it should still
|
|
# work. See http://bugzilla.mozilla.org/show_bug.cgi?id=15980
|
|
# if (defined $ENV{"REQUEST_METHOD"} && length($::buffer) > 1) {
|
|
# $method = $ENV{"REQUEST_METHOD"};
|
|
# }
|
|
print "
|
|
<FORM action=$nexturl method=$method>
|
|
<table>
|
|
<tr>";
|
|
if(Param("useLDAP")) {
|
|
print "
|
|
<td align=right><b>Username:</b></td>
|
|
<td><input size=10 name=LDAP_login></td>
|
|
</tr>
|
|
<tr>
|
|
<td align=right><b>Password:</b></td>
|
|
<td><input type=password size=10 name=LDAP_password></td>";
|
|
} else {
|
|
print "
|
|
<td align=right><b>E-mail address:</b></td>
|
|
<td><input size=35 name=Bugzilla_login></td>
|
|
</tr>
|
|
<tr>
|
|
<td align=right><b>Password:</b></td>
|
|
<td><input type=password size=35 name=Bugzilla_password></td>";
|
|
}
|
|
print "
|
|
</tr>
|
|
</table>
|
|
";
|
|
# Add all the form fields into the form as hidden fields
|
|
# (except for Bugzilla_login and Bugzilla_password which we
|
|
# already added as text fields above).
|
|
foreach my $i ( grep( $_ !~ /^Bugzilla_/ , keys %::FORM ) ) {
|
|
print qq|<input type="hidden" name="$i" value="@{[value_quote($::FORM{$i})]}">\n|;
|
|
}
|
|
|
|
print qq|
|
|
<input type="submit" name="GoAheadAndLogIn" value="Login">
|
|
</form>
|
|
|;
|
|
|
|
# Allow the user to request a token to change their password (unless
|
|
# we are using LDAP, in which case the user must use LDAP to change it).
|
|
unless( Param("useLDAP") ) {
|
|
print qq|
|
|
<hr>
|
|
<form method="get" action="token.cgi">
|
|
<input type="hidden" name="a" value="reqpw">
|
|
If you have forgotten your password,
|
|
enter your login name below and submit a request
|
|
to change your password.<br>
|
|
<input size="35" name="loginname">
|
|
<input type="submit" value="Submit Request">
|
|
</form>
|
|
<hr>
|
|
If you don't have a Bugzilla account, you can <a href="createaccount.cgi">create a new account</a>.
|
|
|;
|
|
}
|
|
|
|
# This seems like as good as time as any to get rid of old
|
|
# crufty junk in the logincookies table. Get rid of any entry
|
|
# that hasn't been used in a month.
|
|
if ($::dbwritesallowed) {
|
|
SendSQL("DELETE FROM logincookies " .
|
|
"WHERE TO_DAYS(NOW()) - TO_DAYS(lastused) > 30");
|
|
}
|
|
|
|
|
|
PutFooter();
|
|
exit;
|
|
}
|
|
|
|
# Update the timestamp on our logincookie, so it'll keep on working.
|
|
if ($::dbwritesallowed) {
|
|
SendSQL("UPDATE logincookies SET lastused = null " .
|
|
"WHERE cookie = $::COOKIE{'Bugzilla_logincookie'}");
|
|
}
|
|
return $::userid;
|
|
}
|
|
|
|
|
|
sub PutHeader {
|
|
my ($title, $h1, $h2, $extra, $jscript) = (@_);
|
|
|
|
if (!defined $h1) {
|
|
$h1 = $title;
|
|
}
|
|
if (!defined $h2) {
|
|
$h2 = "";
|
|
}
|
|
if (!defined $extra) {
|
|
$extra = "";
|
|
}
|
|
$jscript ||= "";
|
|
# If we are shutdown, we want a very basic page to give that
|
|
# information. Also, the page title should indicate that
|
|
# we are down.
|
|
if (Param('shutdownhtml')) {
|
|
$title = "Bugzilla is Down";
|
|
$h1 = "Bugzilla is currently down";
|
|
$h2 = "";
|
|
$extra = "";
|
|
$jscript = "";
|
|
}
|
|
|
|
print "<HTML><HEAD>\n<TITLE>$title</TITLE>\n";
|
|
print Param("headerhtml") . "\n$jscript\n</HEAD>\n";
|
|
print "<BODY " . Param("bodyhtml") . " $extra>\n";
|
|
|
|
print PerformSubsts(Param("bannerhtml"), undef);
|
|
|
|
print "<TABLE BORDER=0 CELLSPACING=0 WIDTH=\"100%\">\n";
|
|
print " <TR>\n";
|
|
print " <TD WIDTH=10% VALIGN=TOP ALIGN=LEFT>\n";
|
|
print " <TABLE BORDER=0 CELLPADDING=0 CELLSPACING=2>\n";
|
|
print " <TR><TD VALIGN=TOP ALIGN=LEFT NOWRAP>\n";
|
|
print " <FONT SIZE=+1><B>$h1</B></FONT>";
|
|
print " </TD></TR>\n";
|
|
print " </TABLE>\n";
|
|
print " </TD>\n";
|
|
print " <TD VALIGN=CENTER> </TD>\n";
|
|
print " <TD VALIGN=CENTER ALIGN=LEFT>\n";
|
|
|
|
print "$h2\n";
|
|
print "</TD></TR></TABLE>\n";
|
|
|
|
if (Param("shutdownhtml")) {
|
|
# If we are dealing with the params page, we want
|
|
# to ignore shutdownhtml
|
|
if ($0 !~ m:[\\/](do)?editparams.cgi$:) {
|
|
print "<p>\n";
|
|
print Param("shutdownhtml");
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
sub PutFooter {
|
|
print PerformSubsts(Param("footerhtml"));
|
|
SyncAnyPendingShadowChanges();
|
|
}
|
|
|
|
|
|
sub DisplayError {
|
|
my ($message, $title) = (@_);
|
|
$title ||= "Error";
|
|
|
|
print "Content-type: text/html\n\n";
|
|
PutHeader($title);
|
|
|
|
print PerformSubsts( Param("errorhtml") , {errormsg => $message} );
|
|
|
|
PutFooter();
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub PuntTryAgain ($) {
|
|
my ($str) = (@_);
|
|
print PerformSubsts(Param("errorhtml"),
|
|
{errormsg => $str});
|
|
SendSQL("UNLOCK TABLES");
|
|
PutFooter();
|
|
exit;
|
|
}
|
|
|
|
|
|
sub CheckIfVotedConfirmed {
|
|
my ($id, $who) = (@_);
|
|
SendSQL("SELECT bugs.votes, bugs.bug_status, products.votestoconfirm, " .
|
|
" bugs.everconfirmed " .
|
|
"FROM bugs, products " .
|
|
"WHERE bugs.bug_id = $id AND products.product = bugs.product");
|
|
my ($votes, $status, $votestoconfirm, $everconfirmed) = (FetchSQLData());
|
|
if ($votes >= $votestoconfirm && $status eq $::unconfirmedstate) {
|
|
SendSQL("UPDATE bugs SET bug_status = 'NEW', everconfirmed = 1 " .
|
|
"WHERE bug_id = $id");
|
|
my $fieldid = GetFieldID("bug_status");
|
|
SendSQL("INSERT INTO bugs_activity " .
|
|
"(bug_id,who,bug_when,fieldid,removed,added) VALUES " .
|
|
"($id,$who,now(),$fieldid,'$::unconfirmedstate','NEW')");
|
|
if (!$everconfirmed) {
|
|
$fieldid = GetFieldID("everconfirmed");
|
|
SendSQL("INSERT INTO bugs_activity " .
|
|
"(bug_id,who,bug_when,fieldid,removed,added) VALUES " .
|
|
"($id,$who,now(),$fieldid,'0','1')");
|
|
}
|
|
AppendComment($id, DBID_to_name($who),
|
|
"*** This bug has been confirmed by popular vote. ***");
|
|
print "<TABLE BORDER=1><TD><H2>Bug $id has been confirmed by votes.</H2>\n";
|
|
system("./processmail", $id);
|
|
print "<TD><A HREF=\"show_bug.cgi?id=$id\">Go To BUG# $id</A></TABLE>\n";
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub DumpBugActivity {
|
|
my ($id, $starttime) = (@_);
|
|
my $datepart = "";
|
|
|
|
die "Invalid id: $id" unless $id=~/^\s*\d+\s*$/;
|
|
|
|
if (defined $starttime) {
|
|
$datepart = "and bugs_activity.bug_when >= $starttime";
|
|
}
|
|
my $query = "
|
|
SELECT IFNULL(fielddefs.description, bugs_activity.fieldid),
|
|
bugs_activity.attach_id,
|
|
bugs_activity.bug_when,
|
|
bugs_activity.removed, bugs_activity.added,
|
|
profiles.login_name
|
|
FROM bugs_activity LEFT JOIN fielddefs ON
|
|
bugs_activity.fieldid = fielddefs.fieldid,
|
|
profiles
|
|
WHERE bugs_activity.bug_id = $id $datepart
|
|
AND profiles.userid = bugs_activity.who
|
|
ORDER BY bugs_activity.bug_when";
|
|
|
|
SendSQL($query);
|
|
|
|
# Instead of outright printing this, we are going to store it in a $html
|
|
# variable and print it and the end. This is so we can explain ? (if nesc.)
|
|
# at the top of the activity table rather than the botom.
|
|
my $html = "";
|
|
$html .= "<table border cellpadding=4>\n";
|
|
$html .= "<tr>\n";
|
|
$html .= " <th>Who</th><th>What</th><th>Removed</th><th>Added</th><th>When</th>\n";
|
|
$html .= "</tr>\n";
|
|
|
|
my @row;
|
|
my $incomplete_data = 0;
|
|
while (@row = FetchSQLData()) {
|
|
my ($field,$attachid,$when,$removed,$added,$who) = (@row);
|
|
$field =~ s/^Attachment/<a href="attachment.cgi?id=$attachid&action=view">Attachment #$attachid<\/a>/
|
|
if (Param('useattachmenttracker') && $attachid);
|
|
$removed = html_quote($removed);
|
|
$added = html_quote($added);
|
|
$removed = " " if $removed eq "";
|
|
$added = " " if $added eq "";
|
|
if ($added =~ /^\?/ || $removed =~ /^\?/) {
|
|
$incomplete_data = 1;
|
|
}
|
|
$html .= "<tr>\n";
|
|
$html .= "<td>$who</td>\n";
|
|
$html .= "<td>$field</td>\n";
|
|
$html .= "<td>$removed</td>\n";
|
|
$html .= "<td>$added</td>\n";
|
|
$html .= "<td>$when</td>\n";
|
|
$html .= "</tr>\n";
|
|
}
|
|
$html .= "</table>\n";
|
|
if ($incomplete_data) {
|
|
print "There was a bug in older versions of Bugzilla which caused activity data \n";
|
|
print "to be lost if there was a large number of cc's or dependencies. That \n";
|
|
print "has been fixed, however, there was some data already lost on this bug \n";
|
|
print "that could not be regenerated. The changes that the script could not \n";
|
|
print "reliably determine are prefixed by '?'\n";
|
|
print "<p>\n";
|
|
}
|
|
print $html;
|
|
}
|
|
|
|
|
|
sub GetCommandMenu {
|
|
my $loggedin = quietly_check_login();
|
|
my $html = "";
|
|
$html .= <<"--endquote--";
|
|
<FORM METHOD=GET ACTION="show_bug.cgi">
|
|
<TABLE width="100%"><TR><TD>
|
|
Actions:
|
|
</TD><TD VALIGN="middle" NOWRAP>
|
|
<a href='enter_bug.cgi'>New</a> | <a href='query.cgi'>Query</a> |
|
|
--endquote--
|
|
|
|
if (-e "query2.cgi") {
|
|
$html .= "[<a href='query2.cgi'>beta</a>]";
|
|
}
|
|
|
|
$html .=
|
|
qq{ <INPUT TYPE=SUBMIT VALUE="Find"> bug \# <INPUT NAME=id SIZE=6>};
|
|
|
|
$html .= " | <a href='reports.cgi'>Reports</a>";
|
|
if ($loggedin) {
|
|
if ($::anyvotesallowed) {
|
|
$html .= " | <A HREF=\"showvotes.cgi\">My votes</A>";
|
|
}
|
|
}
|
|
if ($loggedin) {
|
|
#a little mandatory SQL, used later on
|
|
SendSQL("SELECT mybugslink, userid, blessgroupset FROM profiles " .
|
|
"WHERE login_name = " . SqlQuote($::COOKIE{'Bugzilla_login'}));
|
|
my ($mybugslink, $userid, $blessgroupset) = (FetchSQLData());
|
|
|
|
#Begin settings
|
|
$html .= "</TD><TD> </TD><TD VALIGN=middle><NOBR>Edit <a href='userprefs.cgi'>prefs</a></NOBR>";
|
|
if (UserInGroup("tweakparams")) {
|
|
$html .= ", <a href=editparams.cgi>parameters</a>";
|
|
}
|
|
if (UserInGroup("editusers") || $blessgroupset) {
|
|
$html .= ", <a href=editusers.cgi>users</a>";
|
|
}
|
|
if (UserInGroup("editcomponents")) {
|
|
$html .= ", <a href=editproducts.cgi>components</a>";
|
|
$html .= ", <a href=editattachstatuses.cgi><NOBR>attachment statuses</NOBR></a>"
|
|
if Param('useattachmenttracker');
|
|
}
|
|
if (UserInGroup("creategroups")) {
|
|
$html .= ", <a href=editgroups.cgi>groups</a>";
|
|
}
|
|
if (UserInGroup("editkeywords")) {
|
|
$html .= ", <a href=editkeywords.cgi>keywords</a>";
|
|
}
|
|
if (UserInGroup("tweakparams")) {
|
|
$html .= " | <a href=sanitycheck.cgi><NOBR>Sanity check</NOBR></a>";
|
|
}
|
|
|
|
$html .= " | <NOBR><a href=relogin.cgi>Log out</a> $::COOKIE{'Bugzilla_login'}</NOBR>";
|
|
$html .= "</TD></TR>";
|
|
|
|
#begin preset queries
|
|
my $mybugstemplate = Param("mybugstemplate");
|
|
my %substs;
|
|
$substs{'userid'} = url_quote($::COOKIE{"Bugzilla_login"});
|
|
if (!defined $::anyvotesallowed) {
|
|
GetVersionTable();
|
|
}
|
|
$html .= "</TR><TR>";
|
|
$html .= "<TD>Preset Queries: </TD>";
|
|
$html .= "<TD colspan=3>\n";
|
|
if ($mybugslink) {
|
|
my $mybugsurl = PerformSubsts($mybugstemplate, \%substs);
|
|
$html = $html . "<A HREF='$mybugsurl'><NOBR>My bugs</NOBR></A>";
|
|
}
|
|
SendSQL("SELECT name FROM namedqueries " .
|
|
"WHERE userid = $userid AND linkinfooter");
|
|
my $anynamedqueries = 0;
|
|
while (MoreSQLData()) {
|
|
my ($name) = (FetchSQLData());
|
|
if ($anynamedqueries || $mybugslink) { $html .= " | " }
|
|
$anynamedqueries = 1;
|
|
$html .= "<A HREF=\"buglist.cgi?&cmdtype=runnamed&namedcmd=" .
|
|
url_quote($name) . "\"><NOBR>$name</NOBR></A>";
|
|
}
|
|
$html .= "</TR>\n<TR>";
|
|
} else {
|
|
$html .= "</TD><TD> </TD><TD valign=middle align=right>\n";
|
|
$html .=
|
|
" <a href=\"createaccount.cgi\"><NOBR>New account</NOBR></a>\n";
|
|
$html .=
|
|
" | <NOBR><a href=query.cgi?GoAheadAndLogIn=1>Log in</a></NOBR>";
|
|
$html .= "</TD></TR>";
|
|
}
|
|
$html .= "</FORM>\n";
|
|
$html .= "</TABLE>";
|
|
return $html;
|
|
}
|
|
|
|
############# Live code below here (that is, not subroutine defs) #############
|
|
|
|
|
|
$| = 1;
|
|
|
|
# Uncommenting this next line can help debugging.
|
|
# print "Content-type: text/html\n\nHello mom\n";
|
|
|
|
# foreach my $k (sort(keys %ENV)) {
|
|
# print "$k $ENV{$k}<br>\n";
|
|
# }
|
|
|
|
if (defined $ENV{"REQUEST_METHOD"}) {
|
|
if ($ENV{"REQUEST_METHOD"} eq "GET") {
|
|
if (defined $ENV{"QUERY_STRING"}) {
|
|
$::buffer = $ENV{"QUERY_STRING"};
|
|
} else {
|
|
$::buffer = "";
|
|
}
|
|
ProcessFormFields $::buffer;
|
|
} else {
|
|
if (exists($ENV{"CONTENT_TYPE"}) && $ENV{"CONTENT_TYPE"} =~
|
|
m@multipart/form-data; boundary=\s*([^; ]+)@) {
|
|
ProcessMultipartFormFields($1);
|
|
$::buffer = "";
|
|
} else {
|
|
read STDIN, $::buffer, $ENV{"CONTENT_LENGTH"} ||
|
|
die "Couldn't get form data";
|
|
ProcessFormFields $::buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (defined $ENV{"HTTP_COOKIE"}) {
|
|
foreach my $pair (split(/;/, $ENV{"HTTP_COOKIE"})) {
|
|
$pair = trim($pair);
|
|
if ($pair =~ /^([^=]*)=(.*)$/) {
|
|
$::COOKIE{$1} = $2;
|
|
} else {
|
|
$::COOKIE{$pair} = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
1;
|