File Coverage

File:Dpkg/Source/Package.pm
Coverage:23.4%

linestmtbrancondsubpodtimecode
1# Copyright © 2008-2011 Raphaël Hertzog <hertzog@debian.org>
2# Copyright © 2008-2019 Guillem Jover <guillem@debian.org>
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17=encoding utf8
18
19 - 30
=head1 NAME

Dpkg::Source::Package - manipulate Debian source packages

=head1 DESCRIPTION

This module provides a class that can manipulate Debian source
packages. While it supports both the extraction and the creation
of source packages, the only API that is officially supported
is the one that supports the extraction of the source package.

=cut
31
32package Dpkg::Source::Package 2.03;
33
34
9
9
9
18
12
120
use strict;
35
9
9
9
12
9
297
use warnings;
36
37our @EXPORT_OK = qw(
38    get_default_diff_ignore_regex
39    set_default_diff_ignore_regex
40    get_default_tar_ignore_pattern
41);
42
43
9
9
9
18
9
228
use Exporter qw(import);
44
9
9
9
15
72
30
use POSIX qw(:errno_h :sys_wait_h);
45
9
9
9
2142
6
222
use Carp;
46
9
9
9
2511
46215
366
use File::Temp;
47
9
9
9
1299
11373
249
use File::Copy qw(cp);
48
9
9
9
27
3
216
use File::Basename;
49
9
9
9
24
6
84
use File::Spec;
50
51
9
9
9
9
9
222
use Dpkg::Gettext;
52
9
9
9
18
6
276
use Dpkg::ErrorHandling;
53
9
9
9
1404
9
408
use Dpkg::Control;
54
9
9
9
1524
9
270
use Dpkg::Checksums;
55
9
9
9
1533
9
498
use Dpkg::Version;
56
9
9
9
1635
153
474
use Dpkg::Compression;
57
9
9
9
1446
9
282
use Dpkg::Path qw(check_files_are_the_same check_directory_traversal);
58
9
9
9
21
9
150
use Dpkg::Vendor qw(run_vendor_hook);
59
9
9
9
1176
9
141
use Dpkg::Source::Format;
60
9
9
9
1167
9
135
use Dpkg::OpenPGP;
61
9
9
9
21
6
735
use Dpkg::OpenPGP::ErrorCodes;
62
63my $diff_ignore_default_regex = '
64# Ignore general backup files
65(?:^|/).*~$|
66# Ignore emacs recovery files
67(?:^|/)\.#.*$|
68# Ignore vi swap files
69(?:^|/)\..*\.sw.$|
70# Ignore baz-style junk files or directories
71(?:^|/),,.*(?:$|/.*$)|
72# File-names that should be ignored (never directories)
73(?:^|/)(?:DEADJOE|\.arch-inventory|\.(?:bzr|cvs|hg|git|mtn-)ignore)$|
74# File or directory names that should be ignored
75(?:^|/)(?:CVS|RCS|\.deps|\{arch\}|\.arch-ids|\.svn|
76\.hg(?:tags|sigs)?|_darcs|\.git(?:attributes|modules|review)?|
77\.mailmap|\.shelf|_MTN|\.be|\.bzr(?:\.backup|tags)?)(?:$|/.*$)
78';
79# Take out comments and newlines
80$diff_ignore_default_regex =~ s/^#.*$//mg;
81$diff_ignore_default_regex =~ s/\n//sg;
82
83
9
9
9
18
3
12042
no warnings 'qw'; ## no critic (TestingAndDebugging::ProhibitNoWarnings)
84my @tar_ignore_default_pattern = qw(
85*.a
86*.la
87*.o
88*.so
89.*.sw?
90*/*~
91,,*
92.[#~]*
93.arch-ids
94.arch-inventory
95.be
96.bzr
97.bzr.backup
98.bzr.tags
99.bzrignore
100.cvsignore
101.deps
102.git
103.gitattributes
104.gitignore
105.gitmodules
106.gitreview
107.hg
108.hgignore
109.hgsigs
110.hgtags
111.mailmap
112.mtn-ignore
113.shelf
114.svn
115CVS
116DEADJOE
117RCS
118_MTN
119_darcs
120{arch}
121);
122## use critic
123
124 - 132
=head1 FUNCTIONS

=over 4

=item $string = get_default_diff_ignore_regex()

Returns the default diff ignore regex.

=cut
133
134sub get_default_diff_ignore_regex {
135
0
1
0
    return $diff_ignore_default_regex;
136}
137
138 - 142
=item set_default_diff_ignore_regex($string)

Set a regex as the new default diff ignore regex.

=cut
143
144sub set_default_diff_ignore_regex {
145
0
1
0
    my $regex = shift;
146
147
0
0
    $diff_ignore_default_regex = $regex;
148}
149
150 - 154
=item @array = get_default_tar_ignore_pattern()

Returns the default tar ignore pattern, as an array.

=cut
155
156sub get_default_tar_ignore_pattern {
157
0
1
0
    return @tar_ignore_default_pattern;
158}
159
160=back
161
162 - 205
=head1 METHODS

=over 4

=item $p = Dpkg::Source::Package->new(%opts, options => {})

Creates a new object corresponding to a source package. When the key
B<filename> is set to a F<.dsc> file, it will be used to initialize the
source package with its description. Otherwise if the B<format> key is
set to a valid value, the object will be initialized for that format
(since dpkg 1.19.3).

The B<options> key is a hash ref which supports the following options:

=over 8

=item skip_debianization

If set to 1, do not apply Debian changes on the extracted source package.

=item skip_patches

If set to 1, do not apply Debian-specific patches. This options is
specific for source packages using format "2.0" and "3.0 (quilt)".

=item require_valid_signature

If set to 1, the check_signature() method will be stricter and will error
out if the signature can't be verified.

=item require_strong_checksums

If set to 1, the check_checksums() method will be stricter and will error
out if there is no strong checksum.

=item copy_orig_tarballs

If set to 1, the extraction will copy the upstream tarballs next the
target directory. This is useful if you want to be able to rebuild the
source package after its extraction.

=back

=cut
206
207# Class methods
208sub new {
209
9
1
18
    my ($this, %args) = @_;
210
9
30
    my $class = ref($this) || $this;
211
9
27
    my $self = {
212        fields => Dpkg::Control->new(type => CTRL_DSC),
213        format => Dpkg::Source::Format->new(),
214        options => {},
215        checksums => Dpkg::Checksums->new(),
216        openpgp => Dpkg::OpenPGP->new(needs => { api => 'verify' }),
217    };
218
9
24
    bless $self, $class;
219
9
15
    if (exists $args{options}) {
220
0
0
        $self->{options} = $args{options};
221    }
222
9
18
    if (exists $args{filename}) {
223
0
0
        $self->initialize($args{filename});
224
0
0
        $self->init_options();
225    } elsif ($args{format}) {
226
0
0
        $self->{fields}{Format} = $args{format};
227
0
0
        $self->upgrade_object_type(0);
228
0
0
        $self->init_options();
229    }
230
231
9
21
    if ($self->{options}{require_valid_signature}) {
232
0
0
        $self->{report_verify} = \&error;
233    } else {
234
9
15
        $self->{report_verify} = \&warning;
235    }
236
237
9
15
    return $self;
238}
239
240sub init_options {
241
0
0
0
    my $self = shift;
242    # Use full ignore list by default
243    # note: this function is not called by V1 packages
244
0
0
    $self->{options}{diff_ignore_regex} ||= $diff_ignore_default_regex;
245
0
0
    $self->{options}{diff_ignore_regex} .= '|(?:^|/)debian/source/local-.*$';
246
0
0
    $self->{options}{diff_ignore_regex} .= '|(?:^|/)debian/files(?:\.new)?$';
247
0
0
    if (defined $self->{options}{tar_ignore}) {
248        $self->{options}{tar_ignore} = [ @tar_ignore_default_pattern ]
249
0
0
0
0
            unless @{$self->{options}{tar_ignore}};
250    } else {
251
0
0
        $self->{options}{tar_ignore} = [ @tar_ignore_default_pattern ];
252    }
253
0
0
0
0
    push @{$self->{options}{tar_ignore}},
254         'debian/source/local-options',
255         'debian/source/local-patch-header',
256         'debian/files',
257         'debian/files.new';
258
0
0
    $self->{options}{copy_orig_tarballs} //= 0;
259
260    # Skip debianization while specific to some formats has an impact
261    # on code common to all formats
262
0
0
    $self->{options}{skip_debianization} //= 0;
263
0
0
    $self->{options}{skip_patches} //= 0;
264
265    # Set default validation checks.
266
0
0
    $self->{options}{require_valid_signature} //= 0;
267
0
0
    $self->{options}{require_strong_checksums} //= 0;
268
269    # Set default compressor for new formats.
270
0
0
    $self->{options}{compression} //= 'xz';
271
0
0
    $self->{options}{comp_level} //= compression_get_level($self->{options}{compression});
272
0
0
    $self->{options}{comp_ext} //= compression_get_file_extension($self->{options}{compression});
273}
274
275sub initialize {
276
0
0
0
    my ($self, $filename) = @_;
277
0
0
    my ($fn, $dir) = fileparse($filename);
278
0
0
    error(g_('%s is not the name of a file'), $filename) unless $fn;
279
0
0
    $self->{basedir} = $dir || './';
280
0
0
    $self->{filename} = $fn;
281
282    # Read the fields
283
0
0
    my $fields = $self->{fields};
284
0
0
    $fields->load($filename);
285
0
0
    $self->{is_signed} = $fields->get_option('is_pgp_signed');
286
287
0
0
    foreach my $f (qw(Source Version Files)) {
288
0
0
        unless (defined($fields->{$f})) {
289
0
0
            error(g_('missing critical source control field %s'), $f);
290        }
291    }
292
293
0
0
    $self->{checksums}->add_from_control($fields, use_files_for_md5 => 1);
294
295
0
0
    $self->upgrade_object_type(0);
296}
297
298sub upgrade_object_type {
299
0
0
0
    my ($self, $update_format) = @_;
300
0
0
    $update_format //= 1;
301
302
0
0
    my $format = $self->{fields}{'Format'} // '1.0';
303
0
0
    my ($major, $minor, $variant) = $self->{format}->set($format);
304
305
0
0
    my $module = "Dpkg::Source::Package::V$major";
306
0
0
    $module .= '::' . ucfirst $variant if defined $variant;
307
0
0
    eval qq{
308        require $module;
309        \$minor = \$${module}::CURRENT_MINOR_VERSION;
310    };
311
0
0
    if ($@) {
312
0
0
        error(g_("source package format '%s' is not supported: %s"),
313              $format, $@);
314    }
315
0
0
    if ($update_format) {
316
0
0
        $self->{format}->set_from_parts($major, $minor, $variant);
317
0
0
        $self->{fields}{'Format'} = $self->{format}->get();
318    }
319
320
0
0
    $module->prerequisites() if $module->can('prerequisites');
321
0
0
    bless $self, $module;
322}
323
324 - 328
=item $p->get_filename()

Returns the filename of the DSC file.

=cut
329
330sub get_filename {
331
0
1
0
    my $self = shift;
332
0
0
    return File::Spec->catfile($self->{basedir}, $self->{filename});
333}
334
335 - 340
=item $p->get_files()

Returns the list of files referenced by the source package. The filenames
usually do not have any path information.

=cut
341
342sub get_files {
343
0
1
0
    my $self = shift;
344
0
0
    return $self->{checksums}->get_files();
345}
346
347 - 357
=item $p->check_checksums()

Verify the checksums embedded in the DSC file. It requires the presence of
the other files constituting the source package. If any inconsistency is
discovered, it immediately errors out. It will make sure at least one strong
checksum is present.

If the object has been created with the "require_strong_checksums" option,
then any problem will result in a fatal error.

=cut
358
359sub check_checksums {
360
0
1
0
    my $self = shift;
361
0
0
    my $checksums = $self->{checksums};
362
0
0
    my $warn_on_weak = 0;
363
364    # add_from_file verify the checksums if they are already existing
365
0
0
    foreach my $file ($checksums->get_files()) {
366
0
0
        if (not $checksums->has_strong_checksums($file)) {
367
0
0
            if ($self->{options}{require_strong_checksums}) {
368
0
0
                error(g_('source package uses only weak checksums'));
369            } else {
370
0
0
                $warn_on_weak = 1;
371            }
372        }
373
0
0
        my $pathname = File::Spec->catfile($self->{basedir}, $file);
374
0
0
        $checksums->add_from_file($pathname, key => $file);
375    }
376
377
0
0
    warning(g_('source package uses only weak checksums')) if $warn_on_weak;
378}
379
380sub get_basename {
381
0
0
0
    my ($self, $with_revision) = @_;
382
0
0
    my $f = $self->{fields};
383
0
0
    unless (exists $f->{'Source'} and exists $f->{'Version'}) {
384
0
0
        error(g_('%s and %s fields are required to compute the source basename'),
385              'Source', 'Version');
386    }
387
0
0
    my $v = Dpkg::Version->new($f->{'Version'});
388
0
0
    my $vs = $v->as_string(omit_epoch => 1, omit_revision => !$with_revision);
389
0
0
    return $f->{'Source'} . '_' . $vs;
390}
391
392 - 396
=item $p->get_basedirname()

Returns the default base directory name for the package.

=cut
397
398sub get_basedirname {
399
0
1
0
    my $self = shift;
400
401
0
0
    my $dirname = $self->get_basename();
402
0
0
    $dirname =~ s/_/-/;
403
404
0
0
    return $dirname;
405}
406
407sub find_original_tarballs {
408
0
0
0
    my ($self, %opts) = @_;
409
0
0
    $opts{extension} //= compression_get_file_extension_regex();
410
0
0
    $opts{include_main} //= 1;
411
0
0
    $opts{include_supplementary} //= 1;
412
0
0
    my $basename = $self->get_basename();
413
0
0
    my @tar;
414
0
0
    foreach my $dir ('.', $self->{basedir}, $self->{options}{origtardir}) {
415
0
0
        next unless defined($dir) and -d $dir;
416
0
0
        opendir(my $dir_dh, $dir) or syserr(g_('cannot opendir %s'), $dir);
417
0
0
        push @tar, map { File::Spec->catfile($dir, $_) } grep {
418
0
0
                ($opts{include_main} and
419                 /^\Q$basename\E\.orig\.tar\.$opts{extension}$/) or
420                ($opts{include_supplementary} and
421
0
0
                 /^\Q$basename\E\.orig-[[:alnum:]-]+\.tar\.$opts{extension}$/)
422            } readdir($dir_dh);
423
0
0
        closedir($dir_dh);
424    }
425
0
0
    return @tar;
426}
427
428 - 432
=item $p->get_upstream_signing_key($dir)

Get the filename for the upstream key.

=cut
433
434sub get_upstream_signing_key {
435
0
1
0
    my ($self, $dir) = @_;
436
437
0
0
    return "$dir/debian/upstream/signing-key.asc";
438}
439
440 - 446
=item $p->armor_original_tarball_signature($bin, $asc)

Convert a signature from binary to ASCII armored form. If the signature file
does not exist, it is a no-op. If the signature file is already ASCII armored
then simply copy it, otherwise convert it from binary to ASCII armored form.

=cut
447
448sub armor_original_tarball_signature {
449
24
1
39
    my ($self, $bin, $asc) = @_;
450
451
24
109
    if (-e $bin) {
452
15
42
        return $self->{openpgp}->armor('SIGNATURE', $bin, $asc);
453    }
454
455
9
33
    return;
456}
457
458 - 465
=item $p->check_original_tarball_signature($dir, @asc)

Verify the original upstream tarball signatures @asc using the upstream
public keys. It requires the origin upstream tarballs, their signatures
and the upstream signing key, as found in an unpacked source tree $dir.
If any inconsistency is discovered, it immediately errors out.

=cut
466
467sub check_original_tarball_signature {
468
0
1
    my ($self, $dir, @asc) = @_;
469
470
0
    my $upstream_key = $self->get_upstream_signing_key($dir);
471
0
    if (not -e $upstream_key) {
472
0
        warning(g_('upstream tarball signatures but no upstream signing key'));
473
0
        return;
474    }
475
476
0
    foreach my $asc (@asc) {
477
0
        my $datafile = $asc =~ s/\.asc$//r;
478
479
0
        info(g_('verifying %s'), $asc);
480
0
        my $rc = $self->{openpgp}->verify($datafile, $asc, $upstream_key);
481
0
        if ($rc) {
482
0
            $self->{report_verify}->(g_('cannot verify upstream tarball signature for %s: %s'),
483                                     $datafile, openpgp_errorcode_to_string($rc));
484        }
485    }
486}
487
488 - 493
=item $bool = $p->is_signed()

Returns 1 if the DSC files contains an embedded OpenPGP signature.
Otherwise returns 0.

=cut
494
495sub is_signed {
496
0
1
    my $self = shift;
497
0
    return $self->{is_signed};
498}
499
500 - 508
=item $p->check_signature()

Implement the same OpenPGP signature check that dpkg-source does.
In case of problems, it prints a warning or errors out.

If the object has been created with the "require_valid_signature" option,
then any problem will result in a fatal error.

=cut
509
510sub check_signature {
511
0
1
    my $self = shift;
512
0
    my $dsc = $self->get_filename();
513
0
    my @certs;
514
515
0
    push @certs, $self->{openpgp}->get_trusted_keyrings();
516
517
0
    foreach my $vendor_keyring (run_vendor_hook('package-keyrings')) {
518
0
        if (-r $vendor_keyring) {
519
0
            push @certs, $vendor_keyring;
520        }
521    }
522
523
0
    my $rc = $self->{openpgp}->inline_verify($dsc, undef, @certs);
524
0
    if ($rc) {
525
0
        $self->{report_verify}->(g_('cannot verify inline signature for %s: %s'),
526                                 $dsc, openpgp_errorcode_to_string($rc));
527    }
528}
529
530sub describe_cmdline_options {
531
0
0
    return;
532}
533
534sub parse_cmdline_options {
535
0
0
    my ($self, @opts) = @_;
536
0
    foreach my $option (@opts) {
537
0
        if (not $self->parse_cmdline_option($option)) {
538
0
            warning(g_('%s is not a valid option for %s'), $option, ref $self);
539        }
540    }
541}
542
543sub parse_cmdline_option {
544
0
0
    return 0;
545}
546
547 - 553
=item $p->extract($targetdir)

Extracts the source package in the target directory $targetdir. Beware
that if $targetdir already exists, it will be erased (as long as the
no_overwrite_dir option is set).

=cut
554
555sub extract {
556
0
1
    my ($self, $newdirectory) = @_;
557
558
0
    my ($ok, $error) = version_check($self->{fields}{'Version'});
559
0
    if (not $ok) {
560
0
        if ($self->{options}{ignore_bad_version}) {
561
0
            warning($error);
562        } else {
563
0
            error($error);
564        }
565    }
566
567    # Copy orig tarballs
568
0
    if ($self->{options}{copy_orig_tarballs}) {
569
0
        my $basename = $self->get_basename();
570
0
        my ($dirname, $destdir) = fileparse($newdirectory);
571
0
        $destdir ||= './';
572
0
        my $ext = compression_get_file_extension_regex();
573
0
0
        foreach my $orig (grep { /^\Q$basename\E\.orig(-[[:alnum:]-]+)?\.tar\.$ext$/ }
574                          $self->get_files())
575        {
576
0
            my $src = File::Spec->catfile($self->{basedir}, $orig);
577
0
            my $dst = File::Spec->catfile($destdir, $orig);
578
0
            if (not check_files_are_the_same($src, $dst, 1)) {
579
0
                cp($src, $dst)
580                    or syserr(g_('cannot copy %s to %s'), $src, $dst);
581            }
582        }
583    }
584
585    # Try extract
586
0
    $self->do_extract($newdirectory);
587
588    # Check for directory traversals.
589
0
    if (not $self->{options}{skip_debianization} and not $self->{no_check}) {
590        # We need to add a trailing slash to handle the debian directory
591        # possibly being a symlink.
592
0
        check_directory_traversal($newdirectory, "$newdirectory/debian/");
593    }
594
595    # Store format if non-standard so that next build keeps the same format
596
0
    if ($self->{fields}{'Format'} and
597        $self->{fields}{'Format'} ne '1.0' and
598        not $self->{options}{skip_debianization})
599    {
600
0
        my $srcdir = File::Spec->catdir($newdirectory, 'debian', 'source');
601
0
        my $format_file = File::Spec->catfile($srcdir, 'format');
602
0
        unless (-e $format_file) {
603
0
            mkdir($srcdir) unless -e $srcdir;
604
0
            $self->{format}->save($format_file);
605        }
606    }
607
608    # Make sure debian/rules is executable
609
0
    my $rules = File::Spec->catfile($newdirectory, 'debian', 'rules');
610
0
    my @s = lstat($rules);
611
0
    if (not scalar(@s)) {
612
0
        unless ($! == ENOENT) {
613
0
            syserr(g_('cannot stat %s'), $rules);
614        }
615        warning(g_('%s does not exist'), $rules)
616
0
            unless $self->{options}{skip_debianization};
617    } elsif (-f _) {
618
0
        chmod($s[2] | 0111, $rules)
619            or syserr(g_('cannot make %s executable'), $rules);
620    } else {
621
0
        warning(g_('%s is not a plain file'), $rules);
622    }
623}
624
625sub do_extract {
626
0
0
    croak 'Dpkg::Source::Package does not know how to unpack a ' .
627          'source package; use one of the subclasses';
628}
629
630# Function used specifically during creation of a source package
631
632sub before_build {
633
0
0
    my ($self, $dir) = @_;
634}
635
636sub build {
637
0
0
    my ($self, @args) = @_;
638
639
0
    $self->do_build(@args);
640}
641
642sub after_build {
643
0
0
    my ($self, $dir) = @_;
644}
645
646sub do_build {
647
0
0
    croak 'Dpkg::Source::Package does not know how to build a ' .
648          'source package; use one of the subclasses';
649}
650
651sub can_build {
652
0
0
    my ($self, $dir) = @_;
653
0
    return (0, 'can_build() has not been overridden');
654}
655
656sub add_file {
657
0
0
    my ($self, $filename) = @_;
658
0
    my ($fn, $dir) = fileparse($filename);
659
0
    if ($self->{checksums}->has_file($fn)) {
660
0
        croak "tried to add file '$fn' twice";
661    }
662
0
    $self->{checksums}->add_from_file($filename, key => $fn);
663    $self->{checksums}->export_to_control($self->{fields},
664
0
                                            use_files_for_md5 => 1);
665}
666
667sub commit {
668
0
0
    my ($self, @args) = @_;
669
670
0
    $self->do_commit(@args);
671}
672
673sub do_commit {
674
0
0
    my ($self, $dir) = @_;
675    info(g_("'%s' is not supported by the source format '%s'"),
676
0
         'dpkg-source --commit', $self->{fields}{'Format'});
677}
678
679sub write_dsc {
680
0
0
    my ($self, %opts) = @_;
681
0
    my $fields = $self->{fields};
682
683
0
0
    foreach my $f (keys %{$opts{override}}) {
684
0
        $fields->{$f} = $opts{override}{$f};
685    }
686
687
0
    unless ($opts{nocheck}) {
688
0
        foreach my $f (qw(Source Version Architecture)) {
689
0
            unless (defined($fields->{$f})) {
690
0
                error(g_('missing information for critical output field %s'), $f);
691            }
692        }
693
0
        foreach my $f (qw(Maintainer Standards-Version)) {
694
0
            unless (defined($fields->{$f})) {
695
0
                warning(g_('missing information for output field %s'), $f);
696            }
697        }
698    }
699
700
0
0
    foreach my $f (keys %{$opts{remove}}) {
701
0
        delete $fields->{$f};
702    }
703
704
0
    my $filename = $opts{filename};
705
0
    $filename //= $self->get_basename(1) . '.dsc';
706
0
    open(my $dsc_fh, '>', $filename)
707        or syserr(g_('cannot write %s'), $filename);
708
0
    $fields->apply_substvars($opts{substvars});
709
0
    $fields->output($dsc_fh);
710
0
    close($dsc_fh);
711}
712
713=back
714
715 - 756
=head1 CHANGES

=head2 Version 2.03 (dpkg 1.22.7)

New method: $p->get_basedirname().

=head2 Version 2.02 (dpkg 1.21.10)

New method: $p->armor_original_tarball_signature().

=head2 Version 2.01 (dpkg 1.20.1)

New method: $p->get_upstream_signing_key().

=head2 Version 2.00 (dpkg 1.20.0)

New method: $p->check_original_tarball_signature().

Remove variable: $diff_ignore_default_regexp.

Hide variable: @tar_ignore_default_pattern.

=head2 Version 1.03 (dpkg 1.19.3)

New option: format in new().

=head2 Version 1.02 (dpkg 1.18.7)

New option: require_strong_checksums in $p->check_checksums().

=head2 Version 1.01 (dpkg 1.17.2)

New functions: $p->get_default_diff_ignore_regex(),
$p->set_default_diff_ignore_regex(), $p->get_default_tar_ignore_pattern().

Deprecated variables: $diff_ignore_default_regexp, @tar_ignore_default_pattern

=head2 Version 1.00 (dpkg 1.16.1)

Mark the module as public.

=cut
757
7581;