CDS
view release on metacpan or search on metacpan
# 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 @_;
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 )