blob: 59df3fdbf0dc93b953c2615d430f16cdb10c6fa2 [file] [log] [blame]
#!/usr/bin/env 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 Gnats To Bugzilla Conversion Utility.
#
# The Initial Developer of the Original Code is Tom
# Schutter. Portions created by Tom Schutter are
# Copyright (C) 1999 Tom Schutter. All
# Rights Reserved.
#
# Contributor(s): Tom Schutter <tom@platte.com>
#
# Perl script to convert a GNATS database to a Bugzilla database.
# This script generates a file that contains SQL commands for MySQL.
# This script DOES NOT MODIFY the GNATS database.
# This script DOES NOT MODIFY the Bugzilla database.
#
# Usage procedure:
# 1) Regenerate the GNATS index file. It sometimes has inconsistencies,
# and this script relies on it being correct. Use the GNATS command:
# gen-index --numeric --outfile=$GNATS_DIR/gnats-db/gnats-adm/index
# 2) Modify variables at the beginning of this script to match
# what your site requires.
# 3) Modify translate_pr() and write_bugs() below to fixup mapping from
# your GNATS policies to Bugzilla. For example, how do the
# Severity/Priority fields map to bug_severity/priority?
# 4) Run this script.
# 5) Fix the problems in the GNATS database identified in the output
# script file gnats2bz_cleanup.sh. Fixing problems may be a job
# for a custom perl script. If you make changes to GNATS, goto step 2.
# 6) Examine the statistics in the output file gnats2bz_stats.txt.
# These may indicate some more cleanup that is needed. For example,
# you may find that there are invalid "State"s, or not a consistent
# scheme for "Release"s. If you make changes to GNATS, goto step 2.
# 7) Examine the output data file gnats2bz_data.sql. If problems
# exist, goto step 2.
# 8) Create a new, empty Bugzilla database.
# 9) Import the data using the command:
# mysql -uroot -p'ROOT_PASSWORD' bugs < gnats2bz_data.sql
# 10) Update the shadow directory with the command:
# cd $BUGZILLA_DIR; ./processmail regenerate
# 11) Run a sanity check by visiting the sanitycheck.cgi page.
# 12) Manually verify that the database is ok. If it is not, goto step 2.
#
# Important notes:
# Confidential is not mapped or exported.
# Submitter-Id is not mapped or exported.
#
# Design decisions:
# This script generates a SQL script file rather than dumping the data
# directly into the database. This is to allow the user to check
# and/or modify the results before they are put into the database.
# The PR number is very important and must be maintained as the Bugzilla
# bug number, because there are many references to the PR number, such
# as in code comments, CVS comments, customer communications, etc.
# Reading ENUMERATED and TEXT fields:
# 1) All leading and trailing whitespace is stripped.
# Reading MULTITEXT fields:
# 1) All leading blank lines are stripped.
# 2) All trailing whitespace is stripped.
# 3) Indentation is preserved.
# Audit-Trail is not mapped to bugs_activity table, because there
# is no place to put the "Why" text, which can have a fair amount
# of information content.
#
# 15 January 2002 - changes from Andrea Dell'Amico <adellam@link.it>
#
# * Adapted to the new database structure: now long_descs is a
# separate table.
# * Set a default for the target milestone, otherwise bugzilla
# doesn't work with the imported database if milestones are used.
# * In gnats version 3.113 records are separated by "|" and not ":".
# * userid "1" is for the bugzilla administrator, so it's better to
# start from 2.
#
use strict;
# Suffix to be appended to username to make it an email address.
my($username_suffix) = "\@platte.com";
# Default organization that should be ignored and not passed on to Bugzilla.
# Only bugs that are reported outside of the default organization will have
# their Originator,Organization fields passed on.
# The assumption here is that if the Organization is identical to the
# $default_organization, then the Originator will most likely be only an
# alias for the From field in the mail header.
my($default_organization) = "Platte River Associates|platte";
# Username for reporter field if unable to determine from mail header
my($gnats_username) = "gnats\@platte.com";
# Flag indicating if cleanup file should use edit-pr or ${EDITOR}.
# Using edit-pr is safer, but may be too slow if there are too many
# PRs that need cleanup. If you use ${EDITOR}, then you must make
# sure that you have exclusive access to the database, and that you
# do not screw up any fields.
my($cleanup_with_edit_pr) = 0;
# Component name and description for bugs imported from GNATS.
my($default_component) = "GNATS Import";
my($default_component_description) = "Bugs imported from GNATS.";
# First generated userid. Start from 2: 1 is used for the bugzilla
# administrator.
my($userid_base) = 2;
# Output filenames.
my($cleanup_pathname) = "gnats2bz_cleanup.sh";
my($stats_pathname) = "gnats2bz_stats.txt";
my($data_pathname) = "gnats2bz_data.sql";
# List of ENUMERATED and TEXT fields.
my(@text_fields) = qw(Number Category Synopsis Confidential Severity
Priority Responsible State Class Submitter-Id
Arrival-Date Originator Release);
# List of MULTITEXT fields.
my(@multitext_fields) = qw(Mail-Header Organization Environment Description
How-To-Repeat Fix Audit-Trail Unformatted);
# List of fields to report statistics for.
my(@statistics_fields) = qw(Category Confidential Severity Priority
Responsible State Class Submitter-Id Originator
Organization Release Environment);
# Array to hold list of GNATS PRs.
my(@pr_list);
# Array to hold list of GNATS categories.
my(@categories_list);
# Array to hold list of GNATS responsible users.
my(@responsible_list);
# Array to hold list of usernames.
my(@username_list);
# Put the gnats_username in first.
get_userid($gnats_username);
# Hash to hold list of versions.
my(%versions_table);
# Hash to hold contents of PR.
my(%pr_data);
# String to hold duplicate fields found during read of PR.
my($pr_data_dup_fields) = "";
# String to hold badly labeled fields found during read of PR.
# This usually happens when the user does not separate the field name
# from the field data with whitespace.
my($pr_data_bad_fields) = " ";
# Hash to hold statistics (note that this a hash of hashes).
my(%pr_stats);
# Process commmand line.
my($gnats_db_dir) = @ARGV;
defined($gnats_db_dir) || die "gnats-db dir not specified";
(-d $gnats_db_dir) || die "$gnats_db_dir is not a directory";
# Load @pr_list from GNATS index file.
my($index_pathname) = $gnats_db_dir . "/gnats-adm/index";
(-f $index_pathname) || die "$index_pathname not found";
print "Reading $index_pathname...\n";
if (!load_index($index_pathname)) {
return(0);
}
# Load @category_list from GNATS categories file.
my($categories_pathname) = $gnats_db_dir . "/gnats-adm/categories";
(-f $categories_pathname) || die "$categories_pathname not found";
print "Reading $categories_pathname...\n";
if (!load_categories($categories_pathname)) {
return(0);
}
# Load @responsible_list from GNATS responsible file.
my($responsible_pathname) = $gnats_db_dir . "/gnats-adm/responsible";
(-f $responsible_pathname) || die "$responsible_pathname not found";
print "Reading $responsible_pathname...\n";
if (!load_responsible($responsible_pathname)) {
return(0);
}
# Open cleanup file.
open(CLEANUP, ">$cleanup_pathname") ||
die "Unable to open $cleanup_pathname: $!";
chmod(0744, $cleanup_pathname) || warn "Unable to chmod $cleanup_pathname: $!";
print CLEANUP "#!/bin/sh\n";
print CLEANUP "# List of PRs that have problems found by gnats2bz.pl.\n";
# Open data file.
open(DATA, ">$data_pathname") || die "Unable to open $data_pathname: $!";
print DATA "-- Exported data from $gnats_db_dir by gnats2bz.pl.\n";
print DATA "-- Load it into a Bugzilla database using the command:\n";
print DATA "-- mysql -uroot -p'ROOT_PASSWORD' bugs < gnats2bz_data.sql\n";
print DATA "--\n";
# Loop over @pr_list.
my($pr);
foreach $pr (@pr_list) {
print "Processing $pr...\n";
if (!read_pr("$gnats_db_dir/$pr")) {
next;
}
translate_pr();
check_pr($pr);
collect_stats();
update_versions();
write_bugs();
write_longdescs();
}
write_non_bugs_tables();
close(CLEANUP) || die "Unable to close $cleanup_pathname: $!";
close(DATA) || die "Unable to close $data_pathname: $!";
print "Generating $stats_pathname...\n";
report_stats();
sub load_index {
my($pathname) = @_;
my($record);
my(@fields);
open(INDEX, $pathname) || die "Unable to open $pathname: $!";
while ($record = <INDEX>) {
@fields = split(/\|/, $record);
push(@pr_list, $fields[0]);
}
close(INDEX) || die "Unable to close $pathname: $!";
return(1);
}
sub load_categories {
my($pathname) = @_;
my($record);
open(CATEGORIES, $pathname) || die "Unable to open $pathname: $!";
while ($record = <CATEGORIES>) {
if ($record =~ /^#/) {
next;
}
push(@categories_list, [split(/:/, $record)]);
}
close(CATEGORIES) || die "Unable to close $pathname: $!";
return(1);
}
sub load_responsible {
my($pathname) = @_;
my($record);
open(RESPONSIBLE, $pathname) || die "Unable to open $pathname: $!";
while ($record = <RESPONSIBLE>) {
if ($record =~ /^#/) {
next;
}
push(@responsible_list, [split(/\|/, $record)]);
}
close(RESPONSIBLE) || die "Unable to close $pathname: $!";
return(1);
}
sub read_pr {
my($pr_filename) = @_;
my($multitext) = "Mail-Header";
my($field, $mail_header);
# Empty the hash.
%pr_data = ();
# Empty the list of duplicate fields.
$pr_data_dup_fields = "";
# Empty the list of badly labeled fields.
$pr_data_bad_fields = "";
unless (open(PR, $pr_filename)) {
warn "error opening $pr_filename: $!";
return(0);
}
LINELOOP: while (<PR>) {
chomp;
if ($multitext eq "Unformatted") {
# once we reach "Unformatted", rest of file goes there
$pr_data{$multitext} = append_multitext($pr_data{$multitext}, $_);
next LINELOOP;
}
# Handle ENUMERATED and TEXT fields.
foreach $field (@text_fields) {
if (/^>$field:($|\s+)/) {
$pr_data{$field} = $'; # part of string after match
$pr_data{$field} =~ s/\s+$//; # strip trailing whitespace
$multitext = "";
next LINELOOP;
}
}
# Handle MULTITEXT fields.
foreach $field (@multitext_fields) {
if (/^>$field:\s*$/) {
$_ = $'; # set to part of string after match part
if (defined($pr_data{$field})) {
if ($pr_data_dup_fields eq "") {
$pr_data_dup_fields = $field;
} else {
$pr_data_dup_fields = "$pr_data_dup_fields $field";
}
}
$pr_data{$field} = $_;
$multitext = $field;
next LINELOOP;
}
}
# Check for badly labeled fields.
foreach $field ((@text_fields, @multitext_fields)) {
if (/^>$field:/) {
if ($pr_data_bad_fields eq "") {
$pr_data_bad_fields = $field;
} else {
$pr_data_bad_fields = "$pr_data_bad_fields $field";
}
}
}
# Handle continued MULTITEXT field.
$pr_data{$multitext} = append_multitext($pr_data{$multitext}, $_);
}
close(PR) || warn "error closing $pr_filename: $!";
# Strip trailing newlines from MULTITEXT fields.
foreach $field (@multitext_fields) {
if (defined($pr_data{$field})) {
$pr_data{$field} =~ s/\s+$//;
}
}
return(1);
}
sub append_multitext {
my($original, $addition) = @_;
if (defined($original) && $original ne "") {
return "$original\n$addition";
} else {
return $addition;
}
}
sub check_pr {
my($pr) = @_;
my($error_list) = "";
if ($pr_data_dup_fields ne "") {
$error_list = append_error($error_list, "Multiple '$pr_data_dup_fields'");
}
if ($pr_data_bad_fields ne "") {
$error_list = append_error($error_list, "Bad field labels '$pr_data_bad_fields'");
}
if (!defined($pr_data{"Description"}) || $pr_data{"Description"} eq "") {
$error_list = append_error($error_list, "Description empty");
}
if (defined($pr_data{"Unformatted"}) && $pr_data{"Unformatted"} ne "") {
$error_list = append_error($error_list, "Unformatted text");
}
if (defined($pr_data{"Release"}) && length($pr_data{"Release"}) > 16) {
$error_list = append_error($error_list, "Release > 16 chars");
}
if (defined($pr_data{"Fix"}) && $pr_data{"Fix"} =~ /State-Changed-/) {
$error_list = append_error($error_list, "Audit in Fix field");
}
if (defined($pr_data{"Arrival-Date"})) {
if ($pr_data{"Arrival-Date"} eq "") {
$error_list = append_error($error_list, "Arrival-Date empty");
} elsif (unixdate2datetime($pr, $pr_data{"Arrival-Date"}) eq "") {
$error_list = append_error($error_list, "Arrival-Date format");
}
}
# More checks should go here.
if ($error_list ne "") {
if ($cleanup_with_edit_pr) {
my(@parts) = split("/", $pr);
my($pr_num) = $parts[1];
print CLEANUP "echo \"$error_list\"; edit-pr $pr_num\n";
} else {
print CLEANUP "echo \"$error_list\"; \${EDITOR} $pr\n";
}
}
}
sub append_error {
my($original, $addition) = @_;
if ($original ne "") {
return "$original, $addition";
} else {
return $addition;
}
}
sub translate_pr {
# This function performs GNATS -> Bugzilla translations that should
# happen before collect_stats().
if (!defined($pr_data{"Organization"})) {
$pr_data{"Originator"} = "";
}
if ($pr_data{"Organization"} =~ /$default_organization/) {
$pr_data{"Originator"} = "";
$pr_data{"Organization"} = "";
}
$pr_data{"Organization"} =~ s/^\s+//g; # strip leading whitespace
if (!defined($pr_data{"Release"}) ||
$pr_data{"Release"} eq "" ||
$pr_data{"Release"} =~ /^unknown-1.0$/
) {
$pr_data{"Release"} = "unknown";
}
if (defined($pr_data{"Responsible"})) {
$pr_data{"Responsible"} =~ /\w+/;
$pr_data{"Responsible"} = "$&$username_suffix";
}
my($rep_platform, $op_sys) = ("All", "All");
if (defined($pr_data{"Environment"})) {
if ($pr_data{"Environment"} =~ /[wW]in.*NT/) {
$rep_platform = "PC";
$op_sys = "Windows NT";
} elsif ($pr_data{"Environment"} =~ /[wW]in.*95/) {
$rep_platform = "PC";
$op_sys = "Windows 95";
} elsif ($pr_data{"Environment"} =~ /[wW]in.*98/) {
$rep_platform = "PC";
$op_sys = "Windows 98";
} elsif ($pr_data{"Environment"} =~ /OSF/) {
$rep_platform = "DEC";
$op_sys = "OSF/1";
} elsif ($pr_data{"Environment"} =~ /AIX/) {
$rep_platform = "RS/6000";
$op_sys = "AIX";
} elsif ($pr_data{"Environment"} =~ /IRIX/) {
$rep_platform = "SGI";
$op_sys = "IRIX";
} elsif ($pr_data{"Environment"} =~ /SunOS.*5\.\d/) {
$rep_platform = "Sun";
$op_sys = "Solaris";
} elsif ($pr_data{"Environment"} =~ /SunOS.*4\.\d/) {
$rep_platform = "Sun";
$op_sys = "SunOS";
}
}
$pr_data{"Environment"} = "$rep_platform:$op_sys";
}
sub collect_stats {
my($field, $value);
foreach $field (@statistics_fields) {
$value = $pr_data{$field};
if (!defined($value)) {
$value = "";
}
if (defined($pr_stats{$field}{$value})) {
$pr_stats{$field}{$value}++;
} else {
$pr_stats{$field}{$value} = 1;
}
}
}
sub report_stats {
my($field, $value, $count);
open(STATS, ">$stats_pathname") ||
die "Unable to open $stats_pathname: $!";
print STATS "Statistics of $gnats_db_dir collated by gnats2bz.pl.\n";
my($field_stats);
while (($field, $field_stats) = each(%pr_stats)) {
print STATS "\n$field:\n";
while (($value, $count) = each(%$field_stats)) {
print STATS " $value: $count\n";
}
}
close(STATS) || die "Unable to close $stats_pathname: $!";
}
sub get_userid {
my($responsible) = @_;
my($username, $userid);
if (!defined($responsible)) {
return(-1);
}
# Search for current username in the list.
$userid = $userid_base;
foreach $username (@username_list) {
if ($username eq $responsible) {
return($userid);
}
$userid++;
}
push(@username_list, $responsible);
return($userid);
}
sub update_versions {
if (!defined($pr_data{"Release"}) || !defined($pr_data{"Category"})) {
return;
}
my($curr_product) = $pr_data{"Category"};
my($curr_version) = $pr_data{"Release"};
if ($curr_version eq "") {
return;
}
if (!defined($versions_table{$curr_product})) {
$versions_table{$curr_product} = [ ];
}
my($version_list) = $versions_table{$curr_product};
my($version);
foreach $version (@$version_list) {
if ($version eq $curr_version) {
return;
}
}
push(@$version_list, $curr_version);
}
sub write_bugs {
my($bug_id) = $pr_data{"Number"};
my($userid) = get_userid($pr_data{"Responsible"});
# Mapping from Class,Severity to bug_severity
# At our site, the Severity,Priority fields have degenerated
# into a 9-level priority field.
my($bug_severity) = "normal";
if ($pr_data{"Class"} eq "change-request") {
$bug_severity = "enhancement";
} elsif (defined($pr_data{"Synopsis"})) {
if ($pr_data{"Synopsis"} =~ /crash|assert/i) {
$bug_severity = "critical";
} elsif ($pr_data{"Synopsis"} =~ /wrong|error/i) {
$bug_severity = "major";
}
}
$bug_severity = SqlQuote($bug_severity);
# Mapping from Severity,Priority to priority
# At our site, the Severity,Priority fields have degenerated
# into a 9-level priority field.
my($priority) = "P1";
if (defined($pr_data{"Severity"}) && defined($pr_data{"Severity"})) {
if ($pr_data{"Severity"} eq "critical") {
if ($pr_data{"Priority"} eq "high") {
$priority = "P1";
} else {
$priority = "P2";
}
} elsif ($pr_data{"Severity"} eq "serious") {
if ($pr_data{"Priority"} eq "low") {
$priority = "P4";
} else {
$priority = "P3";
}
} else {
if ($pr_data{"Priority"} eq "high") {
$priority = "P4";
} else {
$priority = "P5";
}
}
}
$priority = SqlQuote($priority);
# Map State,Class to bug_status,resolution
my($bug_status, $resolution);
if ($pr_data{"State"} eq "open" || $pr_data{"State"} eq "analyzed") {
$bug_status = "ASSIGNED";
$resolution = "";
} elsif ($pr_data{"State"} eq "feedback") {
$bug_status = "RESOLVED";
$resolution = "FIXED";
} elsif ($pr_data{"State"} eq "closed") {
$bug_status = "CLOSED";
if (defined($pr_data{"Class"}) && $pr_data{"Class"} =~ /^duplicate/) {
$resolution = "DUPLICATE";
} elsif (defined($pr_data{"Class"}) && $pr_data{"Class"} =~ /^mistaken/) {
$resolution = "INVALID";
} else {
$resolution = "FIXED";
}
} elsif ($pr_data{"State"} eq "suspended") {
$bug_status = "RESOLVED";
$resolution = "WONTFIX";
} else {
$bug_status = "NEW";
$resolution = "";
}
$bug_status = SqlQuote($bug_status);
$resolution = SqlQuote($resolution);
my($creation_ts) = "";
if (defined($pr_data{"Arrival-Date"}) && $pr_data{"Arrival-Date"} ne "") {
$creation_ts = unixdate2datetime($bug_id, $pr_data{"Arrival-Date"});
}
$creation_ts = SqlQuote($creation_ts);
my($delta_ts) = "";
if (defined($pr_data{"Audit-Trail"})) {
# note that (?:.|\n)+ is greedy, so this should match the
# last Changed-When
if ($pr_data{"Audit-Trail"} =~ /(?:.|\n)+-Changed-When: (.+)/) {
$delta_ts = unixdate2timestamp($bug_id, $1);
}
}
if ($delta_ts eq "") {
if (defined($pr_data{"Arrival-Date"}) && $pr_data{"Arrival-Date"} ne "") {
$delta_ts = unixdate2timestamp($bug_id, $pr_data{"Arrival-Date"});
}
}
$delta_ts = SqlQuote($delta_ts);
my($short_desc) = SqlQuote($pr_data{"Synopsis"});
my($rep_platform, $op_sys) = split(/\|/, $pr_data{"Environment"});
$rep_platform = SqlQuote($rep_platform);
$op_sys = SqlQuote($op_sys);
my($reporter) = get_userid($gnats_username);
if (
defined($pr_data{"Mail-Header"}) &&
$pr_data{"Mail-Header"} =~ /From ([\w.]+\@[\w.]+)/
) {
$reporter = get_userid($1);
}
my($version) = "";
if (defined($pr_data{"Release"})) {
$version = substr($pr_data{"Release"}, 0, 16);
}
$version = SqlQuote($version);
my($product) = "";
if (defined($pr_data{"Category"})) {
$product = $pr_data{"Category"};
}
$product = SqlQuote($product);
my($component) = SqlQuote($default_component);
my($target_milestone) = "0";
# $target_milestone = SqlQuote($target_milestone);
my($qa_contact) = "0";
# my($bug_file_loc) = "";
# $bug_file_loc = SqlQuote($bug_file_loc);
# my($status_whiteboard) = "";
# $status_whiteboard = SqlQuote($status_whiteboard);
print DATA "\ninsert into bugs (\n";
print DATA " bug_id, assigned_to, bug_severity, priority, bug_status, creation_ts, delta_ts,\n";
print DATA " short_desc,\n";
print DATA " rep_platform, op_sys, reporter, version,\n";
print DATA " product, component, resolution, target_milestone, qa_contact\n";
print DATA ") values (\n";
print DATA " $bug_id, $userid, $bug_severity, $priority, $bug_status, $creation_ts, $delta_ts,\n";
print DATA " $short_desc,\n";
print DATA " $rep_platform, $op_sys, $reporter, $version,\n";
print DATA " $product, $component, $resolution, $target_milestone, $qa_contact\n";
print DATA ");\n";
}
sub write_longdescs {
my($bug_id) = $pr_data{"Number"};
my($who) = get_userid($pr_data{"Responsible"});;
my($bug_when) = "";
if (defined($pr_data{"Arrival-Date"}) && $pr_data{"Arrival-Date"} ne "") {
$bug_when = unixdate2datetime($bug_id, $pr_data{"Arrival-Date"});
}
$bug_when = SqlQuote($bug_when);
my($thetext) = $pr_data{"Description"};
if (defined($pr_data{"How-To-Repeat"}) && $pr_data{"How-To-Repeat"} ne "") {
$thetext =
$thetext . "\n\nHow-To-Repeat:\n" . $pr_data{"How-To-Repeat"};
}
if (defined($pr_data{"Fix"}) && $pr_data{"Fix"} ne "") {
$thetext = $thetext . "\n\nFix:\n" . $pr_data{"Fix"};
}
if (defined($pr_data{"Originator"}) && $pr_data{"Originator"} ne "") {
$thetext = $thetext . "\n\nOriginator:\n" . $pr_data{"Originator"};
}
if (defined($pr_data{"Organization"}) && $pr_data{"Organization"} ne "") {
$thetext = $thetext . "\n\nOrganization:\n" . $pr_data{"Organization"};
}
if (defined($pr_data{"Audit-Trail"}) && $pr_data{"Audit-Trail"} ne "") {
$thetext = $thetext . "\n\nAudit-Trail:\n" . $pr_data{"Audit-Trail"};
}
if (defined($pr_data{"Unformatted"}) && $pr_data{"Unformatted"} ne "") {
$thetext = $thetext . "\n\nUnformatted:\n" . $pr_data{"Unformatted"};
}
$thetext = SqlQuote($thetext);
print DATA "\ninsert into longdescs (\n";
print DATA " bug_id, who, bug_when, thetext\n";
print DATA ") values (\n";
print DATA " $bug_id, $who, $bug_when, $thetext\n";
print DATA ");\n";
}
sub write_non_bugs_tables {
my($categories_record);
foreach $categories_record (@categories_list) {
my($component) = SqlQuote($default_component);
my($product) = SqlQuote(@$categories_record[0]);
my($description) = SqlQuote(@$categories_record[1]);
my($initialowner) = SqlQuote(@$categories_record[2] . $username_suffix);
print DATA "\ninsert into products (\n";
print DATA
" product, description, milestoneurl, disallownew\n";
print DATA ") values (\n";
print DATA
" $product, $description, '', 0\n";
print DATA ");\n";
print DATA "\ninsert into components (\n";
print DATA
" value, program, initialowner, initialqacontact, description\n";
print DATA ") values (\n";
print DATA
" $component, $product, $initialowner, '', $description\n";
print DATA ");\n";
print DATA "\ninsert into milestones (\n";
print DATA
" value, product, sortkey\n";
print DATA ") values (\n";
print DATA
" 0, $product, 0\n";
print DATA ");\n";
}
my($username);
my($userid) = $userid_base;
my($password) = "password";
my($realname);
my($groupset) = 0;
foreach $username (@username_list) {
$realname = map_username_to_realname($username);
$username = SqlQuote($username);
$realname = SqlQuote($realname);
print DATA "\ninsert into profiles (\n";
print DATA
" userid, login_name, cryptpassword, realname, groupset\n";
print DATA ") values (\n";
print DATA
" $userid, $username, encrypt('$password'), $realname, $groupset\n";
print DATA ");\n";
$userid++;
}
my($product);
my($version_list);
while (($product, $version_list) = each(%versions_table)) {
$product = SqlQuote($product);
my($version);
foreach $version (@$version_list) {
$version = SqlQuote($version);
print DATA "\ninsert into versions (value, program) ";
print DATA "values ($version, $product);\n";
}
}
}
sub map_username_to_realname() {
my($username) = @_;
my($name, $realname);
# get the portion before the @
$name = $username;
$name =~ s/\@.*//;
my($responsible_record);
foreach $responsible_record (@responsible_list) {
if (@$responsible_record[0] eq $name) {
return(@$responsible_record[1]);
}
if (defined(@$responsible_record[2])) {
if (@$responsible_record[2] eq $username) {
return(@$responsible_record[1]);
}
}
}
return("");
}
sub detaint_string {
my ($str) = @_;
$str =~ m/^(.*)$/s;
$str = $1;
}
sub SqlQuote {
my ($str) = (@_);
$str =~ s/([\\\'])/\\$1/g;
$str =~ s/\0/\\0/g;
# If it's been SqlQuote()ed, then it's safe, so we tell -T that.
$str = detaint_string($str);
return "'$str'";
}
sub unixdate2datetime {
my($bugid, $unixdate) = @_;
my($year, $month, $day, $hour, $min, $sec);
if (!split_unixdate($bugid, $unixdate, \$year, \$month, \$day, \$hour, \$min, \$sec)) {
return("");
}
return("$year-$month-$day $hour:$min:$sec");
}
sub unixdate2timestamp {
my($bugid, $unixdate) = @_;
my($year, $month, $day, $hour, $min, $sec);
if (!split_unixdate($bugid, $unixdate, \$year, \$month, \$day, \$hour, \$min, \$sec)) {
return("");
}
return("$year$month$day$hour$min$sec");
}
sub split_unixdate {
# "Tue Jun 6 14:50:00 1995"
# "Mon Nov 20 17:03:11 [MST] 1995"
# "12/13/94"
# "jan 1, 1995"
my($bugid, $unixdate, $year, $month, $day, $hour, $min, $sec) = @_;
my(@parts);
$$hour = "00";
$$min = "00";
$$sec = "00";
@parts = split(/ +/, $unixdate);
if (@parts >= 5) {
# year
$$year = $parts[4];
if ($$year =~ /[A-Z]{3}/) {
# Must be timezone, try next field.
$$year = $parts[5];
}
if ($$year =~ /\D/) {
warn "$bugid: Error processing year part '$$year' of date '$unixdate'\n";
return(0);
}
if ($$year < 30) {
$$year = "20" . $$year;
} elsif ($$year < 100) {
$$year = "19" . $$year;
} elsif ($$year < 1970 || $$year > 2029) {
warn "$bugid: Error processing year part '$$year' of date '$unixdate'\n";
return(0);
}
# month
$$month = $parts[1];
if ($$month =~ /\D/) {
if (!month2number($month)) {
warn "$bugid: Error processing month part '$$month' of date '$unixdate'\n";
return(0);
}
} elsif ($$month < 1 || $$month > 12) {
warn "$bugid: Error processing month part '$$month' of date '$unixdate'\n";
return(0);
} elsif (length($$month) == 1) {
$$month = "0" . $$month;
}
# day
$$day = $parts[2];
if ($$day < 1 || $$day > 31) {
warn "$bugid: Error processing day part '$day' of date '$unixdate'\n";
return(0);
} elsif (length($$day) == 1) {
$$day = "0" . $$day;
}
@parts = split(/:/, $parts[3]);
$$hour = $parts[0];
$$min = $parts[1];
$$sec = $parts[2];
return(1);
} elsif (@parts == 3) {
# year
$$year = $parts[2];
if ($$year =~ /\D/) {
warn "$bugid: Error processing year part '$$year' of date '$unixdate'\n";
return(0);
}
if ($$year < 30) {
$$year = "20" . $$year;
} elsif ($$year < 100) {
$$year = "19" . $$year;
} elsif ($$year < 1970 || $$year > 2029) {
warn "$bugid: Error processing year part '$$year' of date '$unixdate'\n";
return(0);
}
# month
$$month = $parts[0];
if ($$month =~ /\D/) {
if (!month2number($month)) {
warn "$bugid: Error processing month part '$$month' of date '$unixdate'\n";
return(0);
}
} elsif ($$month < 1 || $$month > 12) {
warn "$bugid: Error processing month part '$$month' of date '$unixdate'\n";
return(0);
} elsif (length($$month) == 1) {
$$month = "0" . $$month;
}
# day
$$day = $parts[1];
$$day =~ s/,//;
if ($$day < 1 || $$day > 31) {
warn "$bugid: Error processing day part '$day' of date '$unixdate'\n";
return(0);
} elsif (length($$day) == 1) {
$$day = "0" . $$day;
}
return(1);
}
@parts = split(/:/, $unixdate);
if (@parts == 3 && length($unixdate) <= 8) {
$$year = "19" . $parts[2];
$$month = $parts[0];
if (length($$month) == 1) {
$$month = "0" . $$month;
}
$$day = $parts[1];
if (length($$day) == 1) {
$$day = "0" . $$day;
}
return(1);
}
warn "$bugid: Error processing date '$unixdate'\n";
return(0);
}
sub month2number {
my($month) = @_;
if ($$month =~ /jan/i) {
$$month = "01";
} elsif ($$month =~ /feb/i) {
$$month = "02";
} elsif ($$month =~ /mar/i) {
$$month = "03";
} elsif ($$month =~ /apr/i) {
$$month = "04";
} elsif ($$month =~ /may/i) {
$$month = "05";
} elsif ($$month =~ /jun/i) {
$$month = "06";
} elsif ($$month =~ /jul/i) {
$$month = "07";
} elsif ($$month =~ /aug/i) {
$$month = "08";
} elsif ($$month =~ /sep/i) {
$$month = "09";
} elsif ($$month =~ /oct/i) {
$$month = "10";
} elsif ($$month =~ /nov/i) {
$$month = "11";
} elsif ($$month =~ /dec/i) {
$$month = "12";
} else {
return(0);
}
return(1);
}