gnats2bz.pl 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068
  1. #!/usr/bin/env perl -w
  2. # -*- Mode: perl; indent-tabs-mode: nil -*-
  3. #
  4. # The contents of this file are subject to the Mozilla Public
  5. # License Version 1.1 (the "License"); you may not use this file
  6. # except in compliance with the License. You may obtain a copy of
  7. # the License at http://www.mozilla.org/MPL/
  8. #
  9. # Software distributed under the License is distributed on an "AS
  10. # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11. # implied. See the License for the specific language governing
  12. # rights and limitations under the License.
  13. #
  14. # The Original Code is the Gnats To Bugzilla Conversion Utility.
  15. #
  16. # The Initial Developer of the Original Code is Tom
  17. # Schutter. Portions created by Tom Schutter are
  18. # Copyright (C) 1999 Tom Schutter. All
  19. # Rights Reserved.
  20. #
  21. # Contributor(s): Tom Schutter <tom@platte.com>
  22. #
  23. # Perl script to convert a GNATS database to a Bugzilla database.
  24. # This script generates a file that contains SQL commands for MySQL.
  25. # This script DOES NOT MODIFY the GNATS database.
  26. # This script DOES NOT MODIFY the Bugzilla database.
  27. #
  28. # Usage procedure:
  29. # 1) Regenerate the GNATS index file. It sometimes has inconsistencies,
  30. # and this script relies on it being correct. Use the GNATS command:
  31. # gen-index --numeric --outfile=$GNATS_DIR/gnats-db/gnats-adm/index
  32. # 2) Modify variables at the beginning of this script to match
  33. # what your site requires.
  34. # 3) Modify translate_pr() and write_bugs() below to fixup mapping from
  35. # your GNATS policies to Bugzilla. For example, how do the
  36. # Severity/Priority fields map to bug_severity/priority?
  37. # 4) Run this script.
  38. # 5) Fix the problems in the GNATS database identified in the output
  39. # script file gnats2bz_cleanup.sh. Fixing problems may be a job
  40. # for a custom perl script. If you make changes to GNATS, goto step 2.
  41. # 6) Examine the statistics in the output file gnats2bz_stats.txt.
  42. # These may indicate some more cleanup that is needed. For example,
  43. # you may find that there are invalid "State"s, or not a consistent
  44. # scheme for "Release"s. If you make changes to GNATS, goto step 2.
  45. # 7) Examine the output data file gnats2bz_data.sql. If problems
  46. # exist, goto step 2.
  47. # 8) Create a new, empty Bugzilla database.
  48. # 9) Import the data using the command:
  49. # mysql -uroot -p'ROOT_PASSWORD' bugs < gnats2bz_data.sql
  50. # 10) Update the shadow directory with the command:
  51. # cd $BUGZILLA_DIR; ./processmail regenerate
  52. # 11) Run a sanity check by visiting the sanitycheck.cgi page.
  53. # 12) Manually verify that the database is ok. If it is not, goto step 2.
  54. #
  55. # Important notes:
  56. # Confidential is not mapped or exported.
  57. # Submitter-Id is not mapped or exported.
  58. #
  59. # Design decisions:
  60. # This script generates a SQL script file rather than dumping the data
  61. # directly into the database. This is to allow the user to check
  62. # and/or modify the results before they are put into the database.
  63. # The PR number is very important and must be maintained as the Bugzilla
  64. # bug number, because there are many references to the PR number, such
  65. # as in code comments, CVS comments, customer communications, etc.
  66. # Reading ENUMERATED and TEXT fields:
  67. # 1) All leading and trailing whitespace is stripped.
  68. # Reading MULTITEXT fields:
  69. # 1) All leading blank lines are stripped.
  70. # 2) All trailing whitespace is stripped.
  71. # 3) Indentation is preserved.
  72. # Audit-Trail is not mapped to bugs_activity table, because there
  73. # is no place to put the "Why" text, which can have a fair amount
  74. # of information content.
  75. #
  76. # 15 January 2002 - changes from Andrea Dell'Amico <adellam@link.it>
  77. #
  78. # * Adapted to the new database structure: now long_descs is a
  79. # separate table.
  80. # * Set a default for the target milestone, otherwise bugzilla
  81. # doesn't work with the imported database if milestones are used.
  82. # * In gnats version 3.113 records are separated by "|" and not ":".
  83. # * userid "1" is for the bugzilla administrator, so it's better to
  84. # start from 2.
  85. #
  86. use strict;
  87. # Suffix to be appended to username to make it an email address.
  88. my($username_suffix) = "\@platte.com";
  89. # Default organization that should be ignored and not passed on to Bugzilla.
  90. # Only bugs that are reported outside of the default organization will have
  91. # their Originator,Organization fields passed on.
  92. # The assumption here is that if the Organization is identical to the
  93. # $default_organization, then the Originator will most likely be only an
  94. # alias for the From field in the mail header.
  95. my($default_organization) = "Platte River Associates|platte";
  96. # Username for reporter field if unable to determine from mail header
  97. my($gnats_username) = "gnats\@platte.com";
  98. # Flag indicating if cleanup file should use edit-pr or ${EDITOR}.
  99. # Using edit-pr is safer, but may be too slow if there are too many
  100. # PRs that need cleanup. If you use ${EDITOR}, then you must make
  101. # sure that you have exclusive access to the database, and that you
  102. # do not screw up any fields.
  103. my($cleanup_with_edit_pr) = 0;
  104. # Component name and description for bugs imported from GNATS.
  105. my($default_component) = "GNATS Import";
  106. my($default_component_description) = "Bugs imported from GNATS.";
  107. # First generated userid. Start from 2: 1 is used for the bugzilla
  108. # administrator.
  109. my($userid_base) = 2;
  110. # Output filenames.
  111. my($cleanup_pathname) = "gnats2bz_cleanup.sh";
  112. my($stats_pathname) = "gnats2bz_stats.txt";
  113. my($data_pathname) = "gnats2bz_data.sql";
  114. # List of ENUMERATED and TEXT fields.
  115. my(@text_fields) = qw(Number Category Synopsis Confidential Severity
  116. Priority Responsible State Class Submitter-Id
  117. Arrival-Date Originator Release);
  118. # List of MULTITEXT fields.
  119. my(@multitext_fields) = qw(Mail-Header Organization Environment Description
  120. How-To-Repeat Fix Audit-Trail Unformatted);
  121. # List of fields to report statistics for.
  122. my(@statistics_fields) = qw(Category Confidential Severity Priority
  123. Responsible State Class Submitter-Id Originator
  124. Organization Release Environment);
  125. # Array to hold list of GNATS PRs.
  126. my(@pr_list);
  127. # Array to hold list of GNATS categories.
  128. my(@categories_list);
  129. # Array to hold list of GNATS responsible users.
  130. my(@responsible_list);
  131. # Array to hold list of usernames.
  132. my(@username_list);
  133. # Put the gnats_username in first.
  134. get_userid($gnats_username);
  135. # Hash to hold list of versions.
  136. my(%versions_table);
  137. # Hash to hold contents of PR.
  138. my(%pr_data);
  139. # String to hold duplicate fields found during read of PR.
  140. my($pr_data_dup_fields) = "";
  141. # String to hold badly labeled fields found during read of PR.
  142. # This usually happens when the user does not separate the field name
  143. # from the field data with whitespace.
  144. my($pr_data_bad_fields) = " ";
  145. # Hash to hold statistics (note that this a hash of hashes).
  146. my(%pr_stats);
  147. # Process commmand line.
  148. my($gnats_db_dir) = @ARGV;
  149. defined($gnats_db_dir) || die "gnats-db dir not specified";
  150. (-d $gnats_db_dir) || die "$gnats_db_dir is not a directory";
  151. # Load @pr_list from GNATS index file.
  152. my($index_pathname) = $gnats_db_dir . "/gnats-adm/index";
  153. (-f $index_pathname) || die "$index_pathname not found";
  154. print "Reading $index_pathname...\n";
  155. if (!load_index($index_pathname)) {
  156. return(0);
  157. }
  158. # Load @category_list from GNATS categories file.
  159. my($categories_pathname) = $gnats_db_dir . "/gnats-adm/categories";
  160. (-f $categories_pathname) || die "$categories_pathname not found";
  161. print "Reading $categories_pathname...\n";
  162. if (!load_categories($categories_pathname)) {
  163. return(0);
  164. }
  165. # Load @responsible_list from GNATS responsible file.
  166. my($responsible_pathname) = $gnats_db_dir . "/gnats-adm/responsible";
  167. (-f $responsible_pathname) || die "$responsible_pathname not found";
  168. print "Reading $responsible_pathname...\n";
  169. if (!load_responsible($responsible_pathname)) {
  170. return(0);
  171. }
  172. # Open cleanup file.
  173. open(CLEANUP, ">$cleanup_pathname") ||
  174. die "Unable to open $cleanup_pathname: $!";
  175. chmod(0744, $cleanup_pathname) || warn "Unable to chmod $cleanup_pathname: $!";
  176. print CLEANUP "#!/bin/sh\n";
  177. print CLEANUP "# List of PRs that have problems found by gnats2bz.pl.\n";
  178. # Open data file.
  179. open(DATA, ">$data_pathname") || die "Unable to open $data_pathname: $!";
  180. print DATA "-- Exported data from $gnats_db_dir by gnats2bz.pl.\n";
  181. print DATA "-- Load it into a Bugzilla database using the command:\n";
  182. print DATA "-- mysql -uroot -p'ROOT_PASSWORD' bugs < gnats2bz_data.sql\n";
  183. print DATA "--\n";
  184. # Loop over @pr_list.
  185. my($pr);
  186. foreach $pr (@pr_list) {
  187. print "Processing $pr...\n";
  188. if (!read_pr("$gnats_db_dir/$pr")) {
  189. next;
  190. }
  191. translate_pr();
  192. check_pr($pr);
  193. collect_stats();
  194. update_versions();
  195. write_bugs();
  196. write_longdescs();
  197. }
  198. write_non_bugs_tables();
  199. close(CLEANUP) || die "Unable to close $cleanup_pathname: $!";
  200. close(DATA) || die "Unable to close $data_pathname: $!";
  201. print "Generating $stats_pathname...\n";
  202. report_stats();
  203. sub load_index {
  204. my($pathname) = @_;
  205. my($record);
  206. my(@fields);
  207. open(INDEX, $pathname) || die "Unable to open $pathname: $!";
  208. while ($record = <INDEX>) {
  209. @fields = split(/\|/, $record);
  210. push(@pr_list, $fields[0]);
  211. }
  212. close(INDEX) || die "Unable to close $pathname: $!";
  213. return(1);
  214. }
  215. sub load_categories {
  216. my($pathname) = @_;
  217. my($record);
  218. open(CATEGORIES, $pathname) || die "Unable to open $pathname: $!";
  219. while ($record = <CATEGORIES>) {
  220. if ($record =~ /^#/) {
  221. next;
  222. }
  223. push(@categories_list, [split(/:/, $record)]);
  224. }
  225. close(CATEGORIES) || die "Unable to close $pathname: $!";
  226. return(1);
  227. }
  228. sub load_responsible {
  229. my($pathname) = @_;
  230. my($record);
  231. open(RESPONSIBLE, $pathname) || die "Unable to open $pathname: $!";
  232. while ($record = <RESPONSIBLE>) {
  233. if ($record =~ /^#/) {
  234. next;
  235. }
  236. push(@responsible_list, [split(/\|/, $record)]);
  237. }
  238. close(RESPONSIBLE) || die "Unable to close $pathname: $!";
  239. return(1);
  240. }
  241. sub read_pr {
  242. my($pr_filename) = @_;
  243. my($multitext) = "Mail-Header";
  244. my($field, $mail_header);
  245. # Empty the hash.
  246. %pr_data = ();
  247. # Empty the list of duplicate fields.
  248. $pr_data_dup_fields = "";
  249. # Empty the list of badly labeled fields.
  250. $pr_data_bad_fields = "";
  251. unless (open(PR, $pr_filename)) {
  252. warn "error opening $pr_filename: $!";
  253. return(0);
  254. }
  255. LINELOOP: while (<PR>) {
  256. chomp;
  257. if ($multitext eq "Unformatted") {
  258. # once we reach "Unformatted", rest of file goes there
  259. $pr_data{$multitext} = append_multitext($pr_data{$multitext}, $_);
  260. next LINELOOP;
  261. }
  262. # Handle ENUMERATED and TEXT fields.
  263. foreach $field (@text_fields) {
  264. if (/^>$field:($|\s+)/) {
  265. $pr_data{$field} = $'; # part of string after match
  266. $pr_data{$field} =~ s/\s+$//; # strip trailing whitespace
  267. $multitext = "";
  268. next LINELOOP;
  269. }
  270. }
  271. # Handle MULTITEXT fields.
  272. foreach $field (@multitext_fields) {
  273. if (/^>$field:\s*$/) {
  274. $_ = $'; # set to part of string after match part
  275. if (defined($pr_data{$field})) {
  276. if ($pr_data_dup_fields eq "") {
  277. $pr_data_dup_fields = $field;
  278. } else {
  279. $pr_data_dup_fields = "$pr_data_dup_fields $field";
  280. }
  281. }
  282. $pr_data{$field} = $_;
  283. $multitext = $field;
  284. next LINELOOP;
  285. }
  286. }
  287. # Check for badly labeled fields.
  288. foreach $field ((@text_fields, @multitext_fields)) {
  289. if (/^>$field:/) {
  290. if ($pr_data_bad_fields eq "") {
  291. $pr_data_bad_fields = $field;
  292. } else {
  293. $pr_data_bad_fields = "$pr_data_bad_fields $field";
  294. }
  295. }
  296. }
  297. # Handle continued MULTITEXT field.
  298. $pr_data{$multitext} = append_multitext($pr_data{$multitext}, $_);
  299. }
  300. close(PR) || warn "error closing $pr_filename: $!";
  301. # Strip trailing newlines from MULTITEXT fields.
  302. foreach $field (@multitext_fields) {
  303. if (defined($pr_data{$field})) {
  304. $pr_data{$field} =~ s/\s+$//;
  305. }
  306. }
  307. return(1);
  308. }
  309. sub append_multitext {
  310. my($original, $addition) = @_;
  311. if (defined($original) && $original ne "") {
  312. return "$original\n$addition";
  313. } else {
  314. return $addition;
  315. }
  316. }
  317. sub check_pr {
  318. my($pr) = @_;
  319. my($error_list) = "";
  320. if ($pr_data_dup_fields ne "") {
  321. $error_list = append_error($error_list, "Multiple '$pr_data_dup_fields'");
  322. }
  323. if ($pr_data_bad_fields ne "") {
  324. $error_list = append_error($error_list, "Bad field labels '$pr_data_bad_fields'");
  325. }
  326. if (!defined($pr_data{"Description"}) || $pr_data{"Description"} eq "") {
  327. $error_list = append_error($error_list, "Description empty");
  328. }
  329. if (defined($pr_data{"Unformatted"}) && $pr_data{"Unformatted"} ne "") {
  330. $error_list = append_error($error_list, "Unformatted text");
  331. }
  332. if (defined($pr_data{"Release"}) && length($pr_data{"Release"}) > 16) {
  333. $error_list = append_error($error_list, "Release > 16 chars");
  334. }
  335. if (defined($pr_data{"Fix"}) && $pr_data{"Fix"} =~ /State-Changed-/) {
  336. $error_list = append_error($error_list, "Audit in Fix field");
  337. }
  338. if (defined($pr_data{"Arrival-Date"})) {
  339. if ($pr_data{"Arrival-Date"} eq "") {
  340. $error_list = append_error($error_list, "Arrival-Date empty");
  341. } elsif (unixdate2datetime($pr, $pr_data{"Arrival-Date"}) eq "") {
  342. $error_list = append_error($error_list, "Arrival-Date format");
  343. }
  344. }
  345. # More checks should go here.
  346. if ($error_list ne "") {
  347. if ($cleanup_with_edit_pr) {
  348. my(@parts) = split("/", $pr);
  349. my($pr_num) = $parts[1];
  350. print CLEANUP "echo \"$error_list\"; edit-pr $pr_num\n";
  351. } else {
  352. print CLEANUP "echo \"$error_list\"; \${EDITOR} $pr\n";
  353. }
  354. }
  355. }
  356. sub append_error {
  357. my($original, $addition) = @_;
  358. if ($original ne "") {
  359. return "$original, $addition";
  360. } else {
  361. return $addition;
  362. }
  363. }
  364. sub translate_pr {
  365. # This function performs GNATS -> Bugzilla translations that should
  366. # happen before collect_stats().
  367. if (!defined($pr_data{"Organization"})) {
  368. $pr_data{"Originator"} = "";
  369. }
  370. if ($pr_data{"Organization"} =~ /$default_organization/) {
  371. $pr_data{"Originator"} = "";
  372. $pr_data{"Organization"} = "";
  373. }
  374. $pr_data{"Organization"} =~ s/^\s+//g; # strip leading whitespace
  375. if (!defined($pr_data{"Release"}) ||
  376. $pr_data{"Release"} eq "" ||
  377. $pr_data{"Release"} =~ /^unknown-1.0$/
  378. ) {
  379. $pr_data{"Release"} = "unknown";
  380. }
  381. if (defined($pr_data{"Responsible"})) {
  382. $pr_data{"Responsible"} =~ /\w+/;
  383. $pr_data{"Responsible"} = "$&$username_suffix";
  384. }
  385. my($rep_platform, $op_sys) = ("All", "All");
  386. if (defined($pr_data{"Environment"})) {
  387. if ($pr_data{"Environment"} =~ /[wW]in.*NT/) {
  388. $rep_platform = "PC";
  389. $op_sys = "Windows NT";
  390. } elsif ($pr_data{"Environment"} =~ /[wW]in.*95/) {
  391. $rep_platform = "PC";
  392. $op_sys = "Windows 95";
  393. } elsif ($pr_data{"Environment"} =~ /[wW]in.*98/) {
  394. $rep_platform = "PC";
  395. $op_sys = "Windows 98";
  396. } elsif ($pr_data{"Environment"} =~ /OSF/) {
  397. $rep_platform = "DEC";
  398. $op_sys = "OSF/1";
  399. } elsif ($pr_data{"Environment"} =~ /AIX/) {
  400. $rep_platform = "RS/6000";
  401. $op_sys = "AIX";
  402. } elsif ($pr_data{"Environment"} =~ /IRIX/) {
  403. $rep_platform = "SGI";
  404. $op_sys = "IRIX";
  405. } elsif ($pr_data{"Environment"} =~ /SunOS.*5\.\d/) {
  406. $rep_platform = "Sun";
  407. $op_sys = "Solaris";
  408. } elsif ($pr_data{"Environment"} =~ /SunOS.*4\.\d/) {
  409. $rep_platform = "Sun";
  410. $op_sys = "SunOS";
  411. }
  412. }
  413. $pr_data{"Environment"} = "$rep_platform:$op_sys";
  414. }
  415. sub collect_stats {
  416. my($field, $value);
  417. foreach $field (@statistics_fields) {
  418. $value = $pr_data{$field};
  419. if (!defined($value)) {
  420. $value = "";
  421. }
  422. if (defined($pr_stats{$field}{$value})) {
  423. $pr_stats{$field}{$value}++;
  424. } else {
  425. $pr_stats{$field}{$value} = 1;
  426. }
  427. }
  428. }
  429. sub report_stats {
  430. my($field, $value, $count);
  431. open(STATS, ">$stats_pathname") ||
  432. die "Unable to open $stats_pathname: $!";
  433. print STATS "Statistics of $gnats_db_dir collated by gnats2bz.pl.\n";
  434. my($field_stats);
  435. while (($field, $field_stats) = each(%pr_stats)) {
  436. print STATS "\n$field:\n";
  437. while (($value, $count) = each(%$field_stats)) {
  438. print STATS " $value: $count\n";
  439. }
  440. }
  441. close(STATS) || die "Unable to close $stats_pathname: $!";
  442. }
  443. sub get_userid {
  444. my($responsible) = @_;
  445. my($username, $userid);
  446. if (!defined($responsible)) {
  447. return(-1);
  448. }
  449. # Search for current username in the list.
  450. $userid = $userid_base;
  451. foreach $username (@username_list) {
  452. if ($username eq $responsible) {
  453. return($userid);
  454. }
  455. $userid++;
  456. }
  457. push(@username_list, $responsible);
  458. return($userid);
  459. }
  460. sub update_versions {
  461. if (!defined($pr_data{"Release"}) || !defined($pr_data{"Category"})) {
  462. return;
  463. }
  464. my($curr_product) = $pr_data{"Category"};
  465. my($curr_version) = $pr_data{"Release"};
  466. if ($curr_version eq "") {
  467. return;
  468. }
  469. if (!defined($versions_table{$curr_product})) {
  470. $versions_table{$curr_product} = [ ];
  471. }
  472. my($version_list) = $versions_table{$curr_product};
  473. my($version);
  474. foreach $version (@$version_list) {
  475. if ($version eq $curr_version) {
  476. return;
  477. }
  478. }
  479. push(@$version_list, $curr_version);
  480. }
  481. sub write_bugs {
  482. my($bug_id) = $pr_data{"Number"};
  483. my($userid) = get_userid($pr_data{"Responsible"});
  484. # Mapping from Class,Severity to bug_severity
  485. # At our site, the Severity,Priority fields have degenerated
  486. # into a 9-level priority field.
  487. my($bug_severity) = "normal";
  488. if ($pr_data{"Class"} eq "change-request") {
  489. $bug_severity = "enhancement";
  490. } elsif (defined($pr_data{"Synopsis"})) {
  491. if ($pr_data{"Synopsis"} =~ /crash|assert/i) {
  492. $bug_severity = "critical";
  493. } elsif ($pr_data{"Synopsis"} =~ /wrong|error/i) {
  494. $bug_severity = "major";
  495. }
  496. }
  497. $bug_severity = SqlQuote($bug_severity);
  498. # Mapping from Severity,Priority to priority
  499. # At our site, the Severity,Priority fields have degenerated
  500. # into a 9-level priority field.
  501. my($priority) = "P1";
  502. if (defined($pr_data{"Severity"}) && defined($pr_data{"Severity"})) {
  503. if ($pr_data{"Severity"} eq "critical") {
  504. if ($pr_data{"Priority"} eq "high") {
  505. $priority = "P1";
  506. } else {
  507. $priority = "P2";
  508. }
  509. } elsif ($pr_data{"Severity"} eq "serious") {
  510. if ($pr_data{"Priority"} eq "low") {
  511. $priority = "P4";
  512. } else {
  513. $priority = "P3";
  514. }
  515. } else {
  516. if ($pr_data{"Priority"} eq "high") {
  517. $priority = "P4";
  518. } else {
  519. $priority = "P5";
  520. }
  521. }
  522. }
  523. $priority = SqlQuote($priority);
  524. # Map State,Class to bug_status,resolution
  525. my($bug_status, $resolution);
  526. if ($pr_data{"State"} eq "open" || $pr_data{"State"} eq "analyzed") {
  527. $bug_status = "ASSIGNED";
  528. $resolution = "";
  529. } elsif ($pr_data{"State"} eq "feedback") {
  530. $bug_status = "RESOLVED";
  531. $resolution = "FIXED";
  532. } elsif ($pr_data{"State"} eq "closed") {
  533. $bug_status = "CLOSED";
  534. if (defined($pr_data{"Class"}) && $pr_data{"Class"} =~ /^duplicate/) {
  535. $resolution = "DUPLICATE";
  536. } elsif (defined($pr_data{"Class"}) && $pr_data{"Class"} =~ /^mistaken/) {
  537. $resolution = "INVALID";
  538. } else {
  539. $resolution = "FIXED";
  540. }
  541. } elsif ($pr_data{"State"} eq "suspended") {
  542. $bug_status = "RESOLVED";
  543. $resolution = "WONTFIX";
  544. } else {
  545. $bug_status = "NEW";
  546. $resolution = "";
  547. }
  548. $bug_status = SqlQuote($bug_status);
  549. $resolution = SqlQuote($resolution);
  550. my($creation_ts) = "";
  551. if (defined($pr_data{"Arrival-Date"}) && $pr_data{"Arrival-Date"} ne "") {
  552. $creation_ts = unixdate2datetime($bug_id, $pr_data{"Arrival-Date"});
  553. }
  554. $creation_ts = SqlQuote($creation_ts);
  555. my($delta_ts) = "";
  556. if (defined($pr_data{"Audit-Trail"})) {
  557. # note that (?:.|\n)+ is greedy, so this should match the
  558. # last Changed-When
  559. if ($pr_data{"Audit-Trail"} =~ /(?:.|\n)+-Changed-When: (.+)/) {
  560. $delta_ts = unixdate2timestamp($bug_id, $1);
  561. }
  562. }
  563. if ($delta_ts eq "") {
  564. if (defined($pr_data{"Arrival-Date"}) && $pr_data{"Arrival-Date"} ne "") {
  565. $delta_ts = unixdate2timestamp($bug_id, $pr_data{"Arrival-Date"});
  566. }
  567. }
  568. $delta_ts = SqlQuote($delta_ts);
  569. my($short_desc) = SqlQuote($pr_data{"Synopsis"});
  570. my($rep_platform, $op_sys) = split(/\|/, $pr_data{"Environment"});
  571. $rep_platform = SqlQuote($rep_platform);
  572. $op_sys = SqlQuote($op_sys);
  573. my($reporter) = get_userid($gnats_username);
  574. if (
  575. defined($pr_data{"Mail-Header"}) &&
  576. $pr_data{"Mail-Header"} =~ /From ([\w.]+\@[\w.]+)/
  577. ) {
  578. $reporter = get_userid($1);
  579. }
  580. my($version) = "";
  581. if (defined($pr_data{"Release"})) {
  582. $version = substr($pr_data{"Release"}, 0, 16);
  583. }
  584. $version = SqlQuote($version);
  585. my($product) = "";
  586. if (defined($pr_data{"Category"})) {
  587. $product = $pr_data{"Category"};
  588. }
  589. $product = SqlQuote($product);
  590. my($component) = SqlQuote($default_component);
  591. my($target_milestone) = "0";
  592. # $target_milestone = SqlQuote($target_milestone);
  593. my($qa_contact) = "0";
  594. # my($bug_file_loc) = "";
  595. # $bug_file_loc = SqlQuote($bug_file_loc);
  596. # my($status_whiteboard) = "";
  597. # $status_whiteboard = SqlQuote($status_whiteboard);
  598. print DATA "\ninsert into bugs (\n";
  599. print DATA " bug_id, assigned_to, bug_severity, priority, bug_status, creation_ts, delta_ts,\n";
  600. print DATA " short_desc,\n";
  601. print DATA " rep_platform, op_sys, reporter, version,\n";
  602. print DATA " product, component, resolution, target_milestone, qa_contact\n";
  603. print DATA ") values (\n";
  604. print DATA " $bug_id, $userid, $bug_severity, $priority, $bug_status, $creation_ts, $delta_ts,\n";
  605. print DATA " $short_desc,\n";
  606. print DATA " $rep_platform, $op_sys, $reporter, $version,\n";
  607. print DATA " $product, $component, $resolution, $target_milestone, $qa_contact\n";
  608. print DATA ");\n";
  609. }
  610. sub write_longdescs {
  611. my($bug_id) = $pr_data{"Number"};
  612. my($who) = get_userid($pr_data{"Responsible"});;
  613. my($bug_when) = "";
  614. if (defined($pr_data{"Arrival-Date"}) && $pr_data{"Arrival-Date"} ne "") {
  615. $bug_when = unixdate2datetime($bug_id, $pr_data{"Arrival-Date"});
  616. }
  617. $bug_when = SqlQuote($bug_when);
  618. my($thetext) = $pr_data{"Description"};
  619. if (defined($pr_data{"How-To-Repeat"}) && $pr_data{"How-To-Repeat"} ne "") {
  620. $thetext =
  621. $thetext . "\n\nHow-To-Repeat:\n" . $pr_data{"How-To-Repeat"};
  622. }
  623. if (defined($pr_data{"Fix"}) && $pr_data{"Fix"} ne "") {
  624. $thetext = $thetext . "\n\nFix:\n" . $pr_data{"Fix"};
  625. }
  626. if (defined($pr_data{"Originator"}) && $pr_data{"Originator"} ne "") {
  627. $thetext = $thetext . "\n\nOriginator:\n" . $pr_data{"Originator"};
  628. }
  629. if (defined($pr_data{"Organization"}) && $pr_data{"Organization"} ne "") {
  630. $thetext = $thetext . "\n\nOrganization:\n" . $pr_data{"Organization"};
  631. }
  632. if (defined($pr_data{"Audit-Trail"}) && $pr_data{"Audit-Trail"} ne "") {
  633. $thetext = $thetext . "\n\nAudit-Trail:\n" . $pr_data{"Audit-Trail"};
  634. }
  635. if (defined($pr_data{"Unformatted"}) && $pr_data{"Unformatted"} ne "") {
  636. $thetext = $thetext . "\n\nUnformatted:\n" . $pr_data{"Unformatted"};
  637. }
  638. $thetext = SqlQuote($thetext);
  639. print DATA "\ninsert into longdescs (\n";
  640. print DATA " bug_id, who, bug_when, thetext\n";
  641. print DATA ") values (\n";
  642. print DATA " $bug_id, $who, $bug_when, $thetext\n";
  643. print DATA ");\n";
  644. }
  645. sub write_non_bugs_tables {
  646. my($categories_record);
  647. foreach $categories_record (@categories_list) {
  648. my($component) = SqlQuote($default_component);
  649. my($product) = SqlQuote(@$categories_record[0]);
  650. my($description) = SqlQuote(@$categories_record[1]);
  651. my($initialowner) = SqlQuote(@$categories_record[2] . $username_suffix);
  652. print DATA "\ninsert into products (\n";
  653. print DATA
  654. " product, description, milestoneurl, disallownew\n";
  655. print DATA ") values (\n";
  656. print DATA
  657. " $product, $description, '', 0\n";
  658. print DATA ");\n";
  659. print DATA "\ninsert into components (\n";
  660. print DATA
  661. " value, program, initialowner, initialqacontact, description\n";
  662. print DATA ") values (\n";
  663. print DATA
  664. " $component, $product, $initialowner, '', $description\n";
  665. print DATA ");\n";
  666. print DATA "\ninsert into milestones (\n";
  667. print DATA
  668. " value, product, sortkey\n";
  669. print DATA ") values (\n";
  670. print DATA
  671. " 0, $product, 0\n";
  672. print DATA ");\n";
  673. }
  674. my($username);
  675. my($userid) = $userid_base;
  676. my($password) = "password";
  677. my($realname);
  678. my($groupset) = 0;
  679. foreach $username (@username_list) {
  680. $realname = map_username_to_realname($username);
  681. $username = SqlQuote($username);
  682. $realname = SqlQuote($realname);
  683. print DATA "\ninsert into profiles (\n";
  684. print DATA
  685. " userid, login_name, cryptpassword, realname, groupset\n";
  686. print DATA ") values (\n";
  687. print DATA
  688. " $userid, $username, encrypt('$password'), $realname, $groupset\n";
  689. print DATA ");\n";
  690. $userid++;
  691. }
  692. my($product);
  693. my($version_list);
  694. while (($product, $version_list) = each(%versions_table)) {
  695. $product = SqlQuote($product);
  696. my($version);
  697. foreach $version (@$version_list) {
  698. $version = SqlQuote($version);
  699. print DATA "\ninsert into versions (value, program) ";
  700. print DATA "values ($version, $product);\n";
  701. }
  702. }
  703. }
  704. sub map_username_to_realname() {
  705. my($username) = @_;
  706. my($name, $realname);
  707. # get the portion before the @
  708. $name = $username;
  709. $name =~ s/\@.*//;
  710. my($responsible_record);
  711. foreach $responsible_record (@responsible_list) {
  712. if (@$responsible_record[0] eq $name) {
  713. return(@$responsible_record[1]);
  714. }
  715. if (defined(@$responsible_record[2])) {
  716. if (@$responsible_record[2] eq $username) {
  717. return(@$responsible_record[1]);
  718. }
  719. }
  720. }
  721. return("");
  722. }
  723. sub detaint_string {
  724. my ($str) = @_;
  725. $str =~ m/^(.*)$/s;
  726. $str = $1;
  727. }
  728. sub SqlQuote {
  729. my ($str) = (@_);
  730. $str =~ s/([\\\'])/\\$1/g;
  731. $str =~ s/\0/\\0/g;
  732. # If it's been SqlQuote()ed, then it's safe, so we tell -T that.
  733. $str = detaint_string($str);
  734. return "'$str'";
  735. }
  736. sub unixdate2datetime {
  737. my($bugid, $unixdate) = @_;
  738. my($year, $month, $day, $hour, $min, $sec);
  739. if (!split_unixdate($bugid, $unixdate, \$year, \$month, \$day, \$hour, \$min, \$sec)) {
  740. return("");
  741. }
  742. return("$year-$month-$day $hour:$min:$sec");
  743. }
  744. sub unixdate2timestamp {
  745. my($bugid, $unixdate) = @_;
  746. my($year, $month, $day, $hour, $min, $sec);
  747. if (!split_unixdate($bugid, $unixdate, \$year, \$month, \$day, \$hour, \$min, \$sec)) {
  748. return("");
  749. }
  750. return("$year$month$day$hour$min$sec");
  751. }
  752. sub split_unixdate {
  753. # "Tue Jun 6 14:50:00 1995"
  754. # "Mon Nov 20 17:03:11 [MST] 1995"
  755. # "12/13/94"
  756. # "jan 1, 1995"
  757. my($bugid, $unixdate, $year, $month, $day, $hour, $min, $sec) = @_;
  758. my(@parts);
  759. $$hour = "00";
  760. $$min = "00";
  761. $$sec = "00";
  762. @parts = split(/ +/, $unixdate);
  763. if (@parts >= 5) {
  764. # year
  765. $$year = $parts[4];
  766. if ($$year =~ /[A-Z]{3}/) {
  767. # Must be timezone, try next field.
  768. $$year = $parts[5];
  769. }
  770. if ($$year =~ /\D/) {
  771. warn "$bugid: Error processing year part '$$year' of date '$unixdate'\n";
  772. return(0);
  773. }
  774. if ($$year < 30) {
  775. $$year = "20" . $$year;
  776. } elsif ($$year < 100) {
  777. $$year = "19" . $$year;
  778. } elsif ($$year < 1970 || $$year > 2029) {
  779. warn "$bugid: Error processing year part '$$year' of date '$unixdate'\n";
  780. return(0);
  781. }
  782. # month
  783. $$month = $parts[1];
  784. if ($$month =~ /\D/) {
  785. if (!month2number($month)) {
  786. warn "$bugid: Error processing month part '$$month' of date '$unixdate'\n";
  787. return(0);
  788. }
  789. } elsif ($$month < 1 || $$month > 12) {
  790. warn "$bugid: Error processing month part '$$month' of date '$unixdate'\n";
  791. return(0);
  792. } elsif (length($$month) == 1) {
  793. $$month = "0" . $$month;
  794. }
  795. # day
  796. $$day = $parts[2];
  797. if ($$day < 1 || $$day > 31) {
  798. warn "$bugid: Error processing day part '$day' of date '$unixdate'\n";
  799. return(0);
  800. } elsif (length($$day) == 1) {
  801. $$day = "0" . $$day;
  802. }
  803. @parts = split(/:/, $parts[3]);
  804. $$hour = $parts[0];
  805. $$min = $parts[1];
  806. $$sec = $parts[2];
  807. return(1);
  808. } elsif (@parts == 3) {
  809. # year
  810. $$year = $parts[2];
  811. if ($$year =~ /\D/) {
  812. warn "$bugid: Error processing year part '$$year' of date '$unixdate'\n";
  813. return(0);
  814. }
  815. if ($$year < 30) {
  816. $$year = "20" . $$year;
  817. } elsif ($$year < 100) {
  818. $$year = "19" . $$year;
  819. } elsif ($$year < 1970 || $$year > 2029) {
  820. warn "$bugid: Error processing year part '$$year' of date '$unixdate'\n";
  821. return(0);
  822. }
  823. # month
  824. $$month = $parts[0];
  825. if ($$month =~ /\D/) {
  826. if (!month2number($month)) {
  827. warn "$bugid: Error processing month part '$$month' of date '$unixdate'\n";
  828. return(0);
  829. }
  830. } elsif ($$month < 1 || $$month > 12) {
  831. warn "$bugid: Error processing month part '$$month' of date '$unixdate'\n";
  832. return(0);
  833. } elsif (length($$month) == 1) {
  834. $$month = "0" . $$month;
  835. }
  836. # day
  837. $$day = $parts[1];
  838. $$day =~ s/,//;
  839. if ($$day < 1 || $$day > 31) {
  840. warn "$bugid: Error processing day part '$day' of date '$unixdate'\n";
  841. return(0);
  842. } elsif (length($$day) == 1) {
  843. $$day = "0" . $$day;
  844. }
  845. return(1);
  846. }
  847. @parts = split(/:/, $unixdate);
  848. if (@parts == 3 && length($unixdate) <= 8) {
  849. $$year = "19" . $parts[2];
  850. $$month = $parts[0];
  851. if (length($$month) == 1) {
  852. $$month = "0" . $$month;
  853. }
  854. $$day = $parts[1];
  855. if (length($$day) == 1) {
  856. $$day = "0" . $$day;
  857. }
  858. return(1);
  859. }
  860. warn "$bugid: Error processing date '$unixdate'\n";
  861. return(0);
  862. }
  863. sub month2number {
  864. my($month) = @_;
  865. if ($$month =~ /jan/i) {
  866. $$month = "01";
  867. } elsif ($$month =~ /feb/i) {
  868. $$month = "02";
  869. } elsif ($$month =~ /mar/i) {
  870. $$month = "03";
  871. } elsif ($$month =~ /apr/i) {
  872. $$month = "04";
  873. } elsif ($$month =~ /may/i) {
  874. $$month = "05";
  875. } elsif ($$month =~ /jun/i) {
  876. $$month = "06";
  877. } elsif ($$month =~ /jul/i) {
  878. $$month = "07";
  879. } elsif ($$month =~ /aug/i) {
  880. $$month = "08";
  881. } elsif ($$month =~ /sep/i) {
  882. $$month = "09";
  883. } elsif ($$month =~ /oct/i) {
  884. $$month = "10";
  885. } elsif ($$month =~ /nov/i) {
  886. $$month = "11";
  887. } elsif ($$month =~ /dec/i) {
  888. $$month = "12";
  889. } else {
  890. return(0);
  891. }
  892. return(1);
  893. }