# upstream-signature -- lintian check script -*- perl -*-
#
# Copyright (C) 2019 Felix Lechner
#
# 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 of the License, 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 this program.  If not, you can find it on the World Wide
# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.

package Lintian::Check::UpstreamSignature;

use v5.20;
use warnings;
use utf8;

use Path::Tiny;

use Moo;
use namespace::clean;

with 'Lintian::Check';

sub source {
    my ($self) = @_;

    my $SIGNING_KEY_FILENAMES
      = $self->data->load('common/signing-key-filenames');

    my @keynames = $SIGNING_KEY_FILENAMES->all;
    my @keypaths
      = map { $self->processable->patched->resolve_path("debian/$_") }
      @keynames;
    my @keys = grep { $_ && $_->is_file } @keypaths;

    # in uscan's gittag mode,signature will never match
    my $watch = $self->processable->patched->resolve_path('debian/watch');
    my $gittag = $watch && $watch->bytes =~ /pgpmode=gittag/;

    my @filenames = sort keys %{$self->processable->files};
    my @origtar= grep { /^.*\.orig(?:-[A-Za-z\d-]+)?\.tar\./ }
      grep { !/\.asc$/ }@filenames;

    my %signatures;
    for my $filename (@origtar) {

        my ($uncompressed) = ($filename =~ /(^.*\.tar)/);

        my @componentsigs;
        for my $tarball ($filename, $uncompressed) {
            my $signaturename = "$tarball.asc";
            push(@componentsigs, $signaturename)
              if exists $self->processable->files->{$signaturename};
        }

        $signatures{$filename} = \@componentsigs;
    }

    # orig tarballs should be signed if upstream's public key is present
    if (@keys && !$self->processable->repacked && !$gittag) {

        for my $filename (@origtar) {

            $self->hint('orig-tarball-missing-upstream-signature', $filename)
              unless scalar @{$signatures{$filename}};
        }
    }

    my $parentdir = path($self->processable->path)->parent->stringify;

    # check signatures
    my @allsigs = map { @{$signatures{$_}} } @origtar;
    for my $signature (@allsigs) {
        my $sig_file = path($parentdir)->child($signature);
        # Only try to slurp file if it exists. Otherwise Path::Tiny ≥
        # 0.142 will bail out. (Returned empty string instead before
        # that version.)
        next unless $sig_file->is_file;

        # take from location near input file
        my $contents = $sig_file->slurp;

        if ($contents =~ /^-----BEGIN PGP ARMORED FILE-----/m) {

            if ($contents =~ /^LS0tLS1CRUd/m) {
                # doubly armored
                $self->hint('doubly-armored-upstream-signature', $signature);

            } else {
                # non standard armored header
                $self->hint('explicitly-armored-upstream-signature',
                    $signature);
            }

            my @spurious = ($contents =~ /\n([^:\n]+):/g);
            $self->hint('spurious-fields-in-upstream-signature',
                $signature, @spurious)
              if @spurious;
        }

        # multiple signatures in one file
        $self->hint('concatenated-upstream-signatures', $signature)
          if $contents
          =~ m/(?:-----BEGIN PGP SIGNATURE-----[^-]*-----END PGP SIGNATURE-----\s*){2,}/;
    }

    return;
}

1;

# Local Variables:
# indent-tabs-mode: nil
# cperl-indent-level: 4
# End:
# vim: syntax=perl sw=4 sts=4 sr et
