App-FuguWeb
view release on metacpan or search on metacpan
lib/App/FuguWeb/Keys.pm view on Meta::CPAN
# nothing. A file of any content would therefore publish under
# the name that every consumer install reads.
#
# A signify signature holds two lines. The first line starts
# with 'untrusted comment: ', which signify(1) needs. The second
# line is the signature body: 100 base64 characters, which
# decode to 74 bytes whose first two bytes spell Ed.
sub _signature_problem ($bytes)
{
my @line = split /\n/, $bytes, -1;
pop @line if @line && $line[-1] eq '';
unless ( @line == 2 ) {
return
'it holds '
. scalar(@line)
. ' lines, and a signify signature holds 2';
}
unless ( $line[0] =~ /\Auntrusted comment: / ) {
return 'the first line is no untrusted comment line';
}
# 99 characters and one pad always decode to 74 bytes, so the
# length needs no second test. A key body needs one, because
# 56 characters carry no pad and decode to 42.
unless ( $line[1] =~ m{\A[A-Za-z0-9+/]{99}=\z} ) {
return 'the signature body is not 100 base64 characters';
}
my $raw = MIME::Base64::decode_base64( $line[1] );
unless ( substr( $raw, 0, 2 ) eq 'Ed' ) {
return 'the signature body names no signify algorithm';
}
return;
}
# _addresses(@keys):
# The Web Key Directory hash of each key, once for each hash, in
# the order that the keys arrive. Two keys of one address share
# one published path.
sub _addresses (@keys)
{
my %seen;
return grep { !$seen{$_}++ } map { $_->{wkd} } @keys;
}
# $self->_fail($reason):
# Record the reason and return undef, so each public method
# fails the same way.
sub _fail ( $self, $reason )
{
$self->{error} = $reason;
return;
}
# _index_body($rows):
# The body fragment of the human page: one row for each key, in
# publication order. Every value is escaped, and a value that
# the description left out becomes an empty cell.
sub _index_body ($rows)
{
my @head = (
'Key', 'Purpose', 'Serial', 'Type',
'Status', 'Fingerprint', 'Since', 'Until'
);
my $html = "<h1>Keys</h1>\n<table>\n<thead>\n<tr>";
$html .= "<th>$_</th>" for @head;
$html .= "</tr>\n</thead>\n<tbody>\n";
for my $row (@$rows) {
my $href = App::FuguWeb::escape_attr( $row->{name} );
my $stem = App::FuguWeb::escape_html( $row->{stem} );
$html .= qq{<tr><td><a href="$href">$stem</a></td>};
$html .= '<td>' . _cell( $row->{$_} ) . '</td>'
for qw(purpose serial type status fingerprint since until);
$html .= "</tr>\n";
}
return $html . "</tbody>\n</table>\n";
}
# _cell($value):
# One table cell. A value that the description left out becomes
# an empty cell, so a template tests one thing and the row keeps
# its column count.
sub _cell ($value)
{
return defined $value ? App::FuguWeb::escape_html($value) : '';
}
# _digest_of($path):
# The lowercase hex SHA256 digest of the file, or undef when the
# file does not open. addfile reads in blocks, so the check
# never holds a whole key set in memory.
sub _digest_of ($path)
{
open my $fh, '<', $path or return;
binmode $fh;
my $sha = Digest::SHA->new(256);
$sha->addfile($fh);
close $fh;
return lc $sha->hexdigest;
}
1;
( run in 0.887 second using v1.01-cache-2.11-cpan-364913b4093 )