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 )