зеркало из https://github.com/mozilla/gecko-dev.git
1213 строки
40 KiB
Perl
Executable File
1213 строки
40 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.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 Despot Account Administration 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>
|
|
# Zach Lipton <zach@zachlipton.org>
|
|
|
|
use strict;
|
|
use diagnostics;
|
|
|
|
|
|
# a few configurables
|
|
#
|
|
my $despotOwnerMailTo = "mailto:sysadmins\@mozilla.org";
|
|
my $despotOwner = "sysadmins";
|
|
|
|
# Shut up misguided -w warnings about "used only once". "use vars" just
|
|
# doesn't work for me.
|
|
|
|
sub sillyness {
|
|
my $zz;
|
|
$zz = $F::state;
|
|
$zz = $F::description;
|
|
$zz = $F::id;
|
|
$zz = $F::newpassword2;
|
|
$zz = $F::files;
|
|
$zz = $F::partition;
|
|
$zz = $F::doclinks;
|
|
$zz = $F::file;
|
|
$zz = $F::newsgroups;
|
|
}
|
|
|
|
|
|
use CGI::Carp qw(fatalsToBrowser);
|
|
|
|
use CGI qw(:standard :html3);
|
|
|
|
require 'utils.pl';
|
|
|
|
$::POSTTYPE = "Post";
|
|
|
|
$| = 1;
|
|
|
|
$::despot = param("_despot");
|
|
if (!defined $::despot) {
|
|
$::despot = 0;
|
|
}
|
|
|
|
# Whether or not the HTTP response headers and beginning of the HTML page has
|
|
# already been returned to the client. Necessary because some user actions
|
|
# (f.e. AddUser) result in PrintHeader() getting called twice.
|
|
$::header_done = 0;
|
|
|
|
$::disabled = "";
|
|
#$::disabled = "Despot is temporarily disabled. Please try again later.";
|
|
if ($::disabled) {
|
|
PrintHeader();
|
|
print h1("Despot -- access control for mozilla.org.");
|
|
print p($::disabled);
|
|
exit;
|
|
}
|
|
|
|
if (!param()) {
|
|
PrintHeader();
|
|
print h1("Despot -- access control for mozilla.org.");
|
|
if (!exists $ENV{"HTTPS"} || lc($ENV{"HTTPS"}) ne "on") {
|
|
my $fixedurl = "https://$ENV{'SERVER_NAME'}$ENV{'SCRIPT_NAME'}";
|
|
print b("<font color=red>If possible, please use the " .
|
|
a({href=>$fixedurl}, "secure version of this form") .
|
|
".</font>");
|
|
}
|
|
print h2("Login, please.");
|
|
print img({-align=>"right",-width=>72,-height=>84,-src=>"handcuff.gif"});
|
|
print p("To manage mozilla users, or to change your mozilla.org " .
|
|
"password, you must first log in.");
|
|
PrintLoginForm();
|
|
exit;
|
|
}
|
|
|
|
import_names("F"); # Makes all form values available as F::.
|
|
|
|
# $F::debug = 1;
|
|
|
|
use Mysql;
|
|
|
|
$::db = Mysql->Connect("localhost", "mozusers", "despot")
|
|
|| die "Can't connect to database server";
|
|
|
|
|
|
$::skiptrailer = 0;
|
|
|
|
if (defined $F::command) {
|
|
my $cmd = $F::command;
|
|
Authenticate() unless grep($cmd eq $_, qw(FindPartition));
|
|
param("command", "");
|
|
PrintHeader() if $cmd eq "MainMenu";
|
|
eval "$cmd()";
|
|
if ($@ ne "") {
|
|
die "Error executing $cmd -- $@";
|
|
}
|
|
} else {
|
|
Authenticate();
|
|
PrintHeader();
|
|
MainMenu();
|
|
}
|
|
|
|
|
|
if (!$::skiptrailer) {
|
|
print hr();
|
|
print MyForm("MainMenu") . submit("Main menu") . end_form();
|
|
}
|
|
|
|
|
|
my $query = Query("select needed from syncneeded where needed=1");
|
|
my @row = $query->fetchrow();
|
|
if ($row[0]) {
|
|
print hr();
|
|
print p("Updating external machines...");
|
|
print "<PRE>";
|
|
if (!open(DOSYNC, "./syncit.pl -user $F::loginname|")) {
|
|
print p("Can't do sync (error $?). Please send mail to " .
|
|
a({href=>$despotOwnerMailTo}, $despotOwner) . ".");
|
|
} else {
|
|
while (<DOSYNC>) {
|
|
if ($::despot) {
|
|
s/\&/\&/g;
|
|
s/</\</g;
|
|
s/>/\>/g;
|
|
print $_;
|
|
}
|
|
}
|
|
close(DOSYNC);
|
|
}
|
|
print "</PRE>";
|
|
print p("... update completed.");
|
|
}
|
|
|
|
|
|
|
|
sub Authenticate {
|
|
my $query = Query("select passwd,despot,neednewpassword,id,disabled from users where email = '$F::loginname'");
|
|
my @row = $query->fetchrow();
|
|
if (!@row || !checkpassword($F::loginpassword, $row[0])) {
|
|
PrintHeader();
|
|
print h1("Authentication Failed");
|
|
print p("I can't figure out who you are. Either your email address/password " .
|
|
"were entered incorrectly, or you didn't enter them. In any case, I " .
|
|
"need to know who you are before I let you do what you wanted to do, " .
|
|
"so please enter your email address and password now.");
|
|
if ($F::loginname && $F::loginname !~ /@/) {
|
|
print p("Note! You must type in your full e-mail address, including the '\@'.");
|
|
}
|
|
PrintLoginForm();
|
|
exit;
|
|
}
|
|
|
|
my $passwd;
|
|
my $disabled;
|
|
($passwd,$::candespot,$::neednewpassword,$::loginid,$disabled) = @row;
|
|
|
|
if ($disabled eq "Yes") {
|
|
Punt("Your account has been disabled.");
|
|
}
|
|
|
|
if ($::candespot eq "Yes") {
|
|
$::candespot = 1;
|
|
} else {
|
|
$::candespot = 0;
|
|
$::despot = 0;
|
|
}
|
|
|
|
if ($::neednewpassword eq "Yes") {
|
|
$::neednewpassword = 1;
|
|
} else {
|
|
$::neednewpassword = 0;
|
|
}
|
|
|
|
}
|
|
|
|
sub PrintHeader {
|
|
return if $::header_done;
|
|
|
|
print header();
|
|
|
|
my $bg = "white";
|
|
my $extra = "";
|
|
if ($::despot) {
|
|
$bg = "lightgreen";
|
|
$extra = " (Wonder-twin powers -- activate!)";
|
|
}
|
|
|
|
print start_html(-Title=>"Despot -- configure mozilla users$extra",
|
|
-BGCOLOR=>$bg);
|
|
|
|
$::header_done = 1;
|
|
}
|
|
|
|
|
|
sub PrintLoginForm {
|
|
# CGI seems to be misbehaving; creating the form tag manually as a workaround.
|
|
print qq|<form method="$::POSTTYPE" enctype="application/x-www-form-urlencoded">\n|;
|
|
# print start_form(-method=>$::POSTTYPE);
|
|
print table(Tr(th({-align=>"right"}, "Email address:"),
|
|
td(textfield(-name=>"loginname",
|
|
-size=>20))),
|
|
Tr(th({-align=>"right"}, "Password:"),
|
|
td(password_field(-name=>"loginpassword",
|
|
-size=>20))));
|
|
print submit(-name=>"Log in");
|
|
|
|
# Add all submitted values to the form (except email address and password,
|
|
# which we want them to retype) as hidden fields so the user can immediately
|
|
# do whatever they wanted to do upon logging in.
|
|
foreach my $field (param()) {
|
|
next if grep($field eq $_, qw(loginname loginpassword));
|
|
print hidden(-name=>$field, -default=>param($field));
|
|
}
|
|
|
|
print hr();
|
|
print p("If you think you should be able to use this system, but you haven't been issued a login, please send mail to " .
|
|
a({href=>$despotOwnerMailTo}, $despotOwner) . ".");
|
|
# print p("If you do not yet have a mozilla.org account, or you have one " .
|
|
# "but have forgotten your password, please fill in your e-mail " .
|
|
# "address above, and click <nobr>here: " .
|
|
# submit({name=>"mailMePassword"}, "Email me a password") . "</nobr>");
|
|
|
|
print end_form();
|
|
}
|
|
|
|
sub MainMenu() {
|
|
print h1("Despot -- main menu");
|
|
print table({-width=>"100%"},
|
|
Tr(td(h3("What would you like to do?")) .
|
|
td({-align=>"right"}, a({-href=>"help.html"},
|
|
"Introduction to Despot"))));
|
|
|
|
my @list = ();
|
|
push(@list, li(MyForm("ViewAccount") .
|
|
submit("View info about your account") .end_form()));
|
|
push(@list, li(MyForm("ChangePassword") .
|
|
submit("Change your password") . end_form()));
|
|
push(@list, li(MyForm("ListPartitions") .
|
|
submit("List all the partitions") .end_form()));
|
|
# my $query = Query("select partitionid,partitions.name,repositories.name,repositories.id from members,partitions,repositories where userid=$::loginid and class != 'Member' and partitions.id=members.partitionid and repositories.id=partitions.repositoryid");
|
|
# my @row;
|
|
# while (@row = $query->fetchrow()) {
|
|
# my ($partid,$partname,$repname,$repid) = (@row);
|
|
# my $title = "Edit partition '$partname' ($repname)";
|
|
# param("partitionid", $partid);
|
|
# push(@list,
|
|
# li(MyForm("EditPartition") . submit($title) .
|
|
# hidden(-name=>"partitionid") . end_form()));
|
|
# }
|
|
|
|
my $query = Query("select id,name from repositories order by name");
|
|
my @vals = ();
|
|
my %labels;
|
|
while (my @row = $query->fetchrow()) {
|
|
my $v = $row[0];
|
|
push(@vals, $v);
|
|
$labels{$v} = $row[1];
|
|
}
|
|
my $radio = popup_menu(-name=>"repid",
|
|
"-values"=>\@vals,
|
|
-default=>$vals[0],
|
|
-labels=>\%labels);
|
|
push(@list, li(MyForm("FindPartition") .
|
|
submit("Find partition containing file") .
|
|
"named<nobr> " .
|
|
textfield(-name=>"file", -size=>60) .
|
|
"</nobr> in <nobr>repository " . $radio .
|
|
end_form()));
|
|
|
|
if ($::candespot) {
|
|
param("_despot", !$::despot);
|
|
my $str = $::despot ? "Disable" : "Enable";
|
|
push(@list,
|
|
li(MyForm("MainMenu") . submit("$str Despot Powers") .
|
|
end_form()));
|
|
param("_despot", $::despot);
|
|
if ($::despot) {
|
|
my @l2 = ();
|
|
my $opts = popup_menu(-name=>"matchtype",
|
|
"-values"=>["regexp", "not regexp"],
|
|
-default=>"regexp",
|
|
-labels=>{"regexp"=>"Matching regexp",
|
|
"not regexp"=>"Not matching regexp"});
|
|
push(@l2,
|
|
li("<nobr>" .
|
|
MyForm("ListUsers") . submit("List all users") .
|
|
$opts .
|
|
textfield(-name=>"match", -size=>50) .
|
|
"</nobr>" .
|
|
end_form()));
|
|
push(@l2,
|
|
li(MyForm("AddUser") . submit("Add or edit user") .
|
|
"with email<nobr> " .
|
|
textfield(-name=>"email", -size=>25) . "</nobr>" .
|
|
end_form()));
|
|
push(@l2,
|
|
li(MyForm("AddPartition") . submit("Create or edit partition")
|
|
. " named<nobr> " .
|
|
textfield(-name=>"partition", -size=>24) . "</nobr>" .
|
|
"</nobr> in <nobr>repository " . $radio .
|
|
end_form()));
|
|
push(@list, "Despot commands:" . ul(\@l2));
|
|
}
|
|
}
|
|
|
|
|
|
print ul(\@list);
|
|
|
|
if ($::neednewpassword) {
|
|
print "<font color=red>Your account has been frozen. To make it usable, you must change your password.</font>";
|
|
}
|
|
|
|
$::skiptrailer = 1;
|
|
}
|
|
|
|
|
|
sub AddUser() {
|
|
EnsureDespot();
|
|
if ($F::email eq "") {
|
|
Punt("You must enter an email address.");
|
|
}
|
|
my $email = $F::email;
|
|
my $query;
|
|
my @row;
|
|
if ($email !~ /^[^@, ]*@[^@, ]*\.[^@, ]*$/) {
|
|
Punt("Email addresses must contain exactly one '\@', and at least one '.' after the \@, and may not contain any commas or spaces.");
|
|
}
|
|
|
|
my $q = SqlQuote($email);
|
|
$query = Query("select count(*) from users where email='$q'");
|
|
@row = $query->fetchrow();
|
|
|
|
if ($row[0] < 1) {
|
|
my $p = "";
|
|
my $realname = "";
|
|
my $plain = pickrandompassword();
|
|
$p = cryptit($plain);
|
|
my $feedback = "'" . tt($plain) . "'";
|
|
my $mailwords = "of '$plain'";
|
|
$p = $::db->quote($p);
|
|
$realname = $::db->quote($realname);
|
|
Query("insert into users (email,passwd,neednewpassword,realname) values ('$q',$p,'Yes',$realname)");
|
|
PrintHeader();
|
|
print p("New account created. Password initialized to $feedback; " .
|
|
"please " .
|
|
a({href=>"mailto:$email?subject=Change your mozilla.org password&body=Your new mozilla.org account has been created. It initially has a%0apassword $mailwords. Please go to http://despot.mozilla.org/despot.cgi%0aand change your password as soon as possible. You won't actually be%0aable to use it for anything until you do."},
|
|
"send mail") .
|
|
" and have the user change the password!");
|
|
print hr();
|
|
}
|
|
EditUser();
|
|
}
|
|
|
|
|
|
sub EditUser() {
|
|
EnsureDespot();
|
|
PrintHeader();
|
|
print h1("Edit a user");
|
|
my $q = SqlQuote($F::email);
|
|
my $query = Query("select * from users where email='$q'");
|
|
my @row;
|
|
@row = $query->fetchrow();
|
|
my $query2 = Query("show columns from users");
|
|
my @list;
|
|
my @desc;
|
|
for (my $i=0 ; $i<@row ; $i++) {
|
|
@desc = $query2->fetchrow();
|
|
my $line = th({-align=>"right"}, "$desc[0]:");
|
|
if ($desc[0] ne ($query->name)[$i]) {
|
|
die "show columns in different order than select???";
|
|
}
|
|
$_ = $desc[1];
|
|
if (/^varchar/ || /int\([0-9]*\)$/) {
|
|
if ($desc[0] eq "voucher") {
|
|
$row[$i] = IdToEmail($row[$i]);
|
|
}
|
|
$line .= td(textfield(-name=>($query->name)[$i],
|
|
-default=>$row[$i],
|
|
-size=>50,
|
|
-override=>1));
|
|
} elsif (/^enum/) {
|
|
s/^enum\(//;
|
|
s/\)$//;
|
|
my @values = split(/,/);
|
|
my $opts = "";
|
|
for (my $j=0 ; $j<@values ; $j++) {
|
|
$values[$j] =~ s/^\'//;
|
|
$values[$j] =~ s/\'$//;
|
|
}
|
|
$line .= td(radio_group(-name=>$desc[0], "-values"=>\@values,
|
|
-default=>$row[$i]));
|
|
}
|
|
$line .= hidden("orig_$desc[0]", $row[$i]);
|
|
|
|
|
|
|
|
push(@list,Tr($line));
|
|
}
|
|
print MyForm("ChangeUser");
|
|
print table(@list);
|
|
print submit("Save changes");
|
|
print end_form();
|
|
print MyForm("GeneratePassword") . hidden(-name=>"email");
|
|
print submit("Generate a new random password for this user");
|
|
print end_form();
|
|
# DeleteUser is currently a bad idea, as it leaves dangling pointers.
|
|
# See bugzilla.mozilla.org bug 17589
|
|
#
|
|
# print MyForm("DeleteUser") . hidden(-name=>"email");
|
|
# print submit("Delete user");
|
|
# print end_form();
|
|
}
|
|
|
|
# DeleteUser is currently a bad idea, as it leaves dangling pointers.
|
|
# See bugzilla.mozilla.org bug 17589
|
|
#
|
|
#sub DeleteUser() {
|
|
# my $q = SqlQuote($F::email);
|
|
# Query("delete from users where email = '$q'");
|
|
# Query("insert into syncneeded (needed) values (1)");
|
|
# PrintHeader();
|
|
# print h1("OK, $F::email is gone.");
|
|
# print hr();
|
|
# MainMenu();
|
|
#}
|
|
|
|
sub ChangeUser() {
|
|
foreach my $field ("email") {
|
|
my $value = param($field);
|
|
if ($value ne param("orig_$field")) {
|
|
my $query = Query("select count(*) from users where $field = '" .
|
|
SqlQuote($value) . "'");
|
|
my @row;
|
|
@row = $query->fetchrow();
|
|
if ($row[0] > 0) {
|
|
Punt("Can't change $field to '$value'; already used.");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
my $query = Query("show columns from users");
|
|
my @list;
|
|
my @row;
|
|
while (@row = $query->fetchrow()) {
|
|
my $old = param("orig_$row[0]");
|
|
my $new = param($row[0]);
|
|
if ($old ne $new) {
|
|
if ($row[0] eq "voucher") {
|
|
$old = EmailToId($old);
|
|
$new = EmailToId($new);
|
|
}
|
|
Query("insert into changes (email,field,oldvalue,newvalue,who) values (" .
|
|
$::db->quote($F::orig_email) . ",'$row[0]'," .
|
|
$::db->quote($old) . "," .
|
|
$::db->quote($new) . ",'" .
|
|
$F::loginname . "')");
|
|
}
|
|
push(@list, "$row[0] = '" . SqlQuote($new) . "'");
|
|
}
|
|
my $qstr = "update users set " . join(",", @list) . " where email='" .
|
|
SqlQuote($F::orig_email) . "'";
|
|
Query($qstr);
|
|
PrintHeader();
|
|
print h1("OK, record for $F::email has been updated.");
|
|
print hr();
|
|
MainMenu();
|
|
Query("insert into syncneeded (needed) values (1)");
|
|
}
|
|
|
|
sub GeneratePassword {
|
|
my $email = $F::email;
|
|
my $plain = pickrandompassword();
|
|
my $p = cryptit($plain);
|
|
Query("update users set passwd = " . $::db->quote($p) . ", neednewpassword='Yes' where email=" .
|
|
$::db->quote($email));
|
|
PrintHeader();
|
|
print h1("OK, new password generated.");
|
|
print "$email now has a new password of '" . tt($plain) . "'. ";
|
|
print "Please " .
|
|
a({href=>"mailto:$email?subject=Change your mozilla.org password&body=Your mozilla.org account now has a password of '$plain'. Please go to%0ahttp://despot.mozilla.org/despot.cgi and change your password as soon as%0apossible. You won't actually be able to use your mozilla.org account%0afor anything until you do."},
|
|
"send mail") .
|
|
" to have the user change the password!";
|
|
Query("insert into syncneeded (needed) values (1)");
|
|
}
|
|
|
|
|
|
|
|
|
|
sub ListPartitions () {
|
|
PrintHeader();
|
|
print p("If you're wondering what a 'partition' is, " .
|
|
a({-href=>"help.html#partition"}, "read this") . ".");
|
|
ListSomething("partitions", "id", "ListPartitions", "EditPartition", "name",
|
|
"name,repositoryid,state,description", "",
|
|
{"branchid"=>["branches.name",
|
|
"branches.id=partitions.branchid"],
|
|
"repositoryid"=>["repositories.name",
|
|
"repositories.id=partitions.repositoryid"]
|
|
},
|
|
"");
|
|
}
|
|
|
|
|
|
sub ListUsers() {
|
|
EnsureDespot();
|
|
|
|
PrintHeader();
|
|
# ListSomething("users", "email", "ListUsers", "EditUser", "email", "email,realname,gila_group,cvs_group", "users as u2", {"voucher"=>"u2.email,u2.id=users.voucher"});
|
|
my $wherepart = "";
|
|
if ($F::match ne "") {
|
|
$wherepart = "email $F::matchtype " . $::db->quote($F::match);
|
|
}
|
|
ListSomething("users", "email", "ListUsers", "EditUser", "email", "email,realname,gila_group,cvs_group", "users as u2", {"voucher"=>["if(users.voucher=0,'NONE',u2.email)", "u2.id=if(users.voucher=0,users.id,users.voucher)", "u2"]}, $wherepart);
|
|
}
|
|
|
|
|
|
sub ListSomething {
|
|
my ($tablename,$idcolumn,$listprocname,$editprocname,$defaultsortorder,
|
|
$defaultcolumns,$extratables,$columndefs,$extrawhere) = (@_);
|
|
|
|
|
|
my %columnremap;
|
|
my %columnwhere;
|
|
my %columntable;
|
|
|
|
while (my ($key,$value) = each %$columndefs) {
|
|
($columnremap{$key}, $columnwhere{$key}, $columntable{$key}) = (@$value);
|
|
if (!defined $columntable{$key}) {
|
|
$columntable{$key} = $columnremap{$key};
|
|
$columntable{$key} =~ s/\..*$//;
|
|
}
|
|
}
|
|
|
|
|
|
print h1("List of $tablename");
|
|
my $sortorder = $defaultsortorder;
|
|
if (defined $F::sortorder) {
|
|
$sortorder = $F::sortorder;
|
|
}
|
|
|
|
my $query = Query("show columns from $tablename");
|
|
my @allcols = ();
|
|
my @cols = ();
|
|
while (@row = $query->fetchrow()) {
|
|
push(@allcols, $row[0]);
|
|
}
|
|
|
|
my $hiddencols = "";
|
|
if (defined @F::showcolumns) {
|
|
@cols = @F::showcolumns;
|
|
foreach my $c (@cols) {
|
|
$hiddencols .= hidden(-name=>"showcolumns",
|
|
-default=>$c,
|
|
-override=>1);
|
|
}
|
|
}
|
|
|
|
if (0 == @cols) {
|
|
@cols = split(/,/, $defaultcolumns);
|
|
}
|
|
|
|
my $rtext = th();
|
|
my @list = ();
|
|
my @emaillist = ();
|
|
my $hiddenbits = "";
|
|
if (defined $F::matchtype) {
|
|
$hiddenbits .= hidden(-name=>"matchtype");
|
|
}
|
|
if (defined $F::match) {
|
|
$hiddenbits .= hidden(-name=>"match");
|
|
}
|
|
|
|
foreach my $c (@cols) {
|
|
if ($c eq $sortorder) {
|
|
$rtext .= th({-valign=>"top"}, "sorted by<br>$c");
|
|
} else {
|
|
$rtext .= th({-valign=>"top"},
|
|
MyForm($listprocname) .
|
|
submit($c) .
|
|
$hiddenbits .
|
|
$hiddencols .
|
|
hidden(-name=>"sortorder",
|
|
-default=>$c,
|
|
-override=>1) .
|
|
end_form());
|
|
}
|
|
if (!defined $columnremap{$c}) {
|
|
$columnremap{$c} = "$tablename.$c";
|
|
$columntable{$c} = $tablename;
|
|
}
|
|
}
|
|
|
|
push(@list, $rtext);
|
|
my @mungedcols;
|
|
my %usedtables;
|
|
my $wherepart = "";
|
|
if (defined $extrawhere && $extrawhere ne "") {
|
|
$wherepart = " where " . $extrawhere;
|
|
}
|
|
foreach my $c (@cols) {
|
|
my $t = $columnremap{$c};
|
|
push(@mungedcols,$t);
|
|
$usedtables{$columntable{$c}} = 1;
|
|
if (defined $columnwhere{$c}) {
|
|
if ($wherepart eq "") {
|
|
$wherepart = " where ";
|
|
} else {
|
|
$wherepart .= " and ";
|
|
}
|
|
$wherepart .= $columnwhere{$c};
|
|
}
|
|
}
|
|
if ($extratables ne "") {
|
|
foreach my $i (split(/,/, $extratables)) {
|
|
$usedtables{$i} = 1;
|
|
if ($i =~ m/as (.*)$/) {
|
|
if (exists $usedtables{$1}) {
|
|
delete $usedtables{$1};
|
|
} else {
|
|
delete $usedtables{$i};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
my $orderby = $columnremap{$sortorder};
|
|
if ($orderby =~ /\(/) {
|
|
# The sort order is too complicated for stupid SQL. Pick something
|
|
# simpler.
|
|
$orderby = "$tablename.$sortorder";
|
|
}
|
|
$query = Query("select $tablename.$idcolumn," . join(",", @mungedcols) . " from " . join(",", keys(%usedtables)) . $wherepart . " order by $orderby");
|
|
while (@row = $query->fetchrow()) {
|
|
my $i;
|
|
for ($i=1 ; $i<@row ; $i++) {
|
|
if (!defined $row[$i]) {
|
|
$row[$i] = "";
|
|
}
|
|
}
|
|
my $thisid = shift(@row);
|
|
push(@emaillist, $thisid);
|
|
push(@list, td(MyForm($editprocname) . hidden($idcolumn, $thisid) .
|
|
submit("Edit") . end_form()) .
|
|
td(\@row));
|
|
}
|
|
print table(Tr(\@list));
|
|
if ($idcolumn eq "email") {
|
|
my $mailtolist = "mailto:" . join(',', @emaillist);
|
|
print p(a({href=>$mailtolist},"Send mail to all users"));
|
|
}
|
|
print hr();
|
|
print MyForm($listprocname);
|
|
print submit("Redisplay with the following columns:") . br();
|
|
print checkbox_group(-name=>"showcolumns",
|
|
"-values"=>\@allcols,
|
|
-default=>\@cols,
|
|
-linebreak=>'true');
|
|
print hidden(-name=>"sortorder", -default=>$sortorder, -override=>1);
|
|
print $hiddenbits;
|
|
print end_form();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
sub ViewAccount {
|
|
my $query = Query("select * from users where email='$F::loginname'");
|
|
my @row;
|
|
@row = $query->fetchrow();
|
|
my $query2 = Query("show columns from users");
|
|
my @list;
|
|
my @desc;
|
|
my $userid;
|
|
for (my $i=0 ; $i<@row ; $i++) {
|
|
@desc = $query2->fetchrow();
|
|
my $line = th({-align=>"right"}, "$desc[0]:");
|
|
if ($desc[0] ne ($query->name)[$i]) {
|
|
die "show columns in different order than select???";
|
|
}
|
|
if ($desc[0] eq "voucher") {
|
|
$row[$i] = IdToEmail($row[$i]);
|
|
}
|
|
$line .= td($row[$i]);
|
|
push(@list,Tr($line));
|
|
}
|
|
PrintHeader();
|
|
print table(@list);
|
|
$query = Query("select partitions.id,repositories.name,partitions.name,class from members,repositories,partitions where members.userid=$::loginid and partitions.id=members.partitionid and repositories.id=partitions.repositoryid order by class");
|
|
while (@row = $query->fetchrow()) {
|
|
my ($partid, $repname,$partname,$class) = (@row);
|
|
param("partitionid", $partid);
|
|
print MyForm("EditPartition") . hidden("partitionid") .
|
|
submit("$class of $partname ($repname)") . end_form();
|
|
}
|
|
|
|
if ($::despot) {
|
|
print MyForm("EditUser") . hidden("email", $F::loginname) .
|
|
submit("Edit your account") .
|
|
"(Since you're a despot, you can do this)" .
|
|
end_form();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
sub FindPartition {
|
|
my $repid = $F::repid;
|
|
my $file = $F::file;
|
|
# Excludes the mozilla-toplevel module which would otherwise always match.
|
|
my $query = Query("select files.pattern,partitions.name,partitions.id from files,partitions where partitions.id=files.partitionid and partitions.repositoryid=$repid and files.pattern != 'mozilla/%'");
|
|
my @matches = ();
|
|
while (@row = $query->fetchrow()) {
|
|
my ($pattern,$name,$id) = (@row);
|
|
if (FileMatches($file, $pattern) || FileMatches($pattern, $file)) {
|
|
push(@matches, { 'name' => $name, 'id' => $id, 'pattern' => $pattern });
|
|
}
|
|
elsif ($file !~ /^mozilla(-org)?\//
|
|
&& (FileMatches("mozilla/$file", $pattern)
|
|
|| FileMatches($pattern, "mozilla/$file")
|
|
|| FileMatches("mozilla-org/$file", $pattern)
|
|
|| FileMatches($pattern, "mozilla-org/$file")))
|
|
{
|
|
push(@matches, { 'name' => $name, 'id' => $id, 'pattern' => $pattern });
|
|
}
|
|
}
|
|
|
|
if (scalar(@matches) > 0) {
|
|
# The "view" parameter means the request came from the "look up owners of file"
|
|
# form on the owners page, so we should redirect the user to the anchor on that
|
|
# page for a single match.
|
|
if (scalar(@matches) == 1 && param("view")) {
|
|
print "Location: http://www.mozilla.org/owners.html#" . name_to_id_token($matches[0]->{name}) . "\n\n";
|
|
exit;
|
|
}
|
|
PrintHeader();
|
|
print h1("Searching for partitions matching $file ...");
|
|
foreach my $match (@matches) {
|
|
if (param("view")) {
|
|
# This should display modules just as they are displayed
|
|
# on owners.html, but that code is embedded into syncit.pl.
|
|
print qq|Module: <a href="http://www.mozilla.org/owners.html#| .
|
|
name_to_id_token($match->{name}) . qq|">$match->{name}</a>
|
|
(matches pattern $match->{pattern})<br>\n|;
|
|
}
|
|
else {
|
|
print MyForm("EditPartition") .
|
|
hidden("partitionid", $match->{id}) .
|
|
submit($match->{name}) .
|
|
"(matches pattern $match->{pattern})" .
|
|
end_form();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
PrintHeader();
|
|
print h1("Searching for partitions matching $file ...");
|
|
print "No partitions found.";
|
|
}
|
|
|
|
if (param("view")) {
|
|
# No need for the footer with the "Main Menu" button
|
|
# for "look up owners of file" users.
|
|
print "</body></html>\n";
|
|
exit;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub AddPartition {
|
|
EnsureDespot();
|
|
my $partition = $F::partition;
|
|
if ($partition eq "") {
|
|
Punt("You must enter a partition name.");
|
|
}
|
|
my $repid = $F::repid;
|
|
my $query;
|
|
my @row;
|
|
|
|
$query = Query("select id from partitions where repositoryid=$repid and name = '$partition'");
|
|
|
|
if (!(@row = $query->fetchrow())) {
|
|
Query("insert into partitions (name,repositoryid,state,branchid) values ('$partition',$repid,'Open',1)");
|
|
PrintHeader();
|
|
print p("New partition created.") . hr();
|
|
@row = Query("select LAST_INSERT_ID()")->fetchrow();
|
|
}
|
|
|
|
$F::partitionid = $row[0];
|
|
EditPartition();
|
|
}
|
|
|
|
sub EditPartition() {
|
|
my $partitionid;
|
|
if (defined $F::partitionid) {
|
|
$partitionid = $F::partitionid;
|
|
} else {
|
|
$partitionid = $F::id;
|
|
}
|
|
my $canchange = CanChangePartition($partitionid);
|
|
my $query = Query("select partitions.name,partitions.description,state,repositories.name,repositories.id,branches.name,newsgroups,doclinks from partitions,repositories,branches where partitions.id = $partitionid and repositories.id = repositoryid and branches.id = branchid");
|
|
@row = $query->fetchrow();
|
|
|
|
my ($partname,$partdesc,$state,$repname,$repid,$branchname,$newsgroups,
|
|
$doclinks) = (@row);
|
|
PrintHeader();
|
|
print h1(($canchange ? "Edit" : "View") . " partition -- $partname");
|
|
if (!$canchange) {
|
|
print p(b("You can't change anything here!") .
|
|
" You can look, but you won't be permitted to touch.");
|
|
}
|
|
|
|
if ($partname eq "default") {
|
|
print "<font color=red>This is the magic default partition. It gets consulted on any file that does not match any other partition.</font>";
|
|
}
|
|
|
|
my @list;
|
|
|
|
push(@list, Tr(th("Repository:") . td($repname)));
|
|
|
|
push(@list, Tr(th("Description:") . td(textarea(-name=>"description",
|
|
-default=>$partdesc,
|
|
-rows=>4,
|
|
-columns=>50))));
|
|
|
|
push(@list, Tr(th("Newsgroups:"),
|
|
td(textfield(-name=>'newsgroups',
|
|
-default=>$newsgroups,
|
|
-size=>30,
|
|
-override=>1))));
|
|
|
|
push(@list, Tr(th("Doc links:"),
|
|
td(textarea(-name=>'doclinks',
|
|
-default=>$doclinks,
|
|
-rows=>10,
|
|
-columns=>50,
|
|
-override=>1))));
|
|
|
|
|
|
push(@list,
|
|
Tr(th(a({-href=>"help.html#state"},"State:")) .
|
|
td(radio_group(-name=>'state',
|
|
"-values"=>['Open', 'Restricted', 'Closed'],
|
|
-default=>$state,
|
|
-linebreak=>'false'))));
|
|
|
|
push(@list,
|
|
Tr(th(a({-href=>"help.html#branch"}, "Branch:")),
|
|
td(textfield(-name=>'branchname',
|
|
-default=>$branchname,
|
|
-size=>30,
|
|
-override=>1))));
|
|
|
|
$query = Query("select pattern from files where partitionid=$partitionid order by pattern");
|
|
push(@list, CreateListRow("files", $query));
|
|
foreach my $class ("Owner", "Peer", "Member") {
|
|
$query = Query("select users.email,users.disabled from members,users where members.partitionid = $partitionid and members.class = '$class' and users.id = members.userid order by users.email");
|
|
push(@list, CreateListRow($class, $query));
|
|
}
|
|
|
|
|
|
param("partitionid", $partitionid);
|
|
param("repid", $repid);
|
|
param("repname", $repname);
|
|
my $h = hidden(-name=>"partitionid") .
|
|
hidden(-name=>"repid") .
|
|
hidden(-name=>"repname");
|
|
|
|
print MyForm("ChangePartition");
|
|
print table(@list);
|
|
if ($canchange) {
|
|
print submit("Save changes");
|
|
print $h;
|
|
print end_form();
|
|
print MyForm("DeletePartition");
|
|
print submit("Delete partition");
|
|
print $h;
|
|
}
|
|
print end_form();
|
|
}
|
|
|
|
sub ChangePartition {
|
|
my $query;
|
|
my @row;
|
|
|
|
EnsureCanChangePartition($F::partitionid);
|
|
|
|
Query("lock tables partitions write,branches write,files write,members write,users read");
|
|
|
|
# Sanity checking first...
|
|
|
|
my @files = split(/\n/, $F::files);
|
|
|
|
my $q = SqlQuote($F::branchname);
|
|
$query = Query("select files.pattern,partitions.name from " .
|
|
"files,partitions,branches where files.partitionid != " .
|
|
"$F::partitionid and partitions.id=files.partitionid " .
|
|
"and partitions.repositoryid=$F::repid and branches.id=" .
|
|
"partitions.branchid and branches.name='$q'");
|
|
while (@row = $query->fetchrow()) {
|
|
my $f1 = $row[0];
|
|
foreach my $f2 (@files) {
|
|
$f2 = trim($f2);
|
|
if ($f2 eq "") {
|
|
next;
|
|
}
|
|
if (FileMatches($f1, $f2) || FileMatches($f2, $f1)) {
|
|
Punt("File rule $f2 overlaps with existing rule in partition $row[1]");
|
|
}
|
|
}
|
|
}
|
|
|
|
my %idhash;
|
|
foreach my $class ("Owner", "Peer", "Member") {
|
|
my @names = split(/\n/, param($class));
|
|
foreach my $n (@names) {
|
|
$n =~ s/\(.*\)//g;
|
|
$n = trim($n);
|
|
if ($n eq "") {
|
|
next;
|
|
}
|
|
$query = Query("select id,${F::repname}_group from users where email = " . $::db->quote($n));
|
|
if (!(@row = $query->fetchrow())) {
|
|
Punt("$n is not an email address in the database.");
|
|
}
|
|
my ($userid,$group) = (@row);
|
|
if (exists $idhash{$n}) {
|
|
Punt("$n seems to be mentioned more than once. A given user can only have one class.");
|
|
}
|
|
$idhash{$n} = $userid;
|
|
if ($group eq "None") {
|
|
Punt("$n is not allowed to use the $F::repname repository at all, so it doesn't make sense to put this user into this partition.");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# And now actually update things.
|
|
|
|
$query = Query("select id from branches where name = " .
|
|
$::db->quote($F::branchname));
|
|
if (!(@row = $query->fetchrow())) {
|
|
Query("insert into branches (name) values (" .
|
|
$::db->quote($F::branchname) . ")");
|
|
$query = Query("select LAST_INSERT_ID()");
|
|
@row = $query->fetchrow();
|
|
}
|
|
my $branchid = $row[0];
|
|
Query("update partitions set description=" .
|
|
$::db->quote($F::description) .
|
|
", branchid=$branchid, state='$F::state', newsgroups=" .
|
|
$::db->quote($F::newsgroups) .
|
|
", doclinks=" .
|
|
$::db->quote($F::doclinks) .
|
|
" where id=$F::partitionid");
|
|
|
|
Query("delete from files where partitionid=$F::partitionid");
|
|
foreach my $f2 (@files) {
|
|
$f2 = trim($f2);
|
|
if ($f2 eq "") {
|
|
next;
|
|
}
|
|
Query("insert into files (partitionid,pattern) values ($F::partitionid," . $::db->quote($f2) . ")");
|
|
}
|
|
|
|
Query("delete from members where partitionid=$F::partitionid");
|
|
foreach my $class ("Owner", "Peer", "Member") {
|
|
my @names = split(/\n/, param($class));
|
|
foreach my $n (@names) {
|
|
$n =~ s/\(.*\)//g;
|
|
$n = trim($n);
|
|
if ($n eq "") {
|
|
next;
|
|
}
|
|
Query("insert into members(userid,partitionid,class) values ($idhash{$n},$F::partitionid,'$class')");
|
|
}
|
|
}
|
|
|
|
PrintHeader();
|
|
print h1("OK, the partition has been updated.");
|
|
print hr();
|
|
Query("unlock tables");
|
|
MainMenu();
|
|
|
|
Query("insert into syncneeded (needed) values (1)");
|
|
}
|
|
|
|
|
|
sub DeletePartition() {
|
|
EnsureCanChangePartition($F::partitionid);
|
|
|
|
Query("delete from partitions where id = '$F::partitionid'");
|
|
Query("delete from files where partitionid = '$F::partitionid'");
|
|
Query("delete from members where partitionid = '$F::partitionid'");
|
|
Query("insert into syncneeded (needed) values (1)");
|
|
PrintHeader();
|
|
print h1("OK, the partition is gone.");
|
|
print hr();
|
|
MainMenu();
|
|
}
|
|
|
|
sub FileMatches {
|
|
my ($pattern, $name) = (@_);
|
|
my $regexp = $pattern;
|
|
if ($pattern =~ /\%$/) {
|
|
$regexp =~ s:\%$:[^/]*\$:;
|
|
} elsif ($pattern =~ /\*$/) {
|
|
$regexp =~ s:\*$:.*:;
|
|
}
|
|
if ($name =~ /$regexp/) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
sub CreateListRow {
|
|
my ($title, $query) = (@_);
|
|
my $result = "";
|
|
my @row;
|
|
while (my @row = $query->fetchrow()) {
|
|
my $v = $row[0];
|
|
if (defined $row[1] && $row[1] eq "Yes") {
|
|
$v .= " (disabled)";
|
|
}
|
|
$result .= $v . "\n";
|
|
}
|
|
return Tr(th(a({-href=>"help.html#$title"}, "$title:")) .
|
|
td(textarea(-name=>$title,
|
|
-default=>$result,
|
|
-rows=>10,
|
|
-columns=>50)));
|
|
}
|
|
|
|
|
|
|
|
sub ChangePassword {
|
|
PrintHeader();
|
|
print h1("Change your mozilla.org password.");
|
|
$F::loginpassword = "";
|
|
# CGI seems to be misbehaving; creating the form tag manually as a workaround.
|
|
print qq|<form method="$::POSTTYPE" enctype="application/x-www-form-urlencoded">\n|;
|
|
# print start_form(-method=>$::POSTTYPE);
|
|
print hidden("loginname", $F::loginname);
|
|
print hidden(-name=>"command",
|
|
-default=>"SetNewPassword",
|
|
-override=>1);
|
|
print table(Tr(th({-align=>"right"}, "Old password:"),
|
|
td(password_field(-name=>"loginpassword", -size=>20,
|
|
-override=>1))),
|
|
Tr(th({-align=>"right"}, "New password:"),
|
|
td(password_field(-name=>"newpassword1", -size=>20))),
|
|
Tr(th({-align=>"right"}, "Retype new password:"),
|
|
td(password_field(-name=>"newpassword2", -size=>20))));
|
|
print submit("Change password");
|
|
print end_form();
|
|
}
|
|
|
|
|
|
sub SetNewPassword {
|
|
if ($F::newpassword1 ne $F::newpassword2) {
|
|
Punt("New passwords didn't match.");
|
|
}
|
|
if ($F::newpassword1 eq $F::loginpassword) {
|
|
Punt("Your new password isn't any different than your old one.");
|
|
}
|
|
my $z1;
|
|
my $salt = "";
|
|
my $pass = cryptit($F::newpassword1, $salt);
|
|
|
|
my $qpass = $::db->quote($pass);
|
|
Query("update users set passwd = $qpass, neednewpassword = 'No' where email='$F::loginname'");
|
|
PrintHeader();
|
|
print h1("Password has been updated.");
|
|
Query("insert into syncneeded (needed) values (1)");
|
|
if ($::despot) {
|
|
param("loginpassword", $F::newpassword1);
|
|
print hr();
|
|
MainMenu();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub MyForm {
|
|
my ($command) = @_;
|
|
# CGI seems to be misbehaving; creating the form tag manually as a workaround.
|
|
my $result = qq|<form method="$::POSTTYPE" enctype="application/x-www-form-urlencoded">| .
|
|
hidden("loginname", $F::loginname) .
|
|
hidden("_despot") .
|
|
hidden("loginpassword", $F::loginpassword) .
|
|
hidden(-name=>"command", -value=>$command, -override=>1);
|
|
return $result;
|
|
}
|
|
|
|
sub Punt {
|
|
my ($header) = @_;
|
|
PrintHeader();
|
|
print h1($header);
|
|
print p("Please hit " . b("back") . " and try again.");
|
|
exit;
|
|
}
|
|
|
|
|
|
sub EnsureDespot {
|
|
if (!$::despot) {
|
|
Punt("You're not a despot. You can't do this.");
|
|
}
|
|
}
|
|
|
|
|
|
sub EnsureCanChangePartition {
|
|
my ($id) = (@_);
|
|
if (!CanChangePartition($id)) {
|
|
Punt("You must be an Owner or Peer of the partition to do this.");
|
|
}
|
|
}
|
|
|
|
|
|
sub CanChangePartition {
|
|
my ($id) = (@_);
|
|
if ($::despot) {
|
|
return 1;
|
|
}
|
|
my $query = Query("select class from members where userid=$::loginid and partitionid=$id");
|
|
my @row;
|
|
if (@row = $query->fetchrow()) {
|
|
if ($row[0] ne "Member") {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
sub pickrandompassword {
|
|
my $sc = "abcdefghijklmnopqrstuvwxyz";
|
|
my $result = "";
|
|
for (my $i=0 ; $i<8 ; $i++) {
|
|
$result .= substr($sc, int (rand () * 100000) % (length ($sc) + 1), 1);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
sub IdToEmail {
|
|
my ($id) = (@_);
|
|
if ($id eq "0") {
|
|
return "(none)";
|
|
} else {
|
|
my $query3 =
|
|
Query("select email,disabled from users where id = $id");
|
|
my @row3;
|
|
if (@row3 = $query3->fetchrow()) {
|
|
if ($row3[1] eq "Yes") {
|
|
$row3[0] .= " (Disabled)";
|
|
}
|
|
return $row3[0];
|
|
}
|
|
}
|
|
|
|
return "<font color=red>Unknown Id $id</font>";
|
|
}
|
|
|
|
sub EmailToId {
|
|
my ($email, $forcevalid) = (@_);
|
|
|
|
$email =~ s/\(.*\)//g;
|
|
$email = trim($email);
|
|
|
|
my $query = Query("select id from users where email = " .
|
|
$::db->quote($email));
|
|
my @row;
|
|
if (@row = $query->fetchrow()) {
|
|
return $row[0];
|
|
}
|
|
if ($forcevalid || $email ne "") {
|
|
Punt("$email is not a registered email address.");
|
|
}
|
|
return 0;
|
|
}
|
|
|