CDS

 view release on metacpan or  search on metacpan

lib/CDS.pm  view on Meta::CPAN

# This is part of the Condensation Perl Module 0.31 (cli) built on 2022-12-08.
# See https://condensation.io for information about the Condensation Data System.

use strict;
use warnings;
use 5.010000;
use CDS::C;

=pod

=head1 CDS - Condensation Data System

Condensation is a general-purpose distributed data system with conflict-free synchronization, and inherent end-to-end security.

This is the Perl implementation. It comes with a Perl module:

    use CDS;

and a command line tool:

    cds

More information is available on L<condensation.io|https://condensation.io>.

=cut

use Cwd;
use Digest::SHA;
use Encode;
use Fcntl;
use HTTP::Date;
use HTTP::Headers;
use HTTP::Request;
use HTTP::Server::Simple;
use LWP::UserAgent;
use Time::Local;
use utf8;
package CDS;

our $VERSION = '0.31';
our $edition = 'cli';
our $releaseDate = '2022-12-08';

sub now { time * 1000 }

sub SECOND { 1000 }
sub MINUTE { 60 * 1000 }
sub HOUR { 60 * 60 * 1000 }
sub DAY { 24 * 60 * 60 * 1000 }
sub WEEK { 7 * 24 * 60 * 60 * 1000 }
sub MONTH { 30 * 24 * 60 * 60 * 1000 }
sub YEAR { 365 * 24 * 60 * 60 * 1000 }

# File system utility functions.

sub readBytesFromFile {
	my $class = shift;
	my $filename = shift;

	open(my $fh, '<:bytes', $filename) || return;
	local $/;
	my $content = <$fh>;
	close $fh;
	return $content;
}

sub writeBytesToFile {
	my $class = shift;
	my $filename = shift;

	open(my $fh, '>:bytes', $filename) || return;
	print $fh @_;
	close $fh;
	return 1;
}

sub readTextFromFile {
	my $class = shift;
	my $filename = shift;

	open(my $fh, '<:utf8', $filename) || return;
	local $/;
	my $content = <$fh>;
	close $fh;
	return $content;
}

sub writeTextToFile {
	my $class = shift;
	my $filename = shift;

	open(my $fh, '>:utf8', $filename) || return;
	print $fh @_;

lib/CDS.pm  view on Meta::CPAN

	my $headers = HTTP::Headers->new;
	my $needsSignature = $boxLabel ne 'messages';
	my $response = $o->request('PUT', $o->{url}.'/accounts/'.$accountHash->hex.'/'.$boxLabel.'/'.$hash->hex, $headers, $keyPair, undef, $needsSignature);
	return if $response->is_success;
	return 'add ==> HTTP '.$response->status_line;
}

sub remove {
	my $o = shift;
	my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash';
	my $boxLabel = shift;
	my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash';
	my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair';

	my $headers = HTTP::Headers->new;
	my $response = $o->request('DELETE', $o->{url}.'/accounts/'.$accountHash->hex.'/'.$boxLabel.'/'.$hash->hex, $headers, $keyPair, undef, 1);
	return if $response->is_success;
	return 'remove ==> HTTP '.$response->status_line;
}

sub modify {
	my $o = shift;
	my $modifications = shift;
	my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair';

	my $bytes = $modifications->toRecord->toObject->bytes;
	my $needsSignature = $modifications->needsSignature($keyPair);
	my $headers = HTTP::Headers->new;
	$headers->header('Content-Type' => 'application/condensation-modifications');
	my $response = $o->request('POST', $o->{url}.'/accounts', $headers, $keyPair, $bytes, $needsSignature, 1);
	return if $response->is_success;
	return 'modify ==> HTTP '.$response->status_line;
}

# Executes a HTTP request.
sub request {
	my $class = shift;
	my $method = shift;
	my $url = shift;
	my $headers = shift;
	my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair';
	my $data = shift;
	my $addSignature = shift;
	my $signData = shift;
		# private
	$headers->date(time);
	$headers->header('User-Agent' => CDS->version);

	if ($addSignature && $keyPair) {
		my $hostAndPath = $url =~ /^https?:\/\/(.*)$/ ? $1 : $url;
		my $date = CDS::ISODate->millisecondString;
		my $bytesToSign = $date."\0".uc($method)."\0".$hostAndPath;
		$bytesToSign .= "\0".$data if $signData;
		my $hashBytesToSign = Digest::SHA::sha256($bytesToSign);
		my $signature = $keyPair->sign($hashBytesToSign);
		$headers->header('Condensation-Date' => $date);
		$headers->header('Condensation-Actor' => $keyPair->publicKey->hash->hex);
		$headers->header('Condensation-Signature' => unpack('H*', $signature));
	}

	return LWP::UserAgent->new->request(HTTP::Request->new($method, $url, $headers, $data));
}

# Models a hash, and offers binary and hexadecimal representation.
package CDS::Hash;

sub fromBytes {
	my $class = shift;
	my $hashBytes = shift // return;

	return if length $hashBytes != 32;
	return bless \$hashBytes;
}

sub fromHex {
	my $class = shift;
	my $hashHex = shift // return;

	$hashHex =~ /^\s*([a-fA-F0-9]{64,64})\s*$/ || return;
	my $hashBytes = pack('H*', $hashHex);
	return bless \$hashBytes;
}

sub calculateFor {
	my $class = shift;
	my $bytes = shift;

	# The Perl built-in SHA256 implementation is a tad faster than our SHA256 implementation.
	#return $class->fromBytes(CDS::C::sha256($bytes));
	return $class->fromBytes(Digest::SHA::sha256($bytes));
}

sub hex {
	my $o = shift;

	return unpack('H*', $$o);
}

sub shortHex {
	my $o = shift;

	return unpack('H*', substr($$o, 0, 8)) . '…';
}

sub bytes {
	my $o = shift;
	 $$o }

sub equals {
	my $this = shift;
	my $that = shift;

	return 1 if ! defined $this && ! defined $that;
	return if ! defined $this || ! defined $that;
	return $$this eq $$that;
}

sub cmp {
	my $this = shift;
	my $that = shift;
	 $$this cmp $$that }



( run in 0.675 second using v1.01-cache-2.11-cpan-e1769b4cff6 )