296 lines
10 KiB
Perl
296 lines
10 KiB
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 Original Code is the Bugzilla Bug Tracking System.
|
|
#
|
|
# Contributor(s): John Keiser <john@johnkeiser.com>
|
|
# Frédéric Buclin <LpSolit@gmail.com>
|
|
|
|
use strict;
|
|
|
|
package Bugzilla::Attachment::PatchReader;
|
|
|
|
use Bugzilla::Error;
|
|
use Bugzilla::Attachment;
|
|
use Bugzilla::Util;
|
|
|
|
sub process_diff {
|
|
my ($attachment, $format, $context) = @_;
|
|
my $dbh = Bugzilla->dbh;
|
|
my $cgi = Bugzilla->cgi;
|
|
my $lc = Bugzilla->localconfig;
|
|
my $vars = {};
|
|
|
|
my ($reader, $last_reader) = setup_patch_readers(undef, $context);
|
|
|
|
if ($format eq 'raw') {
|
|
require PatchReader::DiffPrinter::raw;
|
|
$last_reader->sends_data_to(new PatchReader::DiffPrinter::raw());
|
|
# Actually print out the patch.
|
|
print $cgi->header(-type => 'text/plain',
|
|
-expires => '+3M');
|
|
disable_utf8();
|
|
$reader->iterate_string('Attachment ' . $attachment->id, $attachment->data);
|
|
}
|
|
else {
|
|
my @other_patches = ();
|
|
if ($lc->{interdiffbin} && $lc->{diffpath}) {
|
|
# Get the list of attachments that the user can view in this bug.
|
|
my @attachments =
|
|
@{Bugzilla::Attachment->get_attachments_by_bug($attachment->bug_id)};
|
|
# Extract patches only.
|
|
@attachments = grep {$_->ispatch == 1} @attachments;
|
|
# We want them sorted from newer to older.
|
|
@attachments = sort { $b->id <=> $a->id } @attachments;
|
|
|
|
# Ignore the current patch, but select the one right before it
|
|
# chronologically.
|
|
my $select_next_patch = 0;
|
|
foreach my $attach (@attachments) {
|
|
if ($attach->id == $attachment->id) {
|
|
$select_next_patch = 1;
|
|
}
|
|
else {
|
|
push(@other_patches, { 'id' => $attach->id,
|
|
'desc' => $attach->description,
|
|
'selected' => $select_next_patch });
|
|
$select_next_patch = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
$vars->{'bugid'} = $attachment->bug_id;
|
|
$vars->{'attachid'} = $attachment->id;
|
|
$vars->{'description'} = $attachment->description;
|
|
$vars->{'other_patches'} = \@other_patches;
|
|
|
|
setup_template_patch_reader($last_reader, $format, $context, $vars);
|
|
# The patch is going to be displayed in a HTML page and if the utf8
|
|
# param is enabled, we have to encode attachment data as utf8.
|
|
if (Bugzilla->params->{'utf8'}) {
|
|
$attachment->data; # Populate ->{data}
|
|
utf8::decode($attachment->{data});
|
|
}
|
|
$reader->iterate_string('Attachment ' . $attachment->id, $attachment->data);
|
|
}
|
|
}
|
|
|
|
sub process_interdiff {
|
|
my ($old_attachment, $new_attachment, $format, $context) = @_;
|
|
my $cgi = Bugzilla->cgi;
|
|
my $lc = Bugzilla->localconfig;
|
|
my $vars = {};
|
|
|
|
# Encode attachment data as utf8 if it's going to be displayed in a HTML
|
|
# page using the UTF-8 encoding.
|
|
if ($format ne 'raw' && Bugzilla->params->{'utf8'}) {
|
|
$old_attachment->data; # Populate ->{data}
|
|
utf8::decode($old_attachment->{data});
|
|
$new_attachment->data; # Populate ->{data}
|
|
utf8::decode($new_attachment->{data});
|
|
}
|
|
|
|
# Get old patch data.
|
|
my ($old_filename, $old_file_list) = get_unified_diff($old_attachment, $format);
|
|
# Get new patch data.
|
|
my ($new_filename, $new_file_list) = get_unified_diff($new_attachment, $format);
|
|
|
|
my $warning = warn_if_interdiff_might_fail($old_file_list, $new_file_list);
|
|
|
|
# Send through interdiff, send output directly to template.
|
|
# Must hack path so that interdiff will work.
|
|
$ENV{'PATH'} = $lc->{diffpath};
|
|
open my $interdiff_fh, "$lc->{interdiffbin} $old_filename $new_filename|";
|
|
binmode $interdiff_fh;
|
|
my ($reader, $last_reader) = setup_patch_readers("", $context);
|
|
|
|
if ($format eq 'raw') {
|
|
require PatchReader::DiffPrinter::raw;
|
|
$last_reader->sends_data_to(new PatchReader::DiffPrinter::raw());
|
|
# Actually print out the patch.
|
|
print $cgi->header(-type => 'text/plain',
|
|
-expires => '+3M');
|
|
disable_utf8();
|
|
}
|
|
else {
|
|
# In case the HTML page is displayed with the UTF-8 encoding.
|
|
binmode $interdiff_fh, ':utf8' if Bugzilla->params->{'utf8'};
|
|
|
|
$vars->{'warning'} = $warning if $warning;
|
|
$vars->{'bugid'} = $new_attachment->bug_id;
|
|
$vars->{'oldid'} = $old_attachment->id;
|
|
$vars->{'old_desc'} = $old_attachment->description;
|
|
$vars->{'newid'} = $new_attachment->id;
|
|
$vars->{'new_desc'} = $new_attachment->description;
|
|
|
|
setup_template_patch_reader($last_reader, $format, $context, $vars);
|
|
}
|
|
$reader->iterate_fh($interdiff_fh, 'interdiff #' . $old_attachment->id .
|
|
' #' . $new_attachment->id);
|
|
close $interdiff_fh;
|
|
$ENV{'PATH'} = '';
|
|
|
|
# Delete temporary files.
|
|
unlink($old_filename) or warn "Could not unlink $old_filename: $!";
|
|
unlink($new_filename) or warn "Could not unlink $new_filename: $!";
|
|
}
|
|
|
|
######################
|
|
# Internal routines
|
|
######################
|
|
|
|
sub get_unified_diff {
|
|
my ($attachment, $format) = @_;
|
|
|
|
# Bring in the modules we need.
|
|
require PatchReader::Raw;
|
|
require PatchReader::FixPatchRoot;
|
|
require PatchReader::DiffPrinter::raw;
|
|
require PatchReader::PatchInfoGrabber;
|
|
require File::Temp;
|
|
|
|
$attachment->ispatch
|
|
|| ThrowCodeError('must_be_patch', { 'attach_id' => $attachment->id });
|
|
|
|
# Reads in the patch, converting to unified diff in a temp file.
|
|
my $reader = new PatchReader::Raw;
|
|
my $last_reader = $reader;
|
|
|
|
# Fixes patch root (makes canonical if possible).
|
|
if (Bugzilla->params->{'cvsroot'}) {
|
|
my $fix_patch_root =
|
|
new PatchReader::FixPatchRoot(Bugzilla->params->{'cvsroot'});
|
|
$last_reader->sends_data_to($fix_patch_root);
|
|
$last_reader = $fix_patch_root;
|
|
}
|
|
|
|
# Grabs the patch file info.
|
|
my $patch_info_grabber = new PatchReader::PatchInfoGrabber();
|
|
$last_reader->sends_data_to($patch_info_grabber);
|
|
$last_reader = $patch_info_grabber;
|
|
|
|
# Prints out to temporary file.
|
|
my ($fh, $filename) = File::Temp::tempfile();
|
|
if ($format ne 'raw' && Bugzilla->params->{'utf8'}) {
|
|
# The HTML page will be displayed with the UTF-8 encoding.
|
|
binmode $fh, ':utf8';
|
|
}
|
|
my $raw_printer = new PatchReader::DiffPrinter::raw($fh);
|
|
$last_reader->sends_data_to($raw_printer);
|
|
$last_reader = $raw_printer;
|
|
|
|
# Iterate!
|
|
$reader->iterate_string($attachment->id, $attachment->data);
|
|
|
|
return ($filename, $patch_info_grabber->patch_info()->{files});
|
|
}
|
|
|
|
sub warn_if_interdiff_might_fail {
|
|
my ($old_file_list, $new_file_list) = @_;
|
|
|
|
# Verify that the list of files diffed is the same.
|
|
my @old_files = sort keys %{$old_file_list};
|
|
my @new_files = sort keys %{$new_file_list};
|
|
if (@old_files != @new_files
|
|
|| join(' ', @old_files) ne join(' ', @new_files))
|
|
{
|
|
return 'interdiff1';
|
|
}
|
|
|
|
# Verify that the revisions in the files are the same.
|
|
foreach my $file (keys %{$old_file_list}) {
|
|
if ($old_file_list->{$file}{old_revision} ne
|
|
$new_file_list->{$file}{old_revision})
|
|
{
|
|
return 'interdiff2';
|
|
}
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
sub setup_patch_readers {
|
|
my ($diff_root, $context) = @_;
|
|
|
|
# Parameters:
|
|
# format=raw|html
|
|
# context=patch|file|0-n
|
|
# collapsed=0|1
|
|
# headers=0|1
|
|
|
|
# Define the patch readers.
|
|
# The reader that reads the patch in (whatever its format).
|
|
require PatchReader::Raw;
|
|
my $reader = new PatchReader::Raw;
|
|
my $last_reader = $reader;
|
|
# Fix the patch root if we have a cvs root.
|
|
if (Bugzilla->params->{'cvsroot'}) {
|
|
require PatchReader::FixPatchRoot;
|
|
$last_reader->sends_data_to(new PatchReader::FixPatchRoot(Bugzilla->params->{'cvsroot'}));
|
|
$last_reader->sends_data_to->diff_root($diff_root) if defined($diff_root);
|
|
$last_reader = $last_reader->sends_data_to;
|
|
}
|
|
|
|
# Add in cvs context if we have the necessary info to do it
|
|
if ($context ne 'patch' && Bugzilla->localconfig->{cvsbin}
|
|
&& Bugzilla->params->{'cvsroot_get'})
|
|
{
|
|
require PatchReader::AddCVSContext;
|
|
# We need to set $cvsbin as global, because PatchReader::CVSClient
|
|
# needs it in order to find 'cvs'.
|
|
$main::cvsbin = Bugzilla->localconfig->{cvsbin};
|
|
$last_reader->sends_data_to(
|
|
new PatchReader::AddCVSContext($context, Bugzilla->params->{'cvsroot_get'}));
|
|
$last_reader = $last_reader->sends_data_to;
|
|
}
|
|
|
|
return ($reader, $last_reader);
|
|
}
|
|
|
|
sub setup_template_patch_reader {
|
|
my ($last_reader, $format, $context, $vars) = @_;
|
|
my $cgi = Bugzilla->cgi;
|
|
my $template = Bugzilla->template;
|
|
|
|
require PatchReader::DiffPrinter::template;
|
|
|
|
# Define the vars for templates.
|
|
if (defined $cgi->param('headers')) {
|
|
$vars->{'headers'} = $cgi->param('headers');
|
|
}
|
|
else {
|
|
$vars->{'headers'} = 1;
|
|
}
|
|
|
|
$vars->{'collapsed'} = $cgi->param('collapsed');
|
|
$vars->{'context'} = $context;
|
|
$vars->{'do_context'} = Bugzilla->localconfig->{cvsbin}
|
|
&& Bugzilla->params->{'cvsroot_get'} && !$vars->{'newid'};
|
|
|
|
# Print everything out.
|
|
print $cgi->header(-type => 'text/html',
|
|
-expires => '+3M');
|
|
|
|
$last_reader->sends_data_to(new PatchReader::DiffPrinter::template($template,
|
|
"attachment/diff-header.$format.tmpl",
|
|
"attachment/diff-file.$format.tmpl",
|
|
"attachment/diff-footer.$format.tmpl",
|
|
{ %{$vars},
|
|
bonsai_url => Bugzilla->params->{'bonsai_url'},
|
|
lxr_url => Bugzilla->params->{'lxr_url'},
|
|
lxr_root => Bugzilla->params->{'lxr_root'},
|
|
}));
|
|
}
|
|
|
|
1;
|
|
|
|
__END__
|