CDS
view release on metacpan or search on metacpan
my $envelopeExpirationLimit = time * 1000;
# Read the tree index
$o->readIndex;
# Process all accounts
$o->{ui}->space;
$o->{ui}->title($o->{ui}->left(64, 'Accounts'), ' ', $o->{ui}->right(10, 'messages'), ' ', $o->{ui}->right(10, 'private'), ' ', $o->{ui}->right(10, 'public'), ' ', 'last modification');
$o->startProgress('linked objects');
$o->{usedHashes} = {};
$o->{missingObjects} = {};
$o->{brokenOrigins} = {};
my $countAccounts = 0;
my $countKeptEnvelopes = 0;
my $countDeletedEnvelopes = 0;
for my $accountHash (sort { $$a cmp $$b } $folderStore->accounts) {
# This would be the private key, but we don't use it right now
$o->{usedHashes}->{$accountHash->hex} = 1;
my $newestDate = 0;
my %sizeByBox;
my $accountFolder = $o->{accountsFolder}.'/'.$accountHash->hex;
foreach my $boxLabel (CDS->listFolder($accountFolder)) {
next if $boxLabel =~ /^\./;
my $boxFolder = $accountFolder.'/'.$boxLabel;
my $date = &lastModified($boxFolder);
$newestDate = $date if $newestDate < $date;
my $size = 0;
foreach my $filename (CDS->listFolder($boxFolder)) {
next if $filename =~ /^\./;
my $hash = pack('H*', $filename);
my $file = $boxFolder.'/'.$filename;
my $timestamp = $o->envelopeExpiration($hash, $boxFolder);
if ($timestamp > 0 && $timestamp < $envelopeExpirationLimit) {
$countDeletedEnvelopes += 1;
$handler->deleteEnvelope($file) // return;
next;
}
$countKeptEnvelopes += 1;
my $date = &lastModified($file);
$newestDate = $date if $newestDate < $date;
$size += $o->traverse($hash, $boxFolder);
}
$sizeByBox{$boxLabel} = $size;
}
$o->{ui}->line($accountHash->hex, ' ',
$o->{ui}->right(10, $o->{ui}->niceFileSize($sizeByBox{'messages'} || 0)), ' ',
$o->{ui}->right(10, $o->{ui}->niceFileSize($sizeByBox{'private'} || 0)), ' ',
$o->{ui}->right(10, $o->{ui}->niceFileSize($sizeByBox{'public'} || 0)), ' ',
$newestDate == 0 ? 'never' : $o->{ui}->niceDateTime($newestDate * 1000));
$countAccounts += 1;
}
$o->{ui}->line($countAccounts, ' accounts traversed');
$o->{ui}->space;
# Mark all objects that are younger than 1 day (so that objects being uploaded right now but not linked yet remain)
$o->{ui}->title('Objects');
$o->startProgress('objects');
my %objects;
my @topFolders = sort grep {$_ !~ /^\./} CDS->listFolder($o->{objectsFolder});
foreach my $topFolder (@topFolders) {
my @files = sort grep {$_ !~ /^\./} CDS->listFolder($o->{objectsFolder}.'/'.$topFolder);
foreach my $filename (@files) {
$o->incrementProgress;
my $hash = pack 'H*', $topFolder.$filename;
my @s = stat $o->{objectsFolder}.'/'.$topFolder.'/'.$filename;
$objects{$hash} = $s[7];
next if $s[9] < $dateLimit;
$o->traverse($hash, 'recent object');
}
}
$o->{ui}->line(scalar keys %objects, ' objects traversed');
$o->{ui}->space;
# Delete all unmarked objects, and add the marked objects to the new tree index
my $index = CDS::Record->new;
my $countKeptObjects = 0;
my $sizeKeptObjects = 0;
my $countDeletedObjects = 0;
my $sizeDeletedObjects = 0;
$handler->startDeletion;
$o->startProgress('delete-objects');
for my $hash (keys %objects) {
my $size = $objects{$hash};
if (exists $o->{usedHashes}->{$hash}) {
$countKeptObjects += 1;
$sizeKeptObjects += $size;
my $entry = $o->{index}->{$hash};
$index->addRecord($entry) if $entry;
} else {
$o->incrementProgress;
$countDeletedObjects += 1;
$sizeDeletedObjects += $size;
my $hashHex = unpack 'H*', $hash;
my $file = $o->{objectsFolder}.'/'.substr($hashHex, 0, 2).'/'.substr($hashHex, 2);
$handler->deleteObject($file) // return;
}
}
# Write the new tree index
CDS->writeBytesToFile($o->{storeFolder}.'/.index-new', $index->toObject->bytes);
rename $o->{storeFolder}.'/.index-new', $o->{storeFolder}.'/.index';
# Show what has been done
$o->{ui}->space;
$o->{ui}->line($countDeletedEnvelopes, ' ', $handler->{deletedEnvelopesText});
$o->{ui}->line($countKeptEnvelopes, ' ', $handler->{keptEnvelopesText});
my $line1 = $countDeletedObjects.' '.$handler->{deletedObjectsText};
my $line2 = $countKeptObjects.' '.$handler->{keptObjectsText};
my $maxLength = CDS->max(length $line1, length $line2);
$o->{ui}->line($o->{ui}->left($maxLength, $line1), ' ', $o->{ui}->gray($o->{ui}->niceFileSize($sizeDeletedObjects)));
$o->{ui}->line($o->{ui}->left($maxLength, $line2), ' ', $o->{ui}->gray($o->{ui}->niceFileSize($sizeKeptObjects)));
$o->{ui}->space;
my $cmd = shift;
$cmd->collect($o);
my $folderStore = $o->existingFolderStoreOrShowError // return;
$o->showStore($folderStore);
$o->{ui}->line('Checking permissions â¦');
my $logger = CDS::Commands::FolderStore::CheckLogger->new($o, $folderStore->folder);
$folderStore->checkPermissions($logger) || $o->traversalFailed($folderStore);
$logger->summary;
$o->{ui}->space;
}
sub fixPermissions {
my $o = shift;
my $cmd = shift;
$cmd->collect($o);
my $folderStore = $o->existingFolderStoreOrShowError // return;
$o->showStore($folderStore);
$o->{ui}->line('Fixing permissions â¦');
my $logger = CDS::Commands::FolderStore::FixLogger->new($o, $folderStore->folder);
$folderStore->checkPermissions($logger) || $o->traversalFailed($folderStore);
$logger->summary;
$o->{ui}->space;
}
sub traversalFailed {
my $o = shift;
my $folderStore = shift;
$o->{ui}->space;
$o->{ui}->p('Traversal failed because a file or folder could not be accessed. You may have to fix the permissions manually, or run this command with other privileges.');
$o->{ui}->p('If you have root privileges, you can take over this store using:');
my $userName = getpwuid($<);
my $groupName = getgrgid($();
$o->{ui}->line($o->{ui}->gold(' sudo chown -R ', $userName, ':', $groupName, ' ', $folderStore->folder));
$o->{ui}->p('and then set the desired permission scheme:');
$o->{ui}->line($o->{ui}->gold(' cds set permissions of ', $folderStore->folder, ' to â¦'));
$o->{ui}->space;
exit(1);
}
sub addAccount {
my $o = shift;
my $cmd = shift;
$cmd->collect($o);
# Prepare
my $folderStore = $o->existingFolderStoreOrShowError // return;
my $publicKey = $o->publicKey // return;
# Upload the public key onto the store
my $error = $folderStore->put($publicKey->hash, $publicKey->object);
return $o->{ui}->error('Unable to upload the public key: ', $error) if $error;
# Create the account folder
my $folder = $folderStore->folder.'/accounts/'.$publicKey->hash->hex;
my $permissions = $folderStore->permissions;
$permissions->mkdir($folder, $permissions->accountFolderMode);
return $o->{ui}->error('Unable to create folder "', $folder, '".') if ! -d $folder;
$o->{ui}->pGreen('Account ', $publicKey->hash->hex, ' added.');
return 1;
}
sub publicKey {
my $o = shift;
return $o->{keyPairToken}->keyPair->publicKey if $o->{keyPairToken};
if ($o->{file}) {
my $bytes = CDS->readBytesFromFile($o->{file}) // return $o->{ui}->error('Cannot read "', $o->{file}, '".');
my $object = CDS::Object->fromBytes($bytes) // return $o->{ui}->error('"', $o->{file}, '" is not a public key.');
return CDS::PublicKey->fromObject($object) // return $o->{ui}->error('"', $o->{file}, '" is not a public key.');
}
return $o->{actor}->uiGetPublicKey($o->{accountToken}->actorHash, $o->{accountToken}->cliStore, $o->{actor}->preferredKeyPairToken);
}
sub removeAccount {
my $o = shift;
my $cmd = shift;
$cmd->collect($o);
# Prepare the folder
my $folderStore = $o->existingFolderStoreOrShowError // return;
my $folder = $folderStore->folder.'/accounts/'.$o->{hash}->hex;
my $deletedFolder = $folderStore->folder.'/accounts/deleted-'.$o->{hash}->hex;
# Rename, so that it is not visible any more
$o->recursivelyDelete($deletedFolder) if -e $deletedFolder;
return $o->{ui}->line('The account ', $o->{hash}->hex, ' does not exist.') if ! -e $folder;
rename($folder, $deletedFolder) || return $o->{ui}->error('Unable to rename the folder "', $folder, '".');
# Try to delete it entirely
$o->recursivelyDelete($deletedFolder);
$o->{ui}->pGreen('Account ', $o->{hash}->hex, ' removed.');
return 1;
}
sub recursivelyDelete {
my $o = shift;
my $folder = shift;
for my $filename (CDS->listFolder($folder)) {
next if $filename =~ /^\./;
my $file = $folder.'/'.$filename;
if (-f $file) {
unlink $file || $o->{ui}->pOrange('Unable to remove the file "', $file, '".');
} elsif (-d $file) {
$o->recursivelyDelete($file);
}
}
$o->{aesKey} = $value;
}
sub collectFile {
my $o = shift;
my $label = shift;
my $value = shift;
$o->{dataFile} = $value;
}
sub collectHash {
my $o = shift;
my $label = shift;
my $value = shift;
push @{$o->{hashes}}, $value;
}
sub collectKeypair {
my $o = shift;
my $label = shift;
my $value = shift;
$o->{object} = $value->keyPair->publicKey->object;
}
sub collectObjectfile {
my $o = shift;
my $label = shift;
my $value = shift;
$o->{objectFile} = $value;
}
sub collectStore {
my $o = shift;
my $label = shift;
my $value = shift;
push @{$o->{stores}}, $value;
}
sub new {
my $class = shift;
my $actor = shift;
bless {actor => $actor, ui => $actor->ui} }
# END AUTOGENERATED
# HTML FOLDER NAME store-put
# HTML TITLE Put
sub help {
my $o = shift;
my $cmd = shift;
my $ui = $o->{ui};
$ui->space;
$ui->command('cds put FILE* [onto STORE*]');
$ui->p('Uploads object files onto object stores. If no stores are provided, the selected store is used. If an upload fails, the program immediately quits with exit code 1.');
$ui->space;
$ui->command('cds put FILE encrypted with AESKEY [onto STORE*]');
$ui->p('Encrypts the object before the upload.');
$ui->space;
$ui->command('cds put object with [HASH* and] FILE â¦');
$ui->p('Creates an object with the HASHes as hash list and FILE as data.');
$ui->space;
$ui->command('cds put public key of KEYPAIR â¦');
$ui->p('Uploads the public key of the indicated key pair onto the store.');
$ui->space;
}
sub put {
my $o = shift;
my $cmd = shift;
$o->{hashes} = [];
$o->{stores} = [];
$cmd->collect($o);
# Stores
push @{$o->{stores}}, $o->{actor}->preferredStore if ! scalar @{$o->{stores}};
$o->{get} = [];
return $o->putObject($o->{object}) if $o->{object};
return $o->putObjectFile if $o->{objectFile};
$o->putConstructedFile;
}
sub putObjectFile {
my $o = shift;
my $object = $o->{objectFile}->object;
# Display object information
$o->{ui}->space;
$o->{ui}->title('Uploading ', $o->{objectFile}->file, ' ', $o->{ui}->gray($o->{ui}->niceFileSize($object->byteLength)));
$o->{ui}->line($object->hashesCount == 1 ? '1 hash' : $object->hashesCount.' hashes');
$o->{ui}->line($o->{ui}->niceFileSize(length $object->data).' data');
$o->{ui}->space;
# Upload
$o->putObject($object);
}
sub putConstructedFile {
my $o = shift;
# Create the object
my $data = CDS->readBytesFromFile($o->{dataFile}) // return $o->{ui}->error('Unable to read "', $o->{dataFile}, '".');
my $header = pack('L>', scalar @{$o->{hashes}}) . join('', map { $_->bytes } @{$o->{hashes}});
my $object = CDS::Object->create($header, $data);
# Display object information
$o->{ui}->space;
$o->{ui}->title('Uploading new object ', $o->{ui}->gray($o->{ui}->niceFileSize(length $object->bytes)));
$o->{ui}->line($object->hashesCount == 1 ? '1 hash' : $object->hashesCount.' hashes');
$o->{ui}->line($o->{ui}->niceFileSize(length $object->data).' data from ', $o->{dataFile});
$o->{ui}->space;
# Upload
$o->putObject($object);
}
sub putObject {
my $o = shift;
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object';
my $keyPair = $o->{actor}->preferredKeyPairToken->keyPair;
# Encrypt it if desired
my $objectBytes;
if (defined $o->{aesKey}) {
$object = $object->crypt($o->{aesKey});
unshift @{$o->{get}}, ' decrypted with ', unpack('H*', $o->{aesKey}), ' ';
}
# Calculate the hash
my $hash = $object->calculateHash;
# Upload the object
my $successfulStore;
for my $store (@{$o->{stores}}) {
my $error = $store->put($hash, $object, $keyPair);
next if $error;
$o->{ui}->pGreen('The object was uploaded onto ', $store->url, '.');
$successfulStore = $store;
}
# Show the corresponding download line
return if ! $successfulStore;
$o->{ui}->space;
$o->{ui}->line('To download the object, type:');
$o->{ui}->line($o->{ui}->gold('cds get ', $hash->hex), $o->{ui}->gray(' on ', $successfulStore->url, @{$o->{get}}));
$o->{ui}->space;
}
package CDS::Commands::Remember;
# BEGIN AUTOGENERATED
sub register {
my $class = shift;
my $cds = shift;
my $help = shift;
my $node000 = CDS::Parser::Node->new(0, {constructor => \&new, function => \&showLabels});
my $node001 = CDS::Parser::Node->new(0);
my $node002 = CDS::Parser::Node->new(0);
my $node003 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help});
my $node004 = CDS::Parser::Node->new(0);
my $node005 = CDS::Parser::Node->new(0);
my $node006 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&forget});
my $node007 = CDS::Parser::Node->new(1);
my $node008 = CDS::Parser::Node->new(0);
my $node009 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&remember});
$cds->addArrow($node000, 1, 0, 'remember');
$cds->addArrow($node001, 1, 0, 'forget');
$help->addArrow($node003, 1, 0, 'forget');
$help->addArrow($node003, 1, 0, 'remember');
$node000->addArrow($node004, 1, 0, 'ACTOR', \&collectActor);
$node000->addArrow($node007, 1, 1, 'ACCOUNT', \&collectAccount);
$node000->addArrow($node007, 1, 0, 'ACTOR', \&collectActor);
$node000->addArrow($node007, 1, 0, 'KEYPAIR', \&collectKeypair);
$node000->addArrow($node007, 1, 0, 'STORE', \&collectStore);
$node001->addDefault($node002);
$node002->addArrow($node002, 1, 0, 'LABEL', \&collectLabel);
$node002->addArrow($node006, 1, 0, 'LABEL', \&collectLabel);
$node004->addArrow($node005, 1, 0, 'on');
$node005->addArrow($node007, 1, 0, 'STORE', \&collectStore);
$node007->addArrow($node008, 1, 0, 'as');
$node008->addArrow($node009, 1, 0, 'TEXT', \&collectText);
}
sub collectAccount {
my $o = shift;
my $label = shift;
my $value = shift;
$o->{store} = $value->cliStore;
$o->{actorHash} = $value->actorHash;
}
sub collectActor {
my $o = shift;
my $label = shift;
( run in 0.872 second using v1.01-cache-2.11-cpan-b16cb0d3907 )