summaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/addcustomfield.pl62
-rw-r--r--contrib/fix_comment_text.pl75
-rw-r--r--contrib/moco-ldap-check.pl224
-rw-r--r--contrib/reorg-tools/README9
-rw-r--r--contrib/reorg-tools/bmo-plan.txt82
-rw-r--r--contrib/reorg-tools/fixgroupqueries.pl119
-rw-r--r--contrib/reorg-tools/fixqueries.pl132
-rw-r--r--contrib/reorg-tools/migrate_crash_signatures.pl126
-rw-r--r--contrib/reorg-tools/move_flag_types.pl168
-rw-r--r--contrib/reorg-tools/movebugs.pl151
-rw-r--r--contrib/reorg-tools/movecomponent.pl193
-rw-r--r--contrib/reorg-tools/syncflags.pl86
-rw-r--r--contrib/reorg-tools/syncmsandversions.pl107
-rw-r--r--contrib/sanitizeme.pl176
-rw-r--r--contrib/verify-user.pl129
15 files changed, 1839 insertions, 0 deletions
diff --git a/contrib/addcustomfield.pl b/contrib/addcustomfield.pl
new file mode 100644
index 000000000..c7f93c297
--- /dev/null
+++ b/contrib/addcustomfield.pl
@@ -0,0 +1,62 @@
+#!/usr/bin/perl -wT
+# -*- 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.
+#
+# Contributor(s): Frédéric Buclin <LpSolit@gmail.com>
+# David Miller <justdave@mozilla.com>
+
+use strict;
+use lib qw(. lib);
+
+use Bugzilla;
+use Bugzilla::Constants;
+use Bugzilla::Field;
+
+Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
+
+my %types = (
+ 'freetext' => FIELD_TYPE_FREETEXT,
+ 'single_select' => FIELD_TYPE_SINGLE_SELECT,
+ 'multi_select' => FIELD_TYPE_MULTI_SELECT,
+ 'textarea' => FIELD_TYPE_TEXTAREA,
+ 'datetime' => FIELD_TYPE_DATETIME,
+ 'bug_id' => FIELD_TYPE_BUG_ID,
+ 'bug_urls' => FIELD_TYPE_BUG_URLS,
+ 'keywords' => FIELD_TYPE_KEYWORDS,
+);
+
+my $syntax =
+ "syntax: addcustomfield.pl <field name> [field type]\n\n" .
+ "valid field types:\n " . join("\n ", sort keys %types) . "\n\n" .
+ "the default field type is single_select\n";
+
+my $name = shift || die $syntax;
+my $type = lc(shift || 'single_select');
+exists $types{$type} || die "Invalid field type '$type'.\n\n$syntax";
+$type = $types{$type};
+
+Bugzilla::Field->create({
+ name => $name,
+ description => 'Please give me a description!',
+ type => $type,
+ mailhead => 0,
+ enter_bug => 0,
+ obsolete => 1,
+ custom => 1,
+ buglist => 1,
+});
+print "Done!\n";
+
+my $urlbase = Bugzilla->params->{urlbase};
+print "Please visit ${urlbase}editfields.cgi?action=edit&name=$name to finish setting up this field.\n";
diff --git a/contrib/fix_comment_text.pl b/contrib/fix_comment_text.pl
new file mode 100644
index 000000000..f17bbc3d4
--- /dev/null
+++ b/contrib/fix_comment_text.pl
@@ -0,0 +1,75 @@
+#!/usr/bin/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 Initial Developer of the Original Code is Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2011 the
+# Initial Developer. All Rights Reserved.
+#
+#===============================================================================
+#
+# FILE: fix_comment_text.pl
+#
+# USAGE: ./fix_comment_text.pl <comment_id>
+#
+# DESCRIPTION: Updates a comment in Bugzilla with the text after __DATA__
+#
+# OPTIONS: <comment_id> - The comment id from longdescs with the comment
+# to be replaced.
+# REQUIREMENTS: ---
+# BUGS: ---
+# NOTES: ---
+# AUTHOR: David Lawrence (:dkl), dkl@mozilla.com
+# COMPANY: Mozilla Foundation
+# VERSION: 1.0
+# CREATED: 06/20/2011 03:40:22 PM
+# REVISION: ---
+#===============================================================================
+
+use strict;
+use warnings;
+
+use lib ".";
+
+use Bugzilla;
+use Bugzilla::Util qw(detaint_natural);
+
+my $comment_id = shift;
+
+if (!detaint_natural($comment_id)) {
+ print "Error: invalid comment id or comment id not provided.\n" .
+ "Usage: ./fix_comment_text.pl <comment_id>\n";
+ exit(1);
+}
+
+my $dbh = Bugzilla->dbh;
+
+my $comment = join("", <DATA>);
+
+if ($comment =~ /ENTER NEW COMMENT TEXT HERE/) {
+ print "Please enter the new comment text in the script " .
+ "after the __DATA__ marker.\n";
+ exit(1);
+}
+
+$dbh->bz_start_transaction;
+
+Bugzilla->dbh->do(
+ "UPDATE longdescs SET thetext = ? WHERE comment_id = ?",
+ undef, $comment, $comment_id);
+
+$dbh->bz_commit_transaction;
+
+exit(0);
+
+__DATA__
+ENTER NEW COMMENT TEXT HERE BELOW THE __DATA__ MARKER!
diff --git a/contrib/moco-ldap-check.pl b/contrib/moco-ldap-check.pl
new file mode 100644
index 000000000..59f515bf2
--- /dev/null
+++ b/contrib/moco-ldap-check.pl
@@ -0,0 +1,224 @@
+#!/usr/bin/perl -wT
+# -*- 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 the Mozilla
+# Foundation. Portions created by Mozilla are
+# Copyright (C) 2011 Mozilla Foundation. All Rights Reserved.
+#
+# Contributor(s): Byron Jones <glob@mozilla.com>
+
+use strict;
+
+use lib qw(.);
+
+use Net::LDAP;
+use XMLRPC::Lite;
+use HTTP::Cookies;
+use LWP::UserAgent;
+use Term::ReadKey;
+$| = 1;
+
+#
+#
+#
+
+print STDERR <<EOF;
+This script cross-checks members of the Bugzilla mozilla-corporation group
+with Mozilla's LDAP repository.
+
+To run this script you need:
+ - a bugzilla.mozilla.org admin account
+ - a Mozilla LDAP account
+
+EOF
+
+my $bugzillaLogin = get_text('bl', 'Bugzilla Login: ');
+my $bugzillaPassword = get_text('bp', 'Bugzilla Password: ', 1);
+my $ldapLogin = get_text('ll', 'LDAP Login: ');
+my $ldapPassword = get_text('lp', 'LDAP Password: ', 1);
+
+sub get_text {
+ my($switch, $prompt, $password) = @_;
+
+ for (my $i = 0; $i <= $#ARGV; $i++) {
+ if ($ARGV[$i] eq "-$switch") {
+ return $ARGV[$i + 1];
+ }
+ }
+
+ print STDERR $prompt;
+ my $response = '';
+ ReadMode 4;
+ my $ch;
+ while(1) {
+ 1 while (not defined ($ch = ReadKey(-1)));
+ exit if $ch eq "\3";
+ last if $ch =~ /[\r\n]/;
+ if ($ch =~ /[\b\x7F]/) {
+ next if $response eq '';
+ chop $response;
+ print "\b \b";
+ next;
+ }
+ if ($ch eq "\025") {
+ my $len = length($response);
+ print(("\b" x $len) . (" " x $len) . ("\b" x $len));
+ $response = '';
+ next;
+ }
+ next if ord($ch) < 32;
+ $response .= $ch;
+ print STDERR $password ? '*' : $ch;
+ }
+ ReadMode 0;
+ print STDERR "\n";
+ return $response;
+}
+END {
+ ReadMode 0;
+}
+
+#
+# get list of users in mo-co group
+#
+
+my %bugzilla;
+{
+ my $cookie_jar = HTTP::Cookies->new(file => "cookies.txt", autosave => 1);
+ my $proxy = XMLRPC::Lite->proxy(
+ 'https://bugzilla.mozilla.org/xmlrpc.cgi',
+ 'cookie_jar' => $cookie_jar);
+ my $response;
+
+ print STDERR "Logging in to Bugzilla...\n";
+ $response = $proxy->call(
+ 'User.login',
+ {
+ login => $bugzillaLogin,
+ password => $bugzillaPassword,
+ remember => 1,
+ }
+ );
+ if ($response->fault) {
+ my ($package, $filename, $line) = caller;
+ die $response->faultstring . "\n";
+ }
+
+ my $ua = LWP::UserAgent->new();
+ $ua->cookie_jar($cookie_jar);
+ $response = $ua->get('https://bugzilla.mozilla.org/editusers.cgi?' .
+ 'action=list&matchvalue=login_name&matchstr=&matchtype=substr&' .
+ 'grouprestrict=1&groupid=42');
+ if (!$response->is_success) {
+ die $response->status_line;
+ }
+
+ print STDERR "Getting user list from Bugzilla...\n";
+ my $content = $response->content;
+ while (
+ $content =~ m#
+ <td([^>]*)>[^<]+
+ <a\shref="editusers[^"]+">([^<]+)</a>[^<]+
+ </td>[^<]+
+ <td[^>]*>([^<]+)</td>
+ #gx
+ ) {
+ my ($class, $email, $name) = ($1, $2, $3);
+ next if $class =~ /bz_inactive/;
+ $email =~ s/(^\s+|\s+$)//g;
+ $email =~ s/&#64;/@/;
+ next unless $email =~ /@/;
+ $name =~ s/(^\s+|\s+$)//g;
+ $bugzilla{lc $email} = $name;
+ }
+}
+
+#
+# build list of current mo-co bugmail accounts
+#
+
+my %ldap;
+{
+ print STDERR "Logging into LDAP...\n";
+ my $ldap = Net::LDAP->new('addressbook.mozilla.com',
+ scheme => 'ldaps', onerror => 'die') or die "$@";
+ $ldap->bind("mail=$ldapLogin,o=com,dc=mozilla", password => $ldapPassword);
+ my $result = $ldap->search(
+ base => 'o=com,dc=mozilla',
+ scope => 'sub',
+ filter => '(mail=*)',
+ attrs => ['mail', 'bugzillaEmail', 'emailAlias', 'cn', 'employeeType'],
+ );
+ print STDERR "Getting user list from LDAP...\n";
+ foreach my $entry ($result->entries) {
+ my ($name, $bugMail, $mail, $type) =
+ map { $entry->get_value($_) || '' }
+ qw(cn bugzillaEmail mail employeeType);
+ next if $type eq 'DISABLED';
+ $mail = lc $mail;
+ $ldap{$mail}{name} = $name;
+ $ldap{$mail}{bugMail} = lc $bugMail;
+ $ldap{$mail}{alias} = {};
+ foreach my $alias (
+ @{$entry->get_value('emailAlias', asref => 1) || []}
+ ) {
+ $ldap{$mail}{alias}{lc $alias} = 1;
+ }
+ }
+}
+
+#
+# cross-check
+#
+
+my @invalid;
+foreach my $bugzilla (sort keys %bugzilla) {
+ # check for matching bugmail entry
+ my $exists = 0;
+ foreach my $mail (sort keys %ldap) {
+ next unless $ldap{$mail}{bugMail} eq $bugzilla;
+ $exists = 1;
+ last;
+ }
+ next if $exists;
+
+ # check for matching mail
+ $exists = 0;
+ foreach my $mail (sort keys %ldap) {
+ next unless $mail eq $bugzilla;
+ $exists = 1;
+ last;
+ }
+ next if $exists;
+
+ # check for matching email alias
+ $exists = 0;
+ foreach my $mail (sort keys %ldap) {
+ next unless exists $ldap{$mail}{alias}{$bugzilla};
+ $exists = 1;
+ last;
+ }
+ next if $exists;
+
+ push @invalid, $bugzilla;
+}
+
+my $max_length = 0;
+foreach my $email (@invalid) {
+ $max_length = length($email) if length($email) > $max_length;
+}
+foreach my $email (@invalid) {
+ printf "%-${max_length}s %s\n", $email, $bugzilla{$email};
+}
diff --git a/contrib/reorg-tools/README b/contrib/reorg-tools/README
new file mode 100644
index 000000000..4e5d6eb4d
--- /dev/null
+++ b/contrib/reorg-tools/README
@@ -0,0 +1,9 @@
+Upstreaming attempt: https://bugzilla.mozilla.org/show_bug.cgi?id=616499
+
+Included in this directory is a group of tools we've used for moving components
+around in a Bugzilla 3.2 install on bugzilla.mozilla.org.
+
+They may require tweaking if you use them on your own install. Putting them
+here to make it easier to collaborate on them and keep them up-to-date.
+Hopefully Bugzilla upstream will be able to just do this from the web UI
+eventually.
diff --git a/contrib/reorg-tools/bmo-plan.txt b/contrib/reorg-tools/bmo-plan.txt
new file mode 100644
index 000000000..838ff0ab9
--- /dev/null
+++ b/contrib/reorg-tools/bmo-plan.txt
@@ -0,0 +1,82 @@
+==BMO Reorg Plan==
+
+Do the following things, mostly in order (but see "Timing" at the end):
+
+1) Create new classifications using GUI:
+ Graveyard (Description: "Old, retired products", sort key: <last>)
+
+2) Rename classifications using GUI:
+ Client Support to Other
+
+3) Move products between classification using GUI:
+ Grendel from Client Software to Graveyard
+ CCK from Unclassified to Graveyard
+ Derivatives from Unclassified to Graveyard
+ MozillaClassic from Unclassified to Graveyard
+ UI: "reclassify" link from the top-level classification list
+
+4) Create new products using GUI:
+ Core Graveyard in Graveyard
+ (desc: "Old, retired Core components", closed for entry, no charts)
+ MailNews Core in Components
+ (desc: "Mail and news components common to Thunderbird and SeaMonkey",
+ open for bug entry, create charts)
+
+5) Rename products using GUI, and fix queries using fixqueries.pl:
+mysql> update series_categories set name="SeaMonkey (2)" where id=32;
+ Mozilla Application Suite to SeaMonkey
+ Sumo to support.mozilla.com
+
+5.5) Rename versions and milestones in Toolkit to match Firefox before step 6
+
+6) Sync milestones, versions and groups between products using
+syncmsandversions.pl:
+ Core -> Core Graveyard (new)
+ Core -> MailNews Core (new)
+ Firefox -> Toolkit
+ Core -> SeaMonkey
+ mozilla.org -> Websites (only 1)
+
+6.5) Sync flag inclusions using syncflags.pl:
+ Core -> Core Graveyard (new)
+ Core -> MailNews Core (new)
+ Core -> SeaMonkey
+ mozilla.org -> Websites (only 1)
+
+6.7) Allow Firefox flags temporarily in Toolkit:
+ 250 | blocking-firefox3 | blocking1.9 - 387
+ 419 | blocking-firefox3.1 | blocking1.9.1 - 416
+ 63 | blocking0.8 | blocking1.6 - 69
+ 76 | blocking0.9 | blocking1.7 - 83
+ 36 | review | review - 4
+ 356 | wanted-firefox3 | wanted1.9 - 357
+ 418 | wanted-firefox3.1 | wanted1.9.1 - 417
+
+7) Move components using movecomponent.pl.
+ Any instruction beginning "moved from".
+ Can't fix the queries for this - oh well
+ <Very long list>
+
+8) Rename components using GUI, and fix queries using fixqueries.pl.
+ Any instruction beginning "renamed from" or "MailNews: prefix removed".
+ <Long list>
+
+9) Create new components using GUI.
+ Any instruction beginning "new".
+ <Long list>
+
+10) Move open bugs using GUI:
+ XP Miscellany to Core/General
+
+11) Merge components by moving bugs using GUI:
+ Any instruction beginning "merge in".
+ Merge all bugs, including closed. Delete empty component when done.
+ <long list of merges>
+
+12) Close Core Graveyard (and Grendel if necessary) to new bugs
+
+13) Rename Toolkit versions and milestones back (from 5.5)
+
+14) Execute flag mapping SQL using Reed's mapping to update bugs in Toolkit
+
+15) Disable above-listed flags in point 6.7 in Toolkit again
diff --git a/contrib/reorg-tools/fixgroupqueries.pl b/contrib/reorg-tools/fixgroupqueries.pl
new file mode 100644
index 000000000..1c75edb97
--- /dev/null
+++ b/contrib/reorg-tools/fixgroupqueries.pl
@@ -0,0 +1,119 @@
+#!/usr/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 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): Gervase Markham <gerv@gerv.net>
+
+use strict;
+
+use lib qw(. lib);
+
+use Bugzilla;
+use Bugzilla::Constants;
+use Bugzilla::Util;
+
+sub usage() {
+ print <<USAGE;
+Usage: fixgroupqueries.pl <oldvalue> <newvalue>
+
+E.g.: fixgroupqueries.pl w-security webtools-security
+will change all occurrences of "w-security" to "webtools-security" in the
+appropriate places in the namedqueries.
+
+Note that all parameters are case-sensitive.
+USAGE
+}
+
+sub do_namedqueries($$) {
+ my ($old, $new) = @_;
+ $old = url_quote($old);
+ $new = url_quote($new);
+
+ my $dbh = Bugzilla->dbh;
+
+ my $replace_count = 0;
+ my $query = $dbh->selectall_arrayref("SELECT id, query FROM namedqueries");
+ if ($query) {
+ my $sth = $dbh->prepare("UPDATE namedqueries SET query = ?
+ WHERE id = ?");
+
+ foreach my $row (@$query) {
+ my ($id, $query) = @$row;
+ if (($query =~ /field\d+-\d+-\d+=bug_group/) &&
+ ($query =~ /(?:^|&|;)value\d+-\d+-\d+=$old(?:;|&|$)/)) {
+ $query =~ s/((?:^|&|;)value\d+-\d+-\d+=)$old(;|&|$)/$1$new$2/;
+ $sth->execute($query, $id);
+ $replace_count++;
+ }
+ }
+ }
+
+ print "namedqueries: $replace_count replacements made.\n";
+}
+
+# series
+sub do_series($$) {
+ my ($old, $new) = @_;
+ $old = url_quote($old);
+ $new = url_quote($new);
+
+ my $dbh = Bugzilla->dbh;
+ #$dbh->bz_start_transaction();
+
+ my $replace_count = 0;
+ my $query = $dbh->selectall_arrayref("SELECT series_id, query
+ FROM series");
+ if ($query) {
+ my $sth = $dbh->prepare("UPDATE series SET query = ?
+ WHERE series_id = ?");
+ foreach my $row (@$query) {
+ my ($series_id, $query) = @$row;
+
+ if (($query =~ /field\d+-\d+-\d+=bug_group/) &&
+ ($query =~ /(?:^|&|;)value\d+-\d+-\d+=$old(?:;|&|$)/)) {
+ $query =~ s/((?:^|&|;)value\d+-\d+-\d+=)$old(;|&|$)/$1$new$2/;
+ $sth->execute($query, $series_id);
+ $replace_count++;
+ }
+ }
+ }
+
+ #$dbh->bz_commit_transaction();
+ print "series: $replace_count replacements made.\n";
+}
+
+#############################################################################
+# MAIN CODE
+#############################################################################
+# This is a pure command line script.
+Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
+
+if (scalar @ARGV < 2) {
+ usage();
+ exit();
+}
+
+my ($old, $new) = @ARGV;
+
+print "Changing all instances of '$old' to '$new'.\n\n";
+
+#do_namedqueries($old, $new);
+do_series($old, $new);
+
+exit(0);
diff --git a/contrib/reorg-tools/fixqueries.pl b/contrib/reorg-tools/fixqueries.pl
new file mode 100644
index 000000000..4b862fd72
--- /dev/null
+++ b/contrib/reorg-tools/fixqueries.pl
@@ -0,0 +1,132 @@
+#!/usr/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 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): Gervase Markham <gerv@gerv.net>
+
+use strict;
+
+use lib qw(. lib);
+
+use Bugzilla;
+use Bugzilla::Constants;
+use Bugzilla::Util;
+
+sub usage() {
+ print <<USAGE;
+Usage: fixqueries.pl <parameter> <oldvalue> <newvalue>
+
+E.g.: fixqueries.pl product FoodReplicator SeaMonkey
+will change all occurrences of "FoodReplicator" to "Seamonkey" in the
+appropriate places in the namedqueries, series and series_categories tables.
+
+Note that all parameters are case-sensitive.
+USAGE
+}
+
+sub do_namedqueries($$$) {
+ my ($field, $old, $new) = @_;
+ $old = url_quote($old);
+ $new = url_quote($new);
+
+ my $dbh = Bugzilla->dbh;
+ #$dbh->bz_start_transaction();
+
+ my $replace_count = 0;
+ my $query = $dbh->selectall_arrayref("SELECT id, query FROM namedqueries");
+ if ($query) {
+ my $sth = $dbh->prepare("UPDATE namedqueries SET query = ?
+ WHERE id = ?");
+
+ foreach my $row (@$query) {
+ my ($id, $query) = @$row;
+ if ($query =~ /(?:^|&|;)$field=$old(?:&|$|;)/) {
+ $query =~ s/((?:^|&|;)$field=)$old(;|&|$)/$1$new$2/;
+ $sth->execute($query, $id);
+ $replace_count++;
+ }
+ }
+ }
+
+ #$dbh->bz_commit_transaction();
+ print "namedqueries: $replace_count replacements made.\n";
+}
+
+# series
+sub do_series($$$) {
+ my ($field, $old, $new) = @_;
+ $old = url_quote($old);
+ $new = url_quote($new);
+
+ my $dbh = Bugzilla->dbh;
+ #$dbh->bz_start_transaction();
+
+ my $replace_count = 0;
+ my $query = $dbh->selectall_arrayref("SELECT series_id, query
+ FROM series");
+ if ($query) {
+ my $sth = $dbh->prepare("UPDATE series SET query = ?
+ WHERE series_id = ?");
+ foreach my $row (@$query) {
+ my ($series_id, $query) = @$row;
+
+ if ($query =~ /(?:^|&|;)$field=$old(?:&|$|;)/) {
+ $query =~ s/((?:^|&|;)$field=)$old(;|&|$)/$1$new$2/;
+ $replace_count++;
+ }
+
+ $sth->execute($query, $series_id);
+ }
+ }
+
+ #$dbh->bz_commit_transaction();
+ print "series: $replace_count replacements made.\n";
+}
+
+# series_categories
+sub do_series_categories($$) {
+ my ($old, $new) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ $dbh->do("UPDATE series_categories SET name = ? WHERE name = ?",
+ undef,
+ ($new, $old));
+}
+
+#############################################################################
+# MAIN CODE
+#############################################################################
+# This is a pure command line script.
+Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
+
+if (scalar @ARGV < 3) {
+ usage();
+ exit();
+}
+
+my ($field, $old, $new) = @ARGV;
+
+print "Changing all instances of '$old' to '$new'.\n\n";
+
+do_namedqueries($field, $old, $new);
+do_series($field, $old, $new);
+do_series_categories($old, $new);
+
+exit(0);
+
diff --git a/contrib/reorg-tools/migrate_crash_signatures.pl b/contrib/reorg-tools/migrate_crash_signatures.pl
new file mode 100644
index 000000000..b12446280
--- /dev/null
+++ b/contrib/reorg-tools/migrate_crash_signatures.pl
@@ -0,0 +1,126 @@
+#!/usr/bin/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 Initial Developer of the Original Code is Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011 the
+# Initial Developer. All Rights Reserved.
+#
+#===============================================================================
+#
+# FILE: migrate_crash_signatures.pl
+#
+# USAGE: ./migrate_crash_signatures.pl
+#
+# DESCRIPTION: Migrate current summary data on matched bugs to the
+# new cf_crash_signature custom fields.
+#
+# OPTIONS: No params, then performs dry-run without updating the database.
+# If a true value is passed as single argument, then the database
+# is updated.
+# REQUIREMENTS: None
+# BUGS: 577724
+# NOTES: None
+# AUTHOR: David Lawrence (dkl@mozilla.com),
+# COMPANY: Mozilla Corproation
+# VERSION: 1.0
+# CREATED: 05/31/2011 03:57:52 PM
+# REVISION: 1
+#===============================================================================
+
+use strict;
+use warnings;
+
+use lib qw(. lib);
+
+use Bugzilla;
+use Bugzilla::Constants;
+use Bugzilla::Util;
+
+use Data::Dumper;
+
+Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
+
+my $UPDATE_DB = shift; # Pass true value as single argument to perform database update
+
+my $dbh = Bugzilla->dbh;
+
+# User to make changes as
+my $user_id = $dbh->selectrow_array(
+ "SELECT userid FROM profiles WHERE login_name='nobody\@mozilla.org'");
+$user_id or die "Can't find user ID for 'nobody\@mozilla.org'\n";
+
+my $field_id = $dbh->selectrow_array(
+ "SELECT id FROM fielddefs WHERE name = 'cf_crash_signature'");
+$field_id or die "Can't find field ID for 'cf_crash_signature' field\n";
+
+# Search criteria
+# a) crash or topcrash keyword,
+# b) not have [notacrash] in whiteboard,
+# c) have a properly formulated [@ ...]
+
+# crash and topcrash keyword ids
+my $crash_keyword_id = $dbh->selectrow_array(
+ "SELECT id FROM keyworddefs WHERE name = 'crash'");
+$crash_keyword_id or die "Can't find keyword id for 'crash'\n";
+
+my $topcrash_keyword_id = $dbh->selectrow_array(
+ "SELECT id FROM keyworddefs WHERE name = 'topcrash'");
+$topcrash_keyword_id or die "Can't find keyword id for 'topcrash'\n";
+
+# main search query
+my $bugs = $dbh->selectall_arrayref("
+ SELECT bugs.bug_id, bugs.short_desc
+ FROM bugs LEFT JOIN keywords ON bugs.bug_id = keywords.bug_id
+ WHERE (keywords.keywordid = ? OR keywords.keywordid = ?)
+ AND bugs.status_whiteboard NOT REGEXP '\\\\[notacrash\\\\]'
+ AND bugs.short_desc REGEXP '\\\\[@.+\\\\]'
+ AND (bugs.cf_crash_signature IS NULL OR bugs.cf_crash_signature = '')
+ ORDER BY bugs.bug_id",
+ {'Slice' => {}}, $crash_keyword_id, $topcrash_keyword_id);
+
+my $bug_count = scalar @$bugs;
+$bug_count or die "No bugs were found in matching search criteria.\n";
+
+print "Migrating $bug_count bugs to new crash signature field\n";
+
+$dbh->bz_start_transaction() if $UPDATE_DB;
+
+foreach my $bug (@$bugs) {
+ my $bug_id = $bug->{'bug_id'};
+ my $summary = $bug->{'short_desc'};
+
+ print "Updating bug $bug_id ...";
+
+ my @signatures;
+ while ($summary =~ /(\[\@(?:\[.*\]|[^\[])*\])/g) {
+ push(@signatures, $1);
+ }
+
+ if (@signatures && $UPDATE_DB) {
+ my $timestamp = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+ $dbh->do("UPDATE bugs SET cf_crash_signature = ? WHERE bug_id = ?",
+ undef, join("\n", @signatures), $bug_id);
+ $dbh->do("INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added) " .
+ "VALUES (?, ?, ?, ?, '', ?)",
+ undef, $bug_id, $user_id, $timestamp, $field_id, join("\n", @signatures));
+ $dbh->do("UPDATE bugs SET delta_ts = ?, lastdiffed = ? WHERE bug_id = ?",
+ undef, $timestamp, $timestamp, $bug_id);
+ }
+ elsif (@signatures) {
+ print Dumper(\@signatures);
+ }
+
+ print "done.\n";
+}
+
+$dbh->bz_commit_transaction() if $UPDATE_DB;
diff --git a/contrib/reorg-tools/move_flag_types.pl b/contrib/reorg-tools/move_flag_types.pl
new file mode 100644
index 000000000..a75b7f497
--- /dev/null
+++ b/contrib/reorg-tools/move_flag_types.pl
@@ -0,0 +1,168 @@
+#!/usr/bin/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 Initial Developer of the Original Code is Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2011 the
+# Initial Developer. All Rights Reserved.
+#
+#===============================================================================
+#
+# FILE: move_flag_types.pl
+#
+# USAGE: ./move_flag_types.pl
+#
+# DESCRIPTION: Move current set flag from one type_id to another
+# based on product and optionally component.
+#
+# OPTIONS: ---
+# REQUIREMENTS: ---
+# BUGS: ---
+# NOTES: ---
+# AUTHOR: David Lawrence (:dkl), dkl@mozilla.com
+# COMPANY: Mozilla Foundation
+# VERSION: 1.0
+# CREATED: 08/22/2011 05:18:06 PM
+# REVISION: ---
+#===============================================================================
+
+=head1 NAME
+
+move_flag_types.pl - Move currently set flags from one type id to another based
+on product and optionally component.
+
+=head1 SYNOPSIS
+
+This script will move bugs matching a specific product (and optionally a component)
+from one flag type id to another if the bug has the flag set to either +, -, or ?.
+
+./move_flag_types.pl --old-id 4 --new-id 720 --product Firefox --component Installer
+
+=head1 OPTIONS
+
+=over
+
+=item B<--help|-h|?>
+
+Print a brief help message and exits.
+
+=item B<--oldid|-o>
+
+Old flag type id. Use editflagtypes.cgi to determine the type id from the URL.
+
+=item B<--newid|-n>
+
+New flag type id. Use editflagtypes.cgi to determine the type id from the URL.
+
+=item B<--product|-p>
+
+The product that the bugs most be assigned to.
+
+=item B<--component|-c>
+
+Optional: The component of the given product that the bugs must be assigned to.
+
+=item B<--doit|-d>
+
+Without this argument, changes are not actually committed to the database.
+
+=back
+
+=cut
+
+use strict;
+use warnings;
+
+use lib '.';
+
+use Bugzilla;
+use Getopt::Long;
+use Pod::Usage;
+
+my %params;
+GetOptions(\%params, 'help|h|?', 'oldid|o=s', 'newid|n=s',
+ 'product|p=s', 'component|c:s', 'doit|d') or pod2usage(1);
+
+if ($params{'help'} || !$params{'oldid'}
+ || !$params{'newid'} || !$params{'product'}) {
+ pod2usage({ -message => "Missing required argument",
+ -exitval => 1 });
+}
+
+# Set defaults
+$params{'doit'} ||= 0;
+$params{'component'} ||= '';
+
+my $dbh = Bugzilla->dbh;
+
+# Get the flag names
+my $old_flag_name = $dbh->selectrow_array(
+ "SELECT name FROM flagtypes WHERE id = ?",
+ undef, $params{'oldid'});
+my $new_flag_name = $dbh->selectrow_array(
+ "SELECT name FROM flagtypes WHERE id = ?",
+ undef, $params{'newid'});
+
+# Find the product id
+my $product_id = $dbh->selectrow_array(
+ "SELECT id FROM products WHERE name = ?",
+ undef, $params{'product'});
+
+# Find the component id if not __ANY__
+my $component_id;
+if ($params{'component'}) {
+ $component_id = $dbh->selectrow_array(
+ "SELECT id FROM components WHERE name = ? AND product_id = ?",
+ undef, $params{'component'}, $product_id);
+}
+
+my @query_args = ($params{'oldid'});
+
+my $flag_query = "SELECT flags.id AS flag_id, flags.bug_id AS bug_id
+ FROM flags JOIN bugs ON flags.bug_id = bugs.bug_id
+ WHERE flags.type_id = ? ";
+
+if ($component_id) {
+ # No need to compare against product_id as component_id is already
+ # tied to a specific product
+ $flag_query .= "AND bugs.component_id = ?";
+ push(@query_args, $component_id);
+}
+else {
+ # All bugs for a product regardless of component
+ $flag_query .= "AND bugs.product_id = ?";
+ push(@query_args, $product_id);
+}
+
+my $flags = $dbh->selectall_arrayref($flag_query, undef, @query_args);
+
+if (@$flags) {
+ print "Moving '" . scalar @$flags . "' flags " .
+ "from $old_flag_name (" . $params{'oldid'} . ") " .
+ "to $new_flag_name (" . $params{'newid'} . ")...\n";
+
+ if (!$params{'doit'}) {
+ print "Pass the argument --doit or -d to permanently make changes to the database.\n";
+ }
+ else {
+ my $flag_update_sth = $dbh->prepare("UPDATE flags SET type_id = ? WHERE id = ?");
+
+ foreach my $flag (@$flags) {
+ my ($flag_id, $bug_id) = @$flag;
+ print "Bug: $bug_id Flag: $flag_id\n";
+ $flag_update_sth->execute($params{'newid'}, $flag_id);
+ }
+ }
+}
+else {
+ print "No flags to move\n";
+}
diff --git a/contrib/reorg-tools/movebugs.pl b/contrib/reorg-tools/movebugs.pl
new file mode 100644
index 000000000..33156dad7
--- /dev/null
+++ b/contrib/reorg-tools/movebugs.pl
@@ -0,0 +1,151 @@
+#!/usr/bin/perl -w
+use strict;
+
+use Cwd 'abs_path';
+use File::Basename;
+BEGIN {
+ my $root = abs_path(dirname(__FILE__) . '/../..');
+ chdir($root);
+}
+use lib qw(. lib);
+
+use Bugzilla;
+use Bugzilla::Constants;
+use Bugzilla::Util;
+
+Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
+
+if (scalar @ARGV < 4) {
+ die <<USAGE;
+Usage: movebugs.pl <old-product> <old-component> <new-product> <new-component>
+
+Eg. movebugs.pl mozilla.org bmo bugzilla.mozilla.org admin
+Will move all bugs in the mozilla.org:bmo component to the
+bugzilla.mozilla.org:admin component.
+
+The new product must have matching versions and milestones from the old
+product.
+USAGE
+}
+
+my ($old_product, $old_component, $new_product, $new_component) = @ARGV;
+
+my $dbh = Bugzilla->dbh;
+
+my $old_product_id = $dbh->selectrow_array(
+ "SELECT id FROM products WHERE name=?",
+ undef, $old_product);
+$old_product_id
+ or die "Can't find product ID for '$old_product'.\n";
+
+my $old_component_id = $dbh->selectrow_array(
+ "SELECT id FROM components WHERE name=? AND product_id=?",
+ undef, $old_component, $old_product_id);
+$old_component_id
+ or die "Can't find component ID for '$old_component'.\n";
+
+my $new_product_id = $dbh->selectrow_array(
+ "SELECT id FROM products WHERE name=?",
+ undef, $new_product);
+$new_product_id
+ or die "Can't find product ID for '$new_product'.\n";
+
+my $new_component_id = $dbh->selectrow_array(
+ "SELECT id FROM components WHERE name=? AND product_id=?",
+ undef, $new_component, $new_product_id);
+$new_component_id
+ or die "Can't find component ID for '$new_component'.\n";
+
+my $product_field_id = $dbh->selectrow_array(
+ "SELECT id FROM fielddefs WHERE name = 'product'");
+$product_field_id
+ or die "Can't find field ID for 'product' field\n";
+my $component_field_id = $dbh->selectrow_array(
+ "SELECT id FROM fielddefs WHERE name = 'component'");
+$component_field_id
+ or die "Can't find field ID for 'component' field\n";
+
+my $user_id = $dbh->selectrow_array(
+ "SELECT userid FROM profiles WHERE login_name='nobody\@mozilla.org'");
+$user_id
+ or die "Can't find user ID for 'nobody\@mozilla.org'\n";
+
+$dbh->bz_start_transaction();
+
+# build list of bugs
+my $ra_ids = $dbh->selectcol_arrayref(
+ "SELECT bug_id FROM bugs WHERE product_id=? AND component_id=?",
+ undef, $old_product_id, $old_component_id);
+my $bug_count = scalar @$ra_ids;
+$bug_count
+ or die "No bugs were found in '$old_component'\n";
+my $where_sql = 'bug_id IN (' . join(',', @$ra_ids) . ')';
+
+# check versions
+my @missing_versions;
+my $ra_versions = $dbh->selectcol_arrayref(
+ "SELECT DISTINCT version FROM bugs WHERE $where_sql");
+foreach my $version (@$ra_versions) {
+ my $has_version = $dbh->selectrow_array(
+ "SELECT 1 FROM versions WHERE product_id=? AND value=?",
+ undef, $new_product_id, $version);
+ push @missing_versions, $version unless $has_version;
+}
+
+# check milestones
+my @missing_milestones;
+my $ra_milestones = $dbh->selectcol_arrayref(
+ "SELECT DISTINCT target_milestone FROM bugs WHERE $where_sql");
+foreach my $milestone (@$ra_milestones) {
+ my $has_milestone = $dbh->selectrow_array(
+ "SELECT 1 FROM milestones WHERE product_id=? AND value=?",
+ undef, $new_product_id, $milestone);
+ push @missing_milestones, $milestone unless $has_milestone;
+}
+
+my $missing_error = '';
+if (@missing_versions) {
+ $missing_error .= "'$new_product' is missing the following version(s):\n " .
+ join("\n ", @missing_versions) . "\n";
+}
+if (@missing_milestones) {
+ $missing_error .= "'$new_product' is missing the following milestone(s):\n " .
+ join("\n ", @missing_milestones) . "\n";
+}
+die $missing_error if $missing_error;
+
+# confirmation
+print <<EOF;
+About to move $bug_count bugs
+From '$old_product' : '$old_component'
+To '$new_product' : '$new_component'
+
+Press <Ctrl-C> to stop or <Enter> to continue...
+EOF
+getc();
+
+print "Moving $bug_count bugs from $old_product:$old_component to $new_product:$new_component\n";
+
+# update bugs
+$dbh->do(
+ "UPDATE bugs SET product_id=?, component_id=? WHERE $where_sql",
+ undef, $new_product_id, $new_component_id);
+
+# touch bugs
+$dbh->do("UPDATE bugs SET delta_ts=NOW() WHERE $where_sql");
+$dbh->do("UPDATE bugs SET lastdiffed=NOW() WHERE $where_sql");
+
+# update bugs_activity
+$dbh->do(
+ "INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added)
+ SELECT bug_id, ?, delta_ts, ?, ?, ? FROM bugs WHERE $where_sql",
+ undef,
+ $user_id, $product_field_id, $old_product, $new_product);
+$dbh->do(
+ "INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added)
+ SELECT bug_id, ?, delta_ts, ?, ?, ? FROM bugs WHERE $where_sql",
+ undef,
+ $user_id, $component_field_id, $old_component, $new_component);
+
+$dbh->bz_commit_transaction();
+
diff --git a/contrib/reorg-tools/movecomponent.pl b/contrib/reorg-tools/movecomponent.pl
new file mode 100644
index 000000000..8f8bc0abc
--- /dev/null
+++ b/contrib/reorg-tools/movecomponent.pl
@@ -0,0 +1,193 @@
+#!/usr/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 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): Gervase Markham <gerv@gerv.net>
+
+# See also https://bugzilla.mozilla.org/show_bug.cgi?id=119569
+#
+
+use strict;
+
+use Cwd 'abs_path';
+use File::Basename;
+BEGIN {
+ my $root = abs_path(dirname(__FILE__) . '/../..');
+ chdir($root);
+}
+use lib qw(. lib);
+
+use Bugzilla;
+use Bugzilla::Constants;
+use Bugzilla::Util;
+
+sub usage() {
+ print <<USAGE;
+Usage: movecomponent.pl <oldproduct> <newproduct> <component> <doit>
+
+E.g.: movecomponent.pl ReplicationEngine FoodReplicator SeaMonkey
+will move the component "SeaMonkey" from the product "ReplicationEngine"
+to the product "FoodReplicator".
+
+Important: You must make sure the milestones and versions of the bugs in the
+component are available in the new product. See syncmsandversions.pl.
+
+Pass in a true value for "doit" to make the database changes permament.
+USAGE
+
+ exit(1);
+}
+
+#############################################################################
+# MAIN CODE
+#############################################################################
+
+# This is a pure command line script.
+Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
+
+if (scalar @ARGV < 3) {
+ usage();
+ exit();
+}
+
+my ($oldproduct, $newproduct, $component, $doit) = @ARGV;
+
+my $dbh = Bugzilla->dbh;
+
+$dbh->{'AutoCommit'} = 0 unless $doit; # Turn off autocommit by default
+
+# Find product IDs
+my $oldprodid = $dbh->selectrow_array("SELECT id FROM products WHERE name = ?",
+ undef, $oldproduct);
+if (!$oldprodid) {
+ print "Can't find product ID for '$oldproduct'.\n";
+ exit(1);
+}
+
+my $newprodid = $dbh->selectrow_array("SELECT id FROM products WHERE name = ?",
+ undef, $newproduct);
+if (!$newprodid) {
+ print "Can't find product ID for '$newproduct'.\n";
+ exit(1);
+}
+
+# Find component ID
+my $compid = $dbh->selectrow_array("SELECT id FROM components
+ WHERE name = ? AND product_id = ?",
+ undef, $component, $oldprodid);
+if (!$compid) {
+ print "Can't find component ID for '$component' in product " .
+ "'$oldproduct'.\n";
+ exit(1);
+}
+
+my $fieldid = $dbh->selectrow_array("SELECT id FROM fielddefs
+ WHERE name = 'product'");
+if (!$fieldid) {
+ print "Can't find field ID for 'product' field!\n";
+ exit(1);
+}
+
+# check versions
+my @missing_versions;
+my $ra_versions = $dbh->selectcol_arrayref(
+ "SELECT DISTINCT version FROM bugs WHERE component_id = ?",
+ undef, $compid);
+foreach my $version (@$ra_versions) {
+ my $has_version = $dbh->selectrow_array(
+ "SELECT 1 FROM versions WHERE product_id = ? AND value = ?",
+ undef, $newprodid, $version);
+ push @missing_versions, $version unless $has_version;
+}
+
+# check milestones
+my @missing_milestones;
+my $ra_milestones = $dbh->selectcol_arrayref(
+ "SELECT DISTINCT target_milestone FROM bugs WHERE component_id = ?",
+ undef, $compid);
+foreach my $milestone (@$ra_milestones) {
+ my $has_milestone = $dbh->selectrow_array(
+ "SELECT 1 FROM milestones WHERE product_id=? AND value=?",
+ undef, $newprodid, $milestone);
+ push @missing_milestones, $milestone unless $has_milestone;
+}
+
+my $missing_error = '';
+if (@missing_versions) {
+ $missing_error .= "'$newproduct' is missing the following version(s):\n " .
+ join("\n ", @missing_versions) . "\n";
+}
+if (@missing_milestones) {
+ $missing_error .= "'$newproduct' is missing the following milestone(s):\n " .
+ join("\n ", @missing_milestones) . "\n";
+}
+die $missing_error if $missing_error;
+
+# confirmation
+print <<EOF;
+About to move the component '$component'
+From '$oldproduct'
+To '$newproduct'
+
+Press <Ctrl-C> to stop or <Enter> to continue...
+EOF
+getc();
+
+print "Moving '$component' from '$oldproduct' to '$newproduct'...\n\n";
+$dbh->bz_start_transaction() if $doit;
+
+# Bugs table
+$dbh->do("UPDATE bugs SET product_id = ? WHERE component_id = ?",
+ undef,
+ ($newprodid, $compid));
+
+# Flags tables
+$dbh->do("UPDATE flaginclusions SET product_id = ? WHERE component_id = ?",
+ undef,
+ ($newprodid, $compid));
+
+$dbh->do("UPDATE flagexclusions SET product_id = ? WHERE component_id = ?",
+ undef,
+ ($newprodid, $compid));
+
+# Components
+$dbh->do("UPDATE components SET product_id = ? WHERE id = ?",
+ undef,
+ ($newprodid, $compid));
+
+# Mark bugs as touched
+$dbh->do("UPDATE bugs SET delta_ts = NOW()
+ WHERE component_id = ?", undef, $compid);
+$dbh->do("UPDATE bugs SET lastdiffed = NOW()
+ WHERE component_id = ?", undef, $compid);
+
+# Update bugs_activity
+my $userid = 1; # nobody@mozilla.org
+
+$dbh->do("INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed,
+ added)
+ SELECT bug_id, ?, delta_ts, ?, ?, ?
+ FROM bugs WHERE component_id = ?",
+ undef,
+ ($userid, $fieldid, $oldproduct, $newproduct, $compid));
+
+$dbh->bz_commit_transaction() if $doit;
+
+exit(0);
+
diff --git a/contrib/reorg-tools/syncflags.pl b/contrib/reorg-tools/syncflags.pl
new file mode 100644
index 000000000..6c5b8293a
--- /dev/null
+++ b/contrib/reorg-tools/syncflags.pl
@@ -0,0 +1,86 @@
+#!/usr/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 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): Gervase Markham <gerv@gerv.net>
+
+# See also https://bugzilla.mozilla.org/show_bug.cgi?id=119569
+
+use strict;
+
+use lib qw(. lib);
+
+use Bugzilla;
+use Bugzilla::Constants;
+
+sub usage() {
+ print <<USAGE;
+Usage: syncflags.pl <srcproduct> <tgtproduct>
+
+E.g.: syncflags.pl FoodReplicator SeaMonkey
+will copy any flag inclusions (only) for the product "FoodReplicator"
+so matching inclusions exist for the product "SeaMonkey". This script is
+normally used prior to moving components from srcproduct to tgtproduct.
+USAGE
+
+ exit(1);
+}
+
+#############################################################################
+# MAIN CODE
+#############################################################################
+
+# This is a pure command line script.
+Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
+
+if (scalar @ARGV < 2) {
+ usage();
+ exit();
+}
+
+my ($srcproduct, $tgtproduct) = @ARGV;
+
+my $dbh = Bugzilla->dbh;
+
+# Find product IDs
+my $srcprodid = $dbh->selectrow_array("SELECT id FROM products WHERE name = ?",
+ undef, $srcproduct);
+if (!$srcprodid) {
+ print "Can't find product ID for '$srcproduct'.\n";
+ exit(1);
+}
+
+my $tgtprodid = $dbh->selectrow_array("SELECT id FROM products WHERE name = ?",
+ undef, $tgtproduct);
+if (!$tgtprodid) {
+ print "Can't find product ID for '$tgtproduct'.\n";
+ exit(1);
+}
+
+$dbh->do("INSERT INTO flaginclusions(component_id, type_id, product_id)
+ SELECT fi1.component_id, fi1.type_id, ? FROM flaginclusions fi1
+ LEFT JOIN flaginclusions fi2
+ ON fi1.type_id = fi2.type_id
+ AND fi2.product_id = ?
+ WHERE fi1.product_id = ?
+ AND fi2.type_id IS NULL",
+ undef,
+ $tgtprodid, $tgtprodid, $srcprodid);
+
+exit(0);
diff --git a/contrib/reorg-tools/syncmsandversions.pl b/contrib/reorg-tools/syncmsandversions.pl
new file mode 100644
index 000000000..b25b3348b
--- /dev/null
+++ b/contrib/reorg-tools/syncmsandversions.pl
@@ -0,0 +1,107 @@
+#!/usr/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 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): Gervase Markham <gerv@gerv.net>
+
+# See also https://bugzilla.mozilla.org/show_bug.cgi?id=119569
+
+use strict;
+
+use lib qw(. lib);
+
+use Bugzilla;
+use Bugzilla::Constants;
+
+sub usage() {
+ print <<USAGE;
+Usage: syncmsandversions.pl <srcproduct> <tgtproduct>
+
+E.g.: syncmsandversions.pl FoodReplicator SeaMonkey
+will copy any versions and milstones in the product "FoodReplicator"
+which do not exist in product "SeaMonkey" into it. This script is normally
+used prior to moving components from srcproduct to tgtproduct.
+USAGE
+
+ exit(1);
+}
+
+#############################################################################
+# MAIN CODE
+#############################################################################
+
+# This is a pure command line script.
+Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
+
+if (scalar @ARGV < 2) {
+ usage();
+ exit();
+}
+
+my ($srcproduct, $tgtproduct) = @ARGV;
+
+my $dbh = Bugzilla->dbh;
+
+# Find product IDs
+my $srcprodid = $dbh->selectrow_array("SELECT id FROM products WHERE name = ?",
+ undef, $srcproduct);
+if (!$srcprodid) {
+ print "Can't find product ID for '$srcproduct'.\n";
+ exit(1);
+}
+
+my $tgtprodid = $dbh->selectrow_array("SELECT id FROM products WHERE name = ?",
+ undef, $tgtproduct);
+if (!$tgtprodid) {
+ print "Can't find product ID for '$tgtproduct'.\n";
+ exit(1);
+}
+
+#$dbh->bz_start_transaction();
+
+$dbh->do("INSERT INTO milestones(value, sortkey, product_id)
+ SELECT m1.value, m1.sortkey, ? FROM milestones m1
+ LEFT JOIN milestones m2 ON m1.value = m2.value AND
+ m2.product_id = ?
+ WHERE m1.product_id = ? AND m2.value IS NULL",
+ undef,
+ $tgtprodid, $tgtprodid, $srcprodid);
+
+$dbh->do("INSERT INTO versions(value, product_id)
+ SELECT v1.value, ? FROM versions v1
+ LEFT JOIN versions v2 ON v1.value = v2.value AND
+ v2.product_id = ?
+ WHERE v1.product_id = ? AND v2.value IS NULL",
+ undef,
+ $tgtprodid, $tgtprodid, $srcprodid);
+
+$dbh->do("INSERT INTO group_control_map (group_id, product_id, entry, membercontrol, othercontrol, canedit, editcomponents, editbugs, canconfirm)
+ SELECT g1.group_id, ?, g1.entry, g1.membercontrol, g1.othercontrol, g1.canedit, g1.editcomponents, g1.editbugs, g1.canconfirm
+ FROM group_control_map g1
+ LEFT JOIN group_control_map g2 ON g1.product_id = ? AND
+ g2.product_id = ? AND
+ g1.group_id = g2.group_id
+ WHERE g1.product_id = ? AND g2.group_id IS NULL",
+ undef,
+ $tgtprodid, $srcprodid, $tgtprodid, $srcprodid);
+
+#$dbh->bz_commit_transaction();
+
+exit(0);
+
diff --git a/contrib/sanitizeme.pl b/contrib/sanitizeme.pl
new file mode 100644
index 000000000..362700be0
--- /dev/null
+++ b/contrib/sanitizeme.pl
@@ -0,0 +1,176 @@
+#!/usr/bin/perl -wT
+# -*- 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 the Mozilla
+# Corporation. Portions created by Mozilla are
+# Copyright (C) 2006 Mozilla Foundation. All Rights Reserved.
+#
+# Contributor(s): Myk Melez <myk@mozilla.org>
+# Alex Brugh <alex@cs.umn.edu>
+# Dave Miller <justdave@mozilla.com>
+# Byron Jones <glob@mozilla.com>
+
+use strict;
+
+use lib qw(.);
+
+use Bugzilla;
+use Bugzilla::Constants;
+use Bugzilla::Util;
+
+use Getopt::Long;
+
+my $dbh = Bugzilla->dbh;
+
+# This SQL is designed to sanitize a copy of a Bugzilla database so that it
+# doesn't contain any information that can't be viewed from a web browser by
+# a user who is not logged in.
+
+# Last validated against Bugzilla version 4.0
+
+my ($dry_run, $from_cron, $keep_attachments, $keep_groups,
+ $keep_passwords, $keep_insider, $trace) = (0, 0, 0, '', 0, 0, 0);
+my $keep_groups_sql = '';
+
+GetOptions(
+ "dry-run" => \$dry_run,
+ "from-cron" => \$from_cron,
+ "keep-attachments" => \$keep_attachments,
+ "keep-passwords" => \$keep_passwords,
+ "keep-insider" => \$keep_insider,
+ "keep-groups:s" => \$keep_groups,
+ "trace" => \$trace,
+) or exit;
+
+if ($keep_groups ne '') {
+ my @groups;
+ foreach my $group_id (split(/\s*,\s*/, $keep_groups)) {
+ my $group;
+ if ($group_id =~ /\D/) {
+ $group = Bugzilla::Group->new({ name => $group_id });
+ } else {
+ $group = Bugzilla::Group->new($group_id);
+ }
+ die "Invalid group '$group_id'\n" unless $group;
+ push @groups, $group->id;
+ }
+ $keep_groups_sql = "NOT IN (" . join(",", @groups) . ")";
+}
+
+$dbh->{TraceLevel} = 1 if $trace;
+
+if ($dry_run) {
+ print "** dry run : no changes to the database will be made **\n";
+ $dbh->bz_start_transaction();
+}
+eval {
+ delete_non_public_products();
+ delete_secure_bugs();
+ delete_insider_comments() unless $keep_insider;
+ delete_security_groups();
+ delete_sensitive_user_data();
+ delete_attachment_data() unless $keep_attachments;
+ print "All done!\n";
+ $dbh->bz_rollback_transaction() if $dry_run;
+};
+if ($@) {
+ $dbh->bz_rollback_transaction() if $dry_run;
+ die "$@" if $@;
+}
+
+sub delete_non_public_products {
+ # Delete all non-public products, and all data associated with them
+ my @products = Bugzilla::Product->get_all();
+ my $mandatory = CONTROLMAPMANDATORY;
+ foreach my $product (@products) {
+ # if there are any mandatory groups on the product, nuke it and
+ # everything associated with it (including the bugs)
+ Bugzilla->params->{'allowbugdeletion'} = 1; # override this in memory for now
+ my $mandatorygroups = $dbh->selectcol_arrayref("SELECT group_id FROM group_control_map WHERE product_id = ? AND (membercontrol = $mandatory)", undef, $product->id);
+ if (0 < scalar(@$mandatorygroups)) {
+ print "Deleting product '" . $product->name . "'...\n";
+ $product->remove_from_db();
+ }
+ }
+}
+
+sub delete_secure_bugs {
+ # Delete all data for bugs in security groups.
+ my $buglist = $dbh->selectall_arrayref(
+ $keep_groups
+ ? "SELECT DISTINCT bug_id FROM bug_group_map WHERE group_id $keep_groups_sql"
+ : "SELECT DISTINCT bug_id FROM bug_group_map"
+ );
+ $|=1; # disable buffering so the bug progress counter works
+ my $numbugs = scalar(@$buglist);
+ my $bugnum = 0;
+ print "Deleting $numbugs bugs in " . ($keep_groups ? 'non-' : '') . "security groups...\n";
+ foreach my $row (@$buglist) {
+ my $bug_id = $row->[0];
+ $bugnum++;
+ print "\r$bugnum/$numbugs" unless $from_cron;
+ my $bug = new Bugzilla::Bug($bug_id);
+ $bug->remove_from_db();
+ }
+ print "\rDone \n" unless $from_cron;
+}
+
+sub delete_insider_comments {
+ # Delete all 'insidergroup' comments and attachments
+ print "Deleting 'insidergroup' comments and attachments...\n";
+ $dbh->do("DELETE FROM longdescs WHERE isprivate = 1");
+ $dbh->do("DELETE attach_data FROM attachments JOIN attach_data ON attachments.attach_id = attach_data.id WHERE attachments.isprivate = 1");
+ $dbh->do("DELETE FROM attachments WHERE isprivate = 1");
+ $dbh->do("UPDATE bugs_fulltext SET comments = comments_noprivate");
+}
+
+sub delete_security_groups {
+ # Delete all security groups.
+ print "Deleting " . ($keep_groups ? 'non-' : '') . "security groups...\n";
+ $dbh->do("DELETE user_group_map FROM groups JOIN user_group_map ON groups.id = user_group_map.group_id WHERE groups.isbuggroup = 1");
+ $dbh->do("DELETE group_group_map FROM groups JOIN group_group_map ON (groups.id = group_group_map.member_id OR groups.id = group_group_map.grantor_id) WHERE groups.isbuggroup = 1");
+ $dbh->do("DELETE group_control_map FROM groups JOIN group_control_map ON groups.id = group_control_map.group_id WHERE groups.isbuggroup = 1");
+ $dbh->do("UPDATE flagtypes LEFT JOIN groups ON flagtypes.grant_group_id = groups.id SET grant_group_id = NULL WHERE groups.isbuggroup = 1");
+ $dbh->do("UPDATE flagtypes LEFT JOIN groups ON flagtypes.request_group_id = groups.id SET request_group_id = NULL WHERE groups.isbuggroup = 1");
+ if ($keep_groups) {
+ $dbh->do("DELETE FROM groups WHERE isbuggroup = 1 AND id $keep_groups_sql");
+ } else {
+ $dbh->do("DELETE FROM groups WHERE isbuggroup = 1");
+ }
+}
+
+sub delete_sensitive_user_data {
+ # Remove sensitive user account data.
+ print "Deleting sensitive user account data...\n";
+ $dbh->do("UPDATE profiles SET cryptpassword = 'deleted'") unless $keep_passwords;
+ $dbh->do("DELETE FROM profiles_activity");
+ $dbh->do("DELETE FROM profile_search");
+ $dbh->do("DELETE FROM namedqueries");
+ $dbh->do("DELETE FROM tokens");
+ $dbh->do("DELETE FROM logincookies");
+ $dbh->do("DELETE FROM login_failure");
+ $dbh->do("DELETE FROM ts_error");
+ $dbh->do("DELETE FROM ts_exitstatus");
+ $dbh->do("DELETE FROM ts_funcmap");
+ $dbh->do("DELETE FROM ts_job");
+ $dbh->do("DELETE FROM ts_note");
+}
+
+sub delete_attachment_data {
+ # Delete unnecessary attachment data.
+ print "Removing attachment data to preserve disk space...\n";
+ $dbh->do("UPDATE attach_data SET thedata = ''");
+}
+
diff --git a/contrib/verify-user.pl b/contrib/verify-user.pl
new file mode 100644
index 000000000..d12cd745f
--- /dev/null
+++ b/contrib/verify-user.pl
@@ -0,0 +1,129 @@
+#!/usr/bin/perl -wT
+# -*- 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): Myk Melez <myk@mozilla.org>
+# Dave Miller <justdave@bugzilla.org>
+
+# See if a user account has ever done anything
+
+# ./verify-user.pl foo@baz.com
+
+use strict;
+
+use lib qw(.);
+
+use Bugzilla;
+use Bugzilla::Util;
+use Bugzilla::DB;
+use Bugzilla::Constants;
+
+# Make sure accounts were specified on the command line and exist.
+my $user = $ARGV[0] || die "You must specify an user.\n";
+my $dbh = Bugzilla->dbh;
+my $sth;
+
+#$sth = $dbh->prepare("SELECT name, count(*) as qty from bugs, products where reporter=198524 and product_id=products.id group by name order by qty desc");
+#$sth->execute();
+#my $results = $sth->fetchall_arrayref();
+#use Data::Dumper;
+#print Data::Dumper::Dumper($results);
+#exit;
+
+trick_taint($user);
+if ($user =~ /^\d+$/) { # user ID passed instead of email
+ $sth = $dbh->prepare('SELECT login_name FROM profiles WHERE userid = ?');
+ $sth->execute($user);
+ ($user) = $sth->fetchrow_array || die "The user with ID $ARGV[0] does not exist.\n";
+ print "User $ARGV[0]'s login name is $user.\n";
+}
+$sth = $dbh->prepare("SELECT userid FROM profiles WHERE login_name = ?");
+$sth->execute($user);
+my ($user_id) = $sth->fetchrow_array || die "The user $user does not exist.\n";
+
+print "${user}'s ID is $user_id.\n";
+
+$sth = $dbh->prepare("SELECT DISTINCT ipaddr FROM logincookies WHERE userid = ?");
+$sth->execute($user_id);
+my $iplist = $sth->fetchall_arrayref;
+if (@$iplist > 0) {
+ print "This user has recently connected from the following IP addresses:\n";
+ foreach my $ip (@$iplist) {
+ print $$ip[0] . "\n";
+ }
+}
+
+
+# A list of tables and columns to be checked.
+my $columns = {
+ attachments => ['submitter_id'] ,
+ bugs => ['assigned_to', 'reporter', 'qa_contact'] ,
+ bugs_activity => ['who'] ,
+ cc => ['who'] ,
+ components => ['initialowner', 'initialqacontact'] ,
+ flags => ['setter_id', 'requestee_id'] ,
+ logincookies => ['userid'] ,
+ longdescs => ['who'] ,
+ namedqueries => ['userid'] ,
+ profiles_activity => ['userid', 'who'] ,
+ quips => ['userid'] ,
+ series => ['creator'] ,
+ tokens => ['userid'] ,
+ user_group_map => ['user_id'] ,
+ votes => ['who'] ,
+ watch => ['watcher', 'watched'] ,
+
+};
+
+my $fields = 0;
+# Check records for user.
+foreach my $table (keys(%$columns)) {
+ foreach my $column (@{$columns->{$table}}) {
+ $sth = $dbh->prepare("SELECT COUNT(*) FROM $table WHERE $column = ?");
+ if ($table eq 'user_group_map') {
+ $sth = $dbh->prepare("SELECT COUNT(*) FROM $table WHERE $column = ? AND grant_type = " . GRANT_DIRECT);
+ }
+ $sth->execute($user_id);
+ my ($val) = $sth->fetchrow_array;
+ $fields++ if $val;
+ print "$table.$column: $val\n" if $val;
+ }
+}
+
+print "The user is mentioned in $fields fields.\n";
+
+if ($::ARGV[1] && $::ARGV[1] eq '-r') {
+ if ($fields == 0) {
+ $sth = $dbh->prepare("SELECT login_name FROM profiles WHERE login_name = ?");
+ my $count = 0;
+ print "Finding an unused recycle ID";
+ do {
+ $count++;
+ $sth->execute(sprintf("reuseme%03d\@bugzilla.org", $count));
+ print ".";
+ } while (my ($match) = $sth->fetchrow_array());
+ printf "\nUsing reuseme%03d\@bugzilla.org.\n", $count;
+ $dbh->do("DELETE FROM user_group_map WHERE user_id=?",undef,$user_id);
+ $dbh->do("UPDATE profiles SET realname='', cryptpassword='randomgarbage' WHERE userid=?",undef,$user_id);
+ $dbh->do("UPDATE profiles SET login_name=? WHERE userid=?",undef,sprintf("reuseme%03d\@bugzilla.org",$count),$user_id);
+ }
+ else {
+ print "Account has been used, so not recycling.\n";
+ }
+}