summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Bugzilla/Attachment/PatchReader.pm268
-rwxr-xr-xattachment.cgi288
2 files changed, 295 insertions, 261 deletions
diff --git a/Bugzilla/Attachment/PatchReader.pm b/Bugzilla/Attachment/PatchReader.pm
new file mode 100644
index 000000000..615ae91b4
--- /dev/null
+++ b/Bugzilla/Attachment/PatchReader.pm
@@ -0,0 +1,268 @@
+# -*- 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::Config qw(:localconfig);
+use Bugzilla::Error;
+
+
+sub process_diff {
+ my ($attachment, $format, $context) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $cgi = Bugzilla->cgi;
+ 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');
+
+ $reader->iterate_string('Attachment ' . $attachment->id, $attachment->data);
+ }
+ else {
+ $vars->{'other_patches'} = [];
+ if ($interdiffbin && $diffpath) {
+ # Get list of attachments on this bug.
+ # Ignore the current patch, but select the one right before it
+ # chronologically.
+ my $attachment_list =
+ $dbh->selectall_arrayref('SELECT attach_id, description
+ FROM attachments
+ WHERE bug_id = ?
+ AND ispatch = 1
+ ORDER BY creation_ts DESC',
+ undef, $attachment->bug_id);
+
+ my $select_next_patch = 0;
+ foreach (@$attachment_list) {
+ my ($other_id, $other_desc) = @$_;
+ if ($other_id == $attachment->id) {
+ $select_next_patch = 1;
+ }
+ else {
+ push(@{$vars->{'other_patches'}}, {'id' => $other_id,
+ 'desc' => $other_desc,
+ 'selected' => $select_next_patch});
+ if ($select_next_patch) {
+ $select_next_patch = 0;
+ }
+ }
+ }
+ }
+
+ $vars->{'bugid'} = $attachment->bug_id;
+ $vars->{'attachid'} = $attachment->id;
+ $vars->{'description'} = $attachment->description;
+
+ setup_template_patch_reader($last_reader, $format, $context, $vars);
+ # Actually print out the patch.
+ $reader->iterate_string('Attachment ' . $attachment->id, $attachment->data);
+ }
+}
+
+sub process_interdiff {
+ my ($old_attachment, $new_attachment, $format, $context) = @_;
+ my $cgi = Bugzilla->cgi;
+ my $vars = {};
+
+ # Get old patch data.
+ my ($old_filename, $old_file_list) = get_unified_diff($old_attachment);
+ # Get new patch data.
+ my ($new_filename, $new_file_list) = get_unified_diff($new_attachment);
+
+ 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'} = $diffpath;
+ open my $interdiff_fh, "$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');
+ }
+ else {
+ $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 = shift;
+
+ # 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();
+ 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' && $cvsbin && Bugzilla->params->{'cvsroot_get'}) {
+ require PatchReader::AddCVSContext;
+ $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 if !defined $cgi->param('headers');
+ }
+
+ $vars->{'collapsed'} = $cgi->param('collapsed');
+ $vars->{'context'} = $context;
+ $vars->{'do_context'} = $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__
diff --git a/attachment.cgi b/attachment.cgi
index 3939564a8..8541e8d5e 100755
--- a/attachment.cgi
+++ b/attachment.cgi
@@ -38,7 +38,6 @@ use strict;
use lib qw(.);
use Bugzilla;
-use Bugzilla::Config qw(:localconfig);
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Flag;
@@ -48,6 +47,7 @@ use Bugzilla::Util;
use Bugzilla::Bug;
use Bugzilla::Field;
use Bugzilla::Attachment;
+use Bugzilla::Attachment::PatchReader;
use Bugzilla::Token;
Bugzilla->login();
@@ -350,272 +350,38 @@ sub view
}
-sub interdiff
-{
- # Retrieve and validate parameters
- my ($old_id) = validateID('oldid');
- my ($new_id) = validateID('newid');
- my $format = validateFormat('html', 'raw');
- my $context = validateContext();
-
- # Get old patch data
- my ($old_bugid, $old_description, $old_filename, $old_file_list) =
- get_unified_diff($old_id);
-
- # Get new patch data
- my ($new_bugid, $new_description, $new_filename, $new_file_list) =
- get_unified_diff($new_id);
-
- 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'} = $diffpath;
- open my $interdiff_fh, "$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');
- }
- else
- {
- $vars->{warning} = $warning if $warning;
- $vars->{bugid} = $new_bugid;
- $vars->{oldid} = $old_id;
- $vars->{old_desc} = $old_description;
- $vars->{newid} = $new_id;
- $vars->{new_desc} = $new_description;
- delete $vars->{attachid};
- delete $vars->{do_context};
- delete $vars->{context};
- setup_template_patch_reader($last_reader, $format, $context);
- }
- $reader->iterate_fh($interdiff_fh, "interdiff #$old_id #$new_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: $!";
-}
-
-sub get_unified_diff
-{
- my ($id) = @_;
- my $dbh = Bugzilla->dbh;
-
- # Bring in the modules we need
- require PatchReader::Raw;
- require PatchReader::FixPatchRoot;
- require PatchReader::DiffPrinter::raw;
- require PatchReader::PatchInfoGrabber;
- require File::Temp;
-
- # Get the patch
- my ($bugid, $description, $ispatch, $thedata) = $dbh->selectrow_array(
- "SELECT bug_id, description, ispatch, thedata " .
- "FROM attachments " .
- "INNER JOIN attach_data " .
- "ON id = attach_id " .
- "WHERE attach_id = ?", undef, $id);
- if (!$ispatch) {
- $vars->{'attach_id'} = $id;
- ThrowCodeError("must_be_patch");
- }
-
- # 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();
- my $raw_printer = new PatchReader::DiffPrinter::raw($fh);
- $last_reader->sends_data_to($raw_printer);
- $last_reader = $raw_printer;
-
- # Iterate!
- $reader->iterate_string($id, $thedata);
-
- return ($bugid, $description, $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" && $cvsbin && Bugzilla->params->{'cvsroot_get'})
- {
- require PatchReader::AddCVSContext;
- $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) = @_;
-
- require PatchReader::DiffPrinter::template;
-
- # Define the vars for templates
- if (defined $cgi->param('headers')) {
- $vars->{headers} = $cgi->param('headers');
- } else {
- $vars->{headers} = 1 if !defined $cgi->param('headers');
- }
- $vars->{collapsed} = $cgi->param('collapsed');
- $vars->{context} = $context;
- $vars->{do_context} = $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'},
- }));
+sub interdiff {
+ # Retrieve and validate parameters
+ my ($old_id) = validateID('oldid');
+ my ($new_id) = validateID('newid');
+ my $format = validateFormat('html', 'raw');
+ my $context = validateContext();
+
+ # XXX - validateID should be replaced by Attachment::check_attachment()
+ # and should return an attachment object. This would save us a lot of
+ # trouble.
+ my $old_attachment = Bugzilla::Attachment->get($old_id);
+ my $new_attachment = Bugzilla::Attachment->get($new_id);
+
+ Bugzilla::Attachment::PatchReader::process_interdiff(
+ $old_attachment, $new_attachment, $format, $context);
}
-sub diff
-{
- # Retrieve and validate parameters
- my ($attach_id) = validateID();
- my $format = validateFormat('html', 'raw');
- my $context = validateContext();
- my $dbh = Bugzilla->dbh;
-
- # Get patch data
- my ($bugid, $description, $ispatch, $thedata) = $dbh->selectrow_array(
- "SELECT bug_id, description, ispatch, thedata FROM attachments " .
- "INNER JOIN attach_data ON id = attach_id " .
- "WHERE attach_id = ?", undef, $attach_id);
-
- # If it is not a patch, view normally
- if (!$ispatch)
- {
- view();
- return;
- }
+sub diff {
+ # Retrieve and validate parameters
+ my ($attach_id) = validateID();
+ my $format = validateFormat('html', 'raw');
+ my $context = validateContext();
- my ($reader, $last_reader) = setup_patch_readers(undef,$context);
+ my $attachment = Bugzilla::Attachment->get($attach_id);
- 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');
- $reader->iterate_string("Attachment $attach_id", $thedata);
- }
- else
- {
- $vars->{other_patches} = [];
- if ($interdiffbin && $diffpath) {
- # Get list of attachments on this bug.
- # Ignore the current patch, but select the one right before it
- # chronologically.
- my $sth = $dbh->prepare("SELECT attach_id, description
- FROM attachments
- WHERE bug_id = ?
- AND ispatch = 1
- ORDER BY creation_ts DESC");
- $sth->execute($bugid);
- my $select_next_patch = 0;
- while (my ($other_id, $other_desc) = $sth->fetchrow_array) {
- if ($other_id eq $attach_id) {
- $select_next_patch = 1;
- } else {
- push @{$vars->{other_patches}}, { id => $other_id, desc => $other_desc, selected => $select_next_patch };
- if ($select_next_patch) {
- $select_next_patch = 0;
- }
- }
- }
+ # If it is not a patch, view normally.
+ if (!$attachment->ispatch) {
+ view();
+ return;
}
- $vars->{bugid} = $bugid;
- $vars->{attachid} = $attach_id;
- $vars->{description} = $description;
- setup_template_patch_reader($last_reader, $format, $context);
- # Actually print out the patch
- $reader->iterate_string("Attachment $attach_id", $thedata);
- }
+ Bugzilla::Attachment::PatchReader::process_diff($attachment, $format, $context);
}
# Display all attachments for a given bug in a series of IFRAMEs within one