#!/usr/bin/perl
# DeBugzilla Version 0.3
#  Compare Debian Bugtracking data with Bugzilla Bugtracking
# (c) 2003 Erich Schubert <erich@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Privacy Guard; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
#
# Version history:
#  0.1 - initial version
#  0.2 + added support for IssueZilla
#      + support multiple bugtracking systems (+detection)
#      + support a timestamp for output
#  0.3 + specify encoding in config, since bugzilla doesn't...
#
use strict;
use Net::LDAP;
use LWP::UserAgent;
use XML::LibXML;
use HTML::Template;

############### Configuration Section
my $owner='erich@debian.org'; #die 'Please configure';
my $package=shift || die "You need to pass a package name";

### DebBugs access data
my $lhost="bugs.debian.org";
my $lport=35568;
my $lbase="ou=Bugs,o=Debian Project,c=US";

### Bugzilla locations
my %bugzillas = (
	"http://bugzilla.mozilla.org" => {
		transform_url		=> 's/show_bug.cgi/xml.cgi/',
		bug_status_xpath	=> '//bug_status',
		resolution_xpath	=> '//resolution',
		encoding			=> 'ISO-8859-1'
	},
	"http://bugzilla.gnome.org" => {
		transform_url		=> 's/show_bug.cgi/xml.cgi/',
		bug_status_xpath	=> '//bug_status',
		resolution_xpath	=> '//resolution',
		encoding			=> 'ISO-8859-1'
	},
	# Issue Zilla has a slightly different xml format
	"http://www.openoffice.org" => {
		transform_url		=> 's/show_bug.cgi/xml.cgi/',
		bug_status_xpath	=> '//issue_status',
		resolution_xpath	=> '//resolution',
		encoding			=> 'ISO-8859-1'
	},
);
###############
my $filter="(package=$package)";

my $ua=LWP::UserAgent->new(keep_alive => 1, env_proxy => 1, timeout => 120);
$ua->agent("DeBugzillaSync/0.1 ".$owner);

my $parser = XML::LibXML->new();

my $template = HTML::Template->new(
	filename => "debugzilla-html.tmpl",
	die_on_bad_params => 0)
|| die "Coudln't open template";

# Data structure for bug data...
my @buginfos;

# Load debbugs data
debbugs_load(\@buginfos,$ua,$package);

# Generate Output
$template->param(PACKAGE => $package);
$template->param(DATE => scalar(localtime(time())));
$template->param(BUGINFOS => \@buginfos);
print $template->output();

#################################################
sub debbugs_load($$$) {
	my $buginfos = $_[0];
	my $ua = $_[1];
	my $package = $_[2];
	my %buginfo;
	
	my $debbugs = $ua->get("http://master.debian.org/~mh/machine-interface/machpkg.cgi?src=$package&show=severity&show=forwarded&show=title&pend-exc=done&archive=no&repeatmerged=no");
	unless ($debbugs->is_success && ($debbugs->content ne "")) {
		die "ERROR while getting ".
			$debbugs->request->uri." -- ".$debbugs->status_line."\n";
	}
	my $content = $debbugs->content;
	# kill the initial line
	$content =~ s/^(<[^>\n]*>,\s*)*(<[^>\n]*>)?\n\n//s;
	while($content =~ s/^\s*([0-9]+),\s*"(.*?)",\s*([a-z]*),\s*(.*?)(,\s*)?$//m) {
		my %entry;
		$entry{BUGID}=$1;
		$entry{SUBJECT}=$2;
		$entry{SEVERITY}=$3;
		$entry{FORWARDED}=$4;
		process_bug($buginfos,$ua,\%entry);
	}
}

sub process_bug($$$) {
	my $buginfos = $_[0];
	my $ua = $_[1];
	my $entry = $_[2];

	# Skip "done" reports
	if ($$entry{SERVERITY} eq "done") {
		return 1;
	}
	
	my $forwarded=$$entry{FORWARDED};
	# skip if not forwarded
	return 1 unless ($forwarded);
	# skip but print error if we don't understand the forwarded value
	if ($forwarded !~ m/(http:\/\/[^ ]+\/[^ ]+)/) {
		print stderr "ERROR at #".$$entry{BUGID}.
			": Doesn't have a http url: $forwarded\n";
		return 1;
	}
	my $url=$1;

	# Does this fit into a known bugzilla site?
	my $bugsite;
	foreach my $site (keys %bugzillas) {
		if ($url =~ m/$site/) {
			$bugsite = $bugzillas{$site};
			last;
		}
	}
	unless ($bugsite) {
		print stderr "ERROR at #".$$entry{BUGID}.": not a known bugzilla site: $url\n";
		return 1;
	}
	$$entry{URL}=$url;

	# Transform the URL to the XML access URL
	my $transform = $bugsite->{transform_url};
	eval "\$url =~ $transform;" || die "ERROR at #".$$entry{BUGID}.": Transform failed.";

	## Connect to bugzilla
	#print stderr "Loading ".$url."\n";
	my $req;
	# retries are needed, because i sometimes recieved empty strings
	# at least from gnome bugzilla (load issues there?)
	my $retry_count=3;
	do {
		$req = $ua->get($url);
		unless ($req->is_success) {
			print stderr "ERROR at #".$$entry{BUGID}." while getting ".
				$req->request->uri." -- ".$req->status_line."\n";
			return 1;
		}
		$retry_count--;
	} until( ($req->content ne "") || ($retry_count <= 0) );
	# still empty content?
	if (!$req->content || ($req->content eq "")) {
		print stderr "ERROR at #".$$entry{BUGID}.": got empty string\n";
		return 1;
	}
	# Parse the file
	my $doc = $parser->parse_string(
			encodeToUTF8($bugsite->{encoding},$req->content)
		);
	if ($doc) {
		# Find Bug "Status"
		my $bug_status = $doc->documentElement->findnodes($bugsite->{bug_status_xpath});
		if ($bug_status) {
			$$entry{BZSTATUS}=$bug_status->string_value();
		} else {
			$$entry{BZSTATUS}="no bug_status";
		}
		# Find Bug "Resolution"
		my $resolution = $doc->documentElement->findnodes($bugsite->{resolution_xpath});
		if ($resolution) {
			$$entry{RESOLUTION}=$resolution->string_value();
		} else {
			$$entry{RESOLUTION}="";
		}
		# Push this info into the buginfos array.
		push @$buginfos, $entry;
	} else {
		print "ERROR while parsing $url\n";
		return 1;
	}
}
