#!/usr/bin/perl
# DeBugzilla Version 0.4
#  Compare Debian Bugtracking data with Bugzilla Bugtracking
# (c) 2005 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...
#  0.4 - update to new ldap backend
#
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=389; #35568;
my $lbase="dc=bugs,dc=debian,dc=org";

### 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="(debbugsSourcePackage=$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";

# Open LDAP connection
my $ldap = Net::LDAP->new($lhost, port => $lport) or die "$@";
$ldap->bind || die "Couldn't bind to bugs.debian.org";

# Query LDAP
my $mesg = $ldap->search( base=>$lbase, filter=>$filter );
$mesg->code && die "LDAP Search failed: ".$mesg->error;

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

# Process Bugs.
foreach my $entry ($mesg->all_entries) { process_bug_entry(\@buginfos,$entry); }

# Disconnect from LDAP
$ldap->unbind;

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

#################################################
sub process_bug_entry($) {
	my $buginfos = $_[0];
	my $entry = $_[1];
	my %buginfo;
	
	my $bugid=$entry->get_value("debbugsID");
	$buginfo{BUGID}=$bugid;
	
	# Skip "done" reports
	if ($entry->get_value("debbugsDone") || $entry->get_value("debbugsState") eq "done") {
		return 1;
	}
	$buginfo{SEVERITY}=$entry->get_value("debbugsSeverity");
	$buginfo{SUBJECT}=$entry->get_value("debbugsTitle");
	
	my $forwarded=$entry->get_value("debbugsForwardedTo");
	# 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 #".$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 #".$bugid.": not a known bugzilla site: $url\n";
		return 1;
	}
	$buginfo{URL}=$url;

	# Transform the URL to the XML access URL
	my $transform = $bugsite->{transform_url};
	eval "\$url =~ $transform;" || die "ERROR at #".$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 #".$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 #".$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) {
			$buginfo{BZSTATUS}=$bug_status->string_value();
		} else {
			$buginfo{BZSTATUS}="no bug_status";
		}
		# Find Bug "Resolution"
		my $resolution = $doc->documentElement->findnodes($bugsite->{resolution_xpath});
		if ($resolution) {
			$buginfo{RESOLUTION}=$resolution->string_value();
		} else {
			$buginfo{RESOLUTION}="";
		}
		# Push this info into the buginfos array.
		push @$buginfos, \%buginfo;
	} else {
		print "ERROR while parsing $url\n";
		return 1;
	}
}
