line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# This is part of the Condensation Perl Module 0.27 (cli) built on 2022-02-10. |
2
|
|
|
|
|
|
|
# See https://condensation.io for information about the Condensation Data System. |
3
|
|
|
|
|
|
|
|
4
|
1
|
|
|
1
|
|
66709
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
24
|
|
5
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
18
|
|
6
|
1
|
|
|
1
|
|
20
|
use 5.010000; |
|
1
|
|
|
|
|
3
|
|
7
|
1
|
|
|
1
|
|
617
|
use CDS::C; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
37
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=pod |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 CDS - Condensation Data System |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Condensation is a general-purpose distributed data system with conflict-free synchronization, and inherent end-to-end security. |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
This is the Perl implementation. It comes with a Perl module: |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use CDS; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
and a command line tool: |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
cds |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
More information is available on L. |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=cut |
26
|
|
|
|
|
|
|
|
27
|
1
|
|
|
1
|
|
5
|
use Cwd; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
52
|
|
28
|
1
|
|
|
1
|
|
1158
|
use Digest::SHA; |
|
1
|
|
|
|
|
2627
|
|
|
1
|
|
|
|
|
42
|
|
29
|
1
|
|
|
1
|
|
497
|
use Encode; |
|
1
|
|
|
|
|
16154
|
|
|
1
|
|
|
|
|
78
|
|
30
|
1
|
|
|
1
|
|
7
|
use Fcntl; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
182
|
|
31
|
1
|
|
|
1
|
|
421
|
use HTTP::Date; |
|
1
|
|
|
|
|
3483
|
|
|
1
|
|
|
|
|
50
|
|
32
|
1
|
|
|
1
|
|
474
|
use HTTP::Headers; |
|
1
|
|
|
|
|
3921
|
|
|
1
|
|
|
|
|
29
|
|
33
|
1
|
|
|
1
|
|
382
|
use HTTP::Request; |
|
1
|
|
|
|
|
14640
|
|
|
1
|
|
|
|
|
27
|
|
34
|
1
|
|
|
1
|
|
470
|
use HTTP::Server::Simple; |
|
1
|
|
|
|
|
16139
|
|
|
1
|
|
|
|
|
29
|
|
35
|
1
|
|
|
1
|
|
690
|
use LWP::UserAgent; |
|
1
|
|
|
|
|
21902
|
|
|
1
|
|
|
|
|
27
|
|
36
|
1
|
|
|
1
|
|
5
|
use Time::Local; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
46
|
|
37
|
1
|
|
|
1
|
|
505
|
use utf8; |
|
1
|
|
|
|
|
13
|
|
|
1
|
|
|
|
|
8
|
|
38
|
|
|
|
|
|
|
package CDS; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
our $VERSION = '0.27'; |
41
|
|
|
|
|
|
|
our $edition = 'cli'; |
42
|
|
|
|
|
|
|
our $releaseDate = '2022-02-10'; |
43
|
|
|
|
|
|
|
|
44
|
0
|
|
|
0
|
0
|
0
|
sub now { time * 1000 } |
45
|
|
|
|
|
|
|
|
46
|
0
|
|
|
0
|
0
|
0
|
sub SECOND { 1000 } |
47
|
0
|
|
|
0
|
0
|
0
|
sub MINUTE { 60 * 1000 } |
48
|
0
|
|
|
0
|
0
|
0
|
sub HOUR { 60 * 60 * 1000 } |
49
|
0
|
|
|
0
|
0
|
0
|
sub DAY { 24 * 60 * 60 * 1000 } |
50
|
0
|
|
|
0
|
0
|
0
|
sub WEEK { 7 * 24 * 60 * 60 * 1000 } |
51
|
0
|
|
|
0
|
0
|
0
|
sub MONTH { 30 * 24 * 60 * 60 * 1000 } |
52
|
0
|
|
|
0
|
0
|
0
|
sub YEAR { 365 * 24 * 60 * 60 * 1000 } |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# File system utility functions. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
sub readBytesFromFile { |
57
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
58
|
0
|
|
|
|
|
0
|
my $filename = shift; |
59
|
|
|
|
|
|
|
|
60
|
0
|
0
|
|
|
|
0
|
open(my $fh, '<:bytes', $filename) || return; |
61
|
0
|
|
|
|
|
0
|
local $/; |
62
|
0
|
|
|
|
|
0
|
my $content = <$fh>; |
63
|
0
|
|
|
|
|
0
|
close $fh; |
64
|
0
|
|
|
|
|
0
|
return $content; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub writeBytesToFile { |
68
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
69
|
0
|
|
|
|
|
0
|
my $filename = shift; |
70
|
|
|
|
|
|
|
|
71
|
0
|
0
|
|
|
|
0
|
open(my $fh, '>:bytes', $filename) || return; |
72
|
0
|
|
|
|
|
0
|
print $fh @_; |
73
|
0
|
|
|
|
|
0
|
close $fh; |
74
|
0
|
|
|
|
|
0
|
return 1; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub readTextFromFile { |
78
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
79
|
0
|
|
|
|
|
0
|
my $filename = shift; |
80
|
|
|
|
|
|
|
|
81
|
0
|
0
|
|
|
|
0
|
open(my $fh, '<:utf8', $filename) || return; |
82
|
0
|
|
|
|
|
0
|
local $/; |
83
|
0
|
|
|
|
|
0
|
my $content = <$fh>; |
84
|
0
|
|
|
|
|
0
|
close $fh; |
85
|
0
|
|
|
|
|
0
|
return $content; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub writeTextToFile { |
89
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
90
|
0
|
|
|
|
|
0
|
my $filename = shift; |
91
|
|
|
|
|
|
|
|
92
|
0
|
0
|
|
|
|
0
|
open(my $fh, '>:utf8', $filename) || return; |
93
|
0
|
|
|
|
|
0
|
print $fh @_; |
94
|
0
|
|
|
|
|
0
|
close $fh; |
95
|
0
|
|
|
|
|
0
|
return 1; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub listFolder { |
99
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
100
|
0
|
|
|
|
|
0
|
my $folder = shift; |
101
|
|
|
|
|
|
|
|
102
|
0
|
0
|
|
|
|
0
|
opendir(my $dh, $folder) || return; |
103
|
0
|
|
|
|
|
0
|
my @files = readdir $dh; |
104
|
0
|
|
|
|
|
0
|
closedir $dh; |
105
|
0
|
|
|
|
|
0
|
return @files; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub intermediateFolders { |
109
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
110
|
0
|
|
|
|
|
0
|
my $path = shift; |
111
|
|
|
|
|
|
|
|
112
|
0
|
|
|
|
|
0
|
my @paths = ($path); |
113
|
0
|
|
|
|
|
0
|
while (1) { |
114
|
0
|
0
|
|
|
|
0
|
$path =~ /^(.+)\/(.*?)$/ || last; |
115
|
0
|
|
|
|
|
0
|
$path = $1; |
116
|
0
|
0
|
|
|
|
0
|
next if ! length $2; |
117
|
0
|
|
|
|
|
0
|
unshift @paths, $path; |
118
|
|
|
|
|
|
|
} |
119
|
0
|
|
|
|
|
0
|
return @paths; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# This is for debugging purposes only. |
123
|
|
|
|
|
|
|
sub log { |
124
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
125
|
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
0
|
print STDERR @_, "\n"; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub min { |
130
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
131
|
|
|
|
|
|
|
|
132
|
0
|
|
|
|
|
0
|
my $min = shift; |
133
|
0
|
|
|
|
|
0
|
for my $number (@_) { |
134
|
0
|
0
|
|
|
|
0
|
$min = $min < $number ? $min : $number; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
0
|
|
|
|
|
0
|
return $min; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub max { |
141
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
142
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
0
|
my $max = shift; |
144
|
0
|
|
|
|
|
0
|
for my $number (@_) { |
145
|
0
|
0
|
|
|
|
0
|
$max = $max > $number ? $max : $number; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
0
|
return $max; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub booleanCompare { |
152
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
153
|
0
|
|
|
|
|
0
|
my $a = shift; |
154
|
0
|
|
|
|
|
0
|
my $b = shift; |
155
|
0
|
0
|
0
|
|
|
0
|
$a && $b ? 0 : $a ? 1 : $b ? -1 : 0 } |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# Utility functions for random sequences |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
srand(time); |
160
|
|
|
|
|
|
|
our @hexDigits = ('0'..'9', 'a'..'f'); |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub randomHex { |
163
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
164
|
0
|
|
|
|
|
0
|
my $length = shift; |
165
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
0
|
return substr(unpack('H*', CDS::C::randomBytes(int(($length + 1) / 2))), 0, $length); |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
sub randomBytes { |
170
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
171
|
0
|
|
|
|
|
0
|
my $length = shift; |
172
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
0
|
return CDS::C::randomBytes($length); |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub randomKey { |
177
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
178
|
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
0
|
return CDS::C::randomBytes(32); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
0
|
|
|
0
|
0
|
0
|
sub version { 'Condensation, Perl, '.$CDS::VERSION } |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# Conversion of numbers and booleans to and from bytes. |
185
|
|
|
|
|
|
|
# To convert text, use Encode::encode_utf8($text) and Encode::decode_utf8($bytes). |
186
|
|
|
|
|
|
|
# To convert hex sequences, use pack('H*', $hex) and unpack('H*', $bytes). |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub bytesFromBoolean { |
189
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
190
|
0
|
|
|
|
|
0
|
my $value = shift; |
191
|
0
|
0
|
|
|
|
0
|
$value ? 'y' : '' } |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub bytesFromInteger { |
194
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
195
|
0
|
|
|
|
|
0
|
my $value = shift; |
196
|
|
|
|
|
|
|
|
197
|
0
|
0
|
0
|
|
|
0
|
return '' if $value >= 0 && $value < 1; |
198
|
0
|
0
|
0
|
|
|
0
|
return pack 'c', $value if $value >= -0x80 && $value < 0x80; |
199
|
0
|
0
|
0
|
|
|
0
|
return pack 's>', $value if $value >= -0x8000 && $value < 0x8000; |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# This works up to 63 bits, plus 1 sign bit |
202
|
0
|
|
|
|
|
0
|
my $bytes = pack 'q>', $value; |
203
|
|
|
|
|
|
|
|
204
|
0
|
|
|
|
|
0
|
my $pos = 0; |
205
|
0
|
|
|
|
|
0
|
my $first = ord(substr($bytes, 0, 1)); |
206
|
0
|
0
|
|
|
|
0
|
if ($value > 0) { |
|
|
0
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# Perl internally uses an unsigned 64-bit integer if the value is positive |
208
|
0
|
0
|
|
|
|
0
|
return "\x7f\xff\xff\xff\xff\xff\xff\xff" if $first >= 128; |
209
|
0
|
|
|
|
|
0
|
while ($first == 0) { |
210
|
0
|
|
|
|
|
0
|
my $next = ord(substr($bytes, $pos + 1, 1)); |
211
|
0
|
0
|
|
|
|
0
|
last if $next >= 128; |
212
|
0
|
|
|
|
|
0
|
$first = $next; |
213
|
0
|
|
|
|
|
0
|
$pos += 1; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
} elsif ($first == 255) { |
216
|
0
|
|
|
|
|
0
|
while ($first == 255) { |
217
|
0
|
|
|
|
|
0
|
my $next = ord(substr($bytes, $pos + 1, 1)); |
218
|
0
|
0
|
|
|
|
0
|
last if $next < 128; |
219
|
0
|
|
|
|
|
0
|
$first = $next; |
220
|
0
|
|
|
|
|
0
|
$pos += 1; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
0
|
|
|
|
|
0
|
return substr($bytes, $pos); |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub bytesFromUnsigned { |
228
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
229
|
0
|
|
|
|
|
0
|
my $value = shift; |
230
|
|
|
|
|
|
|
|
231
|
0
|
0
|
|
|
|
0
|
return '' if $value < 1; |
232
|
0
|
0
|
|
|
|
0
|
return pack 'C', $value if $value < 0x100; |
233
|
0
|
0
|
|
|
|
0
|
return pack 'S>', $value if $value < 0x10000; |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# This works up to 64 bits |
236
|
0
|
|
|
|
|
0
|
my $bytes = pack 'Q>', $value; |
237
|
0
|
|
|
|
|
0
|
my $pos = 0; |
238
|
0
|
|
|
|
|
0
|
$pos += 1 while substr($bytes, $pos, 1) eq "\0"; |
239
|
0
|
|
|
|
|
0
|
return substr($bytes, $pos); |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
sub bytesFromFloat32 { |
243
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
244
|
0
|
|
|
|
|
0
|
my $value = shift; |
245
|
0
|
|
|
|
|
0
|
pack('f', $value) } |
246
|
|
|
|
|
|
|
sub bytesFromFloat64 { |
247
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
248
|
0
|
|
|
|
|
0
|
my $value = shift; |
249
|
0
|
|
|
|
|
0
|
pack('d', $value) } |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub booleanFromBytes { |
252
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
253
|
0
|
|
|
|
|
0
|
my $bytes = shift; |
254
|
|
|
|
|
|
|
|
255
|
0
|
|
|
|
|
0
|
return length $bytes > 0; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub integerFromBytes { |
259
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
260
|
0
|
|
|
|
|
0
|
my $bytes = shift; |
261
|
|
|
|
|
|
|
|
262
|
0
|
0
|
|
|
|
0
|
return 0 if ! length $bytes; |
263
|
0
|
|
|
|
|
0
|
my $value = unpack('C', substr($bytes, 0, 1)); |
264
|
0
|
0
|
|
|
|
0
|
$value -= 0x100 if $value & 0x80; |
265
|
0
|
|
|
|
|
0
|
for my $i (1 .. length($bytes) - 1) { |
266
|
0
|
|
|
|
|
0
|
$value *= 256; |
267
|
0
|
|
|
|
|
0
|
$value += unpack('C', substr($bytes, $i, 1)); |
268
|
|
|
|
|
|
|
} |
269
|
0
|
|
|
|
|
0
|
return $value; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub unsignedFromBytes { |
273
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
274
|
0
|
|
|
|
|
0
|
my $bytes = shift; |
275
|
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
0
|
my $value = 0; |
277
|
0
|
|
|
|
|
0
|
for my $i (0 .. length($bytes) - 1) { |
278
|
0
|
|
|
|
|
0
|
$value *= 256; |
279
|
0
|
|
|
|
|
0
|
$value += unpack('C', substr($bytes, $i, 1)); |
280
|
|
|
|
|
|
|
} |
281
|
0
|
|
|
|
|
0
|
return $value; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
sub floatFromBytes { |
285
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
286
|
0
|
|
|
|
|
0
|
my $bytes = shift; |
287
|
|
|
|
|
|
|
|
288
|
0
|
0
|
|
|
|
0
|
return unpack('f', $bytes) if length $bytes == 4; |
289
|
0
|
0
|
|
|
|
0
|
return unpack('d', $bytes) if length $bytes == 8; |
290
|
0
|
|
|
|
|
0
|
return undef; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
# Initial counter value for AES in CTR mode |
294
|
0
|
|
|
0
|
0
|
0
|
sub zeroCTR { "\0" x 16 } |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
my $emptyBytesHash = CDS::Hash->fromHex('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); |
297
|
0
|
|
|
0
|
0
|
0
|
sub emptyBytesHash { $emptyBytesHash } |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
# Checks if a box label is valid. |
300
|
|
|
|
|
|
|
sub isValidBoxLabel { |
301
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
302
|
0
|
|
|
|
|
0
|
my $label = shift; |
303
|
0
|
0
|
0
|
|
|
0
|
$label eq 'messages' || $label eq 'private' || $label eq 'public' } |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# Groups box additions or removals by account hash and box label. |
306
|
|
|
|
|
|
|
sub groupedBoxOperations { |
307
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
308
|
0
|
|
|
|
|
0
|
my $operations = shift; |
309
|
|
|
|
|
|
|
|
310
|
0
|
|
|
|
|
0
|
my %byAccountHash; |
311
|
0
|
|
|
|
|
0
|
for my $operation (@$operations) { |
312
|
0
|
|
|
|
|
0
|
my $accountHashBytes = $operation->{accountHash}->bytes; |
313
|
0
|
0
|
|
|
|
0
|
$byAccountHash{$accountHashBytes} = {accountHash => $operation->{accountHash}, byBoxLabel => {}} if ! exists $byAccountHash{$accountHashBytes}; |
314
|
0
|
|
|
|
|
0
|
my $byBoxLabel = $byAccountHash{$accountHashBytes}->{byBoxLabel}; |
315
|
0
|
|
|
|
|
0
|
my $boxLabel = $operation->{boxLabel}; |
316
|
0
|
0
|
|
|
|
0
|
$byBoxLabel->{$boxLabel} = [] if ! exists $byBoxLabel->{$boxLabel}; |
317
|
0
|
|
|
|
|
0
|
push @{$byBoxLabel->{$boxLabel}}, $operation; |
|
0
|
|
|
|
|
0
|
|
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
0
|
|
|
|
|
0
|
return values %byAccountHash; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
### Open envelopes ### |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub verifyEnvelopeSignature { |
326
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
327
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
328
|
0
|
0
|
0
|
|
|
0
|
my $publicKey = shift; die 'wrong type '.ref($publicKey).' for $publicKey' if defined $publicKey && ref $publicKey ne 'CDS::PublicKey'; |
|
0
|
|
|
|
|
0
|
|
329
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
# Read the signature |
332
|
0
|
|
|
|
|
0
|
my $signature = $envelope->child('signature')->bytesValue; |
333
|
0
|
0
|
|
|
|
0
|
return if length $signature < 1; |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# Verify the signature |
336
|
0
|
0
|
|
|
|
0
|
return if ! $publicKey->verifyHash($hash, $signature); |
337
|
0
|
|
|
|
|
0
|
return 1; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# The result of parsing an ACCOUNT token (see Token.pm). |
341
|
|
|
|
|
|
|
package CDS::AccountToken; |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
sub new { |
344
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
345
|
0
|
|
|
|
|
0
|
my $cliStore = shift; |
346
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
347
|
|
|
|
|
|
|
|
348
|
0
|
|
|
|
|
0
|
return bless { |
349
|
|
|
|
|
|
|
cliStore => $cliStore, |
350
|
|
|
|
|
|
|
actorHash => $actorHash, |
351
|
|
|
|
|
|
|
}; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
0
|
|
|
0
|
|
0
|
sub cliStore { shift->{cliStore} } |
355
|
0
|
|
|
0
|
|
0
|
sub actorHash { shift->{actorHash} } |
356
|
|
|
|
|
|
|
sub url { |
357
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
358
|
0
|
|
|
|
|
0
|
$o->{cliStore}->url.'/accounts/'.$o->{actorHash}->hex } |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
package CDS::ActorGroup; |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# Members must be sorted in descending revision order, such that the member with the most recent revision is first. Members must not include any revoked actors. |
363
|
|
|
|
|
|
|
sub new { |
364
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
365
|
0
|
|
|
|
|
0
|
my $members = shift; |
366
|
0
|
|
|
|
|
0
|
my $entrustedActorsRevision = shift; |
367
|
0
|
|
|
|
|
0
|
my $entrustedActors = shift; |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# Create the cache for the "contains" method |
370
|
0
|
|
|
|
|
0
|
my $containCache = {}; |
371
|
0
|
|
|
|
|
0
|
for my $member (@$members) { |
372
|
0
|
|
|
|
|
0
|
$containCache->{$member->actorOnStore->publicKey->hash->bytes} = 1; |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
0
|
|
|
|
|
0
|
return bless { |
376
|
|
|
|
|
|
|
members => $members, |
377
|
|
|
|
|
|
|
entrustedActorsRevision => $entrustedActorsRevision, |
378
|
|
|
|
|
|
|
entrustedActors => $entrustedActors, |
379
|
|
|
|
|
|
|
containsCache => $containCache, |
380
|
|
|
|
|
|
|
}; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub members { |
384
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
385
|
0
|
|
|
|
|
0
|
@{$o->{members}} } |
|
0
|
|
|
|
|
0
|
|
386
|
0
|
|
|
0
|
|
0
|
sub entrustedActorsRevision { shift->{entrustedActorsRevision} } |
387
|
|
|
|
|
|
|
sub entrustedActors { |
388
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
389
|
0
|
|
|
|
|
0
|
@{$o->{entrustedActors}} } |
|
0
|
|
|
|
|
0
|
|
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# Checks whether the actor group contains at least one active member. |
392
|
|
|
|
|
|
|
sub isActive { |
393
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
394
|
|
|
|
|
|
|
|
395
|
0
|
|
|
|
|
0
|
for my $member (@{$o->{members}}) { |
|
0
|
|
|
|
|
0
|
|
396
|
0
|
0
|
|
|
|
0
|
return 1 if $member->isActive; |
397
|
|
|
|
|
|
|
} |
398
|
0
|
|
|
|
|
0
|
return; |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# Returns the most recent active member, the most recent idle member, or undef if the group is empty. |
402
|
|
|
|
|
|
|
sub leader { |
403
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
404
|
|
|
|
|
|
|
|
405
|
0
|
|
|
|
|
0
|
for my $member (@{$o->{members}}) { |
|
0
|
|
|
|
|
0
|
|
406
|
0
|
0
|
|
|
|
0
|
return $member if $member->isActive; |
407
|
|
|
|
|
|
|
} |
408
|
0
|
|
|
|
|
0
|
return $o->{members}->[0]; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# Returns true if the account belongs to this actor group. |
412
|
|
|
|
|
|
|
# Note that multiple (different) actor groups may claim that the account belongs to them. In practice, an account usually belongs to one actor group. |
413
|
|
|
|
|
|
|
sub contains { |
414
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
415
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
416
|
|
|
|
|
|
|
|
417
|
0
|
|
|
|
|
0
|
return exists $o->{containsCache}->{$actorHash->bytes}; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
# Returns true if the account is entrusted by this actor group. |
421
|
|
|
|
|
|
|
sub entrusts { |
422
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
423
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
424
|
|
|
|
|
|
|
|
425
|
0
|
|
|
|
|
0
|
for my $actor (@{$o->{entrustedActors}}) { |
|
0
|
|
|
|
|
0
|
|
426
|
0
|
0
|
|
|
|
0
|
return 1 if $actorHash->equals($actor->publicKey->hash); |
427
|
|
|
|
|
|
|
} |
428
|
0
|
|
|
|
|
0
|
return; |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
# Returns all public keys. |
432
|
|
|
|
|
|
|
sub publicKeys { |
433
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
434
|
|
|
|
|
|
|
|
435
|
0
|
|
|
|
|
0
|
my @publicKeys; |
436
|
0
|
|
|
|
|
0
|
for my $member (@{$o->{members}}) { |
|
0
|
|
|
|
|
0
|
|
437
|
0
|
|
|
|
|
0
|
push @publicKeys, $member->actorOnStore->publicKey; |
438
|
|
|
|
|
|
|
} |
439
|
0
|
|
|
|
|
0
|
for my $actor (@{$o->{entrustedActors}}) { |
|
0
|
|
|
|
|
0
|
|
440
|
0
|
|
|
|
|
0
|
push @publicKeys, $actor->actorOnStore->publicKey; |
441
|
|
|
|
|
|
|
} |
442
|
0
|
|
|
|
|
0
|
return @publicKeys; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# Returns an ActorGroupBuilder with all members and entrusted keys of this ActorGroup. |
446
|
|
|
|
|
|
|
sub toBuilder { |
447
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
448
|
|
|
|
|
|
|
|
449
|
0
|
|
|
|
|
0
|
my $builder = CDS::ActorGroupBuilder->new; |
450
|
0
|
|
|
|
|
0
|
$builder->mergeEntrustedActors($o->{entrustedActorsRevision}); |
451
|
0
|
|
|
|
|
0
|
for my $member (@{$o->{members}}) { |
|
0
|
|
|
|
|
0
|
|
452
|
0
|
|
|
|
|
0
|
my $publicKey = $member->actorOnStore->publicKey; |
453
|
0
|
|
|
|
|
0
|
$builder->addKnownPublicKey($publicKey); |
454
|
0
|
0
|
|
|
|
0
|
$builder->addMember($publicKey->hash, $member->storeUrl, $member->revision, $member->isActive ? 'active' : 'idle'); |
455
|
|
|
|
|
|
|
} |
456
|
0
|
|
|
|
|
0
|
for my $actor (@{$o->{entrustedActors}}) { |
|
0
|
|
|
|
|
0
|
|
457
|
0
|
|
|
|
|
0
|
my $publicKey = $actor->actorOnStore->publicKey; |
458
|
0
|
|
|
|
|
0
|
$builder->addKnownPublicKey($publicKey); |
459
|
0
|
|
|
|
|
0
|
$builder->addEntrustedActor($publicKey->hash, $actor->storeUrl); |
460
|
|
|
|
|
|
|
} |
461
|
0
|
|
|
|
|
0
|
return $builder; |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
package CDS::ActorGroup::EntrustedActor; |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
sub new { |
467
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
468
|
0
|
0
|
0
|
|
|
0
|
my $actorOnStore = shift; die 'wrong type '.ref($actorOnStore).' for $actorOnStore' if defined $actorOnStore && ref $actorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
0
|
|
469
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
470
|
|
|
|
|
|
|
|
471
|
0
|
|
|
|
|
0
|
return bless { |
472
|
|
|
|
|
|
|
actorOnStore => $actorOnStore, |
473
|
|
|
|
|
|
|
storeUrl => $storeUrl, |
474
|
|
|
|
|
|
|
}; |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
0
|
|
|
0
|
|
0
|
sub actorOnStore { shift->{actorOnStore} } |
478
|
0
|
|
|
0
|
|
0
|
sub storeUrl { shift->{storeUrl} } |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
package CDS::ActorGroup::Member; |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
sub new { |
483
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
484
|
0
|
0
|
0
|
|
|
0
|
my $actorOnStore = shift; die 'wrong type '.ref($actorOnStore).' for $actorOnStore' if defined $actorOnStore && ref $actorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
0
|
|
485
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
486
|
0
|
|
|
|
|
0
|
my $revision = shift; |
487
|
0
|
|
|
|
|
0
|
my $isActive = shift; |
488
|
|
|
|
|
|
|
|
489
|
0
|
|
|
|
|
0
|
return bless { |
490
|
|
|
|
|
|
|
actorOnStore => $actorOnStore, |
491
|
|
|
|
|
|
|
storeUrl => $storeUrl, |
492
|
|
|
|
|
|
|
revision => $revision, |
493
|
|
|
|
|
|
|
isActive => $isActive, |
494
|
|
|
|
|
|
|
}; |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
|
497
|
0
|
|
|
0
|
|
0
|
sub actorOnStore { shift->{actorOnStore} } |
498
|
0
|
|
|
0
|
|
0
|
sub storeUrl { shift->{storeUrl} } |
499
|
0
|
|
|
0
|
|
0
|
sub revision { shift->{revision} } |
500
|
0
|
|
|
0
|
|
0
|
sub isActive { shift->{isActive} } |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
package CDS::ActorGroupBuilder; |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
sub new { |
505
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
506
|
|
|
|
|
|
|
|
507
|
0
|
|
|
|
|
0
|
return bless { |
508
|
|
|
|
|
|
|
knownPublicKeys => {}, # A hashref of known public keys (e.g. from the existing actor group) |
509
|
|
|
|
|
|
|
members => {}, # Members by URL |
510
|
|
|
|
|
|
|
entrustedActorsRevision => 0, # Revision of the list of entrusted actors |
511
|
|
|
|
|
|
|
entrustedActors => {}, # Entrusted actors by hash |
512
|
|
|
|
|
|
|
}; |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
sub members { |
516
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
517
|
0
|
|
|
|
|
0
|
values %{$o->{members}} } |
|
0
|
|
|
|
|
0
|
|
518
|
0
|
|
|
0
|
|
0
|
sub entrustedActorsRevision { shift->{entrustedActorsRevision} } |
519
|
|
|
|
|
|
|
sub entrustedActors { |
520
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
521
|
0
|
|
|
|
|
0
|
values %{$o->{entrustedActors}} } |
|
0
|
|
|
|
|
0
|
|
522
|
0
|
|
|
0
|
|
0
|
sub knownPublicKeys { shift->{knownPublicKeys} } |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
sub addKnownPublicKey { |
525
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
526
|
0
|
0
|
0
|
|
|
0
|
my $publicKey = shift; die 'wrong type '.ref($publicKey).' for $publicKey' if defined $publicKey && ref $publicKey ne 'CDS::PublicKey'; |
|
0
|
|
|
|
|
0
|
|
527
|
|
|
|
|
|
|
|
528
|
0
|
|
|
|
|
0
|
$o->{publicKeys}->{$publicKey->hash->bytes} = $publicKey; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
sub addMember { |
532
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
533
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
534
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
535
|
0
|
|
0
|
|
|
0
|
my $revision = shift // 0; |
536
|
0
|
|
0
|
|
|
0
|
my $status = shift // 'active'; |
537
|
|
|
|
|
|
|
|
538
|
0
|
|
|
|
|
0
|
my $url = $storeUrl.'/accounts/'.$hash->hex; |
539
|
0
|
|
|
|
|
0
|
my $member = $o->{members}->{$url}; |
540
|
0
|
0
|
0
|
|
|
0
|
return if $member && $revision <= $member->revision; |
541
|
0
|
|
|
|
|
0
|
$o->{members}->{$url} = CDS::ActorGroupBuilder::Member->new($hash, $storeUrl, $revision, $status); |
542
|
0
|
|
|
|
|
0
|
return 1; |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
sub removeMember { |
546
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
547
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
548
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
549
|
|
|
|
|
|
|
|
550
|
0
|
|
|
|
|
0
|
my $url = $storeUrl.'/accounts/'.$hash->hex; |
551
|
0
|
|
|
|
|
0
|
delete $o->{members}->{$url}; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
sub parseMembers { |
555
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
556
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
557
|
0
|
|
|
|
|
0
|
my $linkedPublicKeys = shift; |
558
|
|
|
|
|
|
|
|
559
|
0
|
0
|
|
|
|
0
|
die 'linked public keys?' if ! defined $linkedPublicKeys; |
560
|
0
|
|
|
|
|
0
|
for my $storeRecord ($record->children) { |
561
|
0
|
|
|
|
|
0
|
my $accountStoreUrl = $storeRecord->asText; |
562
|
|
|
|
|
|
|
|
563
|
0
|
|
|
|
|
0
|
for my $statusRecord ($storeRecord->children) { |
564
|
0
|
|
|
|
|
0
|
my $status = $statusRecord->bytes; |
565
|
|
|
|
|
|
|
|
566
|
0
|
|
|
|
|
0
|
for my $child ($statusRecord->children) { |
567
|
0
|
0
|
|
|
|
0
|
my $hash = $linkedPublicKeys ? $child->hash : CDS::Hash->fromBytes($child->bytes); |
568
|
0
|
|
0
|
|
|
0
|
$o->addMember($hash // next, $accountStoreUrl, $child->integerValue, $status); |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
sub mergeEntrustedActors { |
575
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
576
|
0
|
|
|
|
|
0
|
my $revision = shift; |
577
|
|
|
|
|
|
|
|
578
|
0
|
0
|
|
|
|
0
|
return if $revision <= $o->{entrustedActorsRevision}; |
579
|
0
|
|
|
|
|
0
|
$o->{entrustedActorsRevision} = $revision; |
580
|
0
|
|
|
|
|
0
|
$o->{entrustedActors} = {}; |
581
|
0
|
|
|
|
|
0
|
return 1; |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
sub addEntrustedActor { |
585
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
586
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
587
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
588
|
|
|
|
|
|
|
|
589
|
0
|
|
|
|
|
0
|
my $actor = CDS::ActorGroupBuilder::EntrustedActor->new($hash, $storeUrl); |
590
|
0
|
|
|
|
|
0
|
$o->{entrustedActors}->{$hash->bytes} = $actor; |
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
sub removeEntrustedActor { |
594
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
595
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
596
|
|
|
|
|
|
|
|
597
|
0
|
|
|
|
|
0
|
delete $o->{entrustedActors}->{$hash->bytes}; |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
sub parseEntrustedActors { |
601
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
602
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
603
|
0
|
|
|
|
|
0
|
my $linkedPublicKeys = shift; |
604
|
|
|
|
|
|
|
|
605
|
0
|
|
|
|
|
0
|
for my $revisionRecord ($record->children) { |
606
|
0
|
0
|
|
|
|
0
|
next if ! $o->mergeEntrustedActors($revisionRecord->asInteger); |
607
|
0
|
|
|
|
|
0
|
$o->parseEntrustedActorList($revisionRecord, $linkedPublicKeys); |
608
|
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
} |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
sub parseEntrustedActorList { |
612
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
613
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
614
|
0
|
|
|
|
|
0
|
my $linkedPublicKeys = shift; |
615
|
|
|
|
|
|
|
|
616
|
0
|
0
|
|
|
|
0
|
die 'linked public keys?' if ! defined $linkedPublicKeys; |
617
|
0
|
|
|
|
|
0
|
for my $storeRecord ($record->children) { |
618
|
0
|
|
|
|
|
0
|
my $storeUrl = $storeRecord->asText; |
619
|
|
|
|
|
|
|
|
620
|
0
|
|
|
|
|
0
|
for my $child ($storeRecord->children) { |
621
|
0
|
0
|
|
|
|
0
|
my $hash = $linkedPublicKeys ? $child->hash : CDS::Hash->fromBytes($child->bytes); |
622
|
0
|
|
0
|
|
|
0
|
$o->addEntrustedActor($hash // next, $storeUrl); |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
sub parse { |
628
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
629
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
630
|
0
|
|
|
|
|
0
|
my $linkedPublicKeys = shift; |
631
|
|
|
|
|
|
|
|
632
|
0
|
|
|
|
|
0
|
$o->parseMembers($record->child('actor group'), $linkedPublicKeys); |
633
|
0
|
|
|
|
|
0
|
$o->parseEntrustedActors($record->child('entrusted actors'), $linkedPublicKeys); |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
sub load { |
637
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
638
|
0
|
|
|
|
|
0
|
my $store = shift; |
639
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
640
|
0
|
|
|
|
|
0
|
my $delegate = shift; |
641
|
|
|
|
|
|
|
|
642
|
0
|
|
|
|
|
0
|
return CDS::LoadActorGroup->load($o, $store, $keyPair, $delegate); |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
sub discover { |
646
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
647
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
648
|
0
|
|
|
|
|
0
|
my $delegate = shift; |
649
|
|
|
|
|
|
|
|
650
|
0
|
|
|
|
|
0
|
return CDS::DiscoverActorGroup->discover($o, $keyPair, $delegate); |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
# Serializes the actor group to a record that can be passed to parse. |
654
|
|
|
|
|
|
|
sub addToRecord { |
655
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
656
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
657
|
0
|
|
|
|
|
0
|
my $linkedPublicKeys = shift; |
658
|
|
|
|
|
|
|
|
659
|
0
|
0
|
|
|
|
0
|
die 'linked public keys?' if ! defined $linkedPublicKeys; |
660
|
|
|
|
|
|
|
|
661
|
0
|
|
|
|
|
0
|
my $actorGroupRecord = $record->add('actor group'); |
662
|
0
|
|
|
|
|
0
|
my $currentStoreUrl = undef; |
663
|
0
|
|
|
|
|
0
|
my $currentStoreRecord = undef; |
664
|
0
|
|
|
|
|
0
|
my $currentStatus = undef; |
665
|
0
|
|
|
|
|
0
|
my $currentStatusRecord = undef; |
666
|
0
|
0
|
|
|
|
0
|
for my $member (sort { $a->storeUrl cmp $b->storeUrl || CDS->booleanCompare($b->status, $a->status) } $o->members) { |
|
0
|
|
|
|
|
0
|
|
667
|
0
|
0
|
|
|
|
0
|
next if ! $member->revision; |
668
|
|
|
|
|
|
|
|
669
|
0
|
0
|
0
|
|
|
0
|
if (! defined $currentStoreUrl || $currentStoreUrl ne $member->storeUrl) { |
670
|
0
|
|
|
|
|
0
|
$currentStoreUrl = $member->storeUrl; |
671
|
0
|
|
|
|
|
0
|
$currentStoreRecord = $actorGroupRecord->addText($currentStoreUrl); |
672
|
0
|
|
|
|
|
0
|
$currentStatus = undef; |
673
|
0
|
|
|
|
|
0
|
$currentStatusRecord = undef; |
674
|
|
|
|
|
|
|
} |
675
|
|
|
|
|
|
|
|
676
|
0
|
0
|
0
|
|
|
0
|
if (! defined $currentStatus || $currentStatus ne $member->status) { |
677
|
0
|
|
|
|
|
0
|
$currentStatus = $member->status; |
678
|
0
|
|
|
|
|
0
|
$currentStatusRecord = $currentStoreRecord->add($currentStatus); |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
|
681
|
0
|
0
|
|
|
|
0
|
my $hashRecord = $linkedPublicKeys ? $currentStatusRecord->addHash($member->hash) : $currentStatusRecord->add($member->hash->bytes); |
682
|
0
|
|
|
|
|
0
|
$hashRecord->addInteger($member->revision); |
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
|
685
|
0
|
0
|
|
|
|
0
|
if ($o->{entrustedActorsRevision}) { |
686
|
0
|
|
|
|
|
0
|
my $listRecord = $o->entrustedActorListToRecord($linkedPublicKeys); |
687
|
0
|
|
|
|
|
0
|
$record->add('entrusted actors')->addInteger($o->{entrustedActorsRevision})->addRecord($listRecord->children); |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
} |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
sub toRecord { |
692
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
693
|
0
|
|
|
|
|
0
|
my $linkedPublicKeys = shift; |
694
|
|
|
|
|
|
|
|
695
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->new; |
696
|
0
|
|
|
|
|
0
|
$o->addToRecord($record, $linkedPublicKeys); |
697
|
0
|
|
|
|
|
0
|
return $record; |
698
|
|
|
|
|
|
|
} |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
sub entrustedActorListToRecord { |
701
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
702
|
0
|
|
|
|
|
0
|
my $linkedPublicKeys = shift; |
703
|
|
|
|
|
|
|
|
704
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->new; |
705
|
0
|
|
|
|
|
0
|
my $currentStoreUrl = undef; |
706
|
0
|
|
|
|
|
0
|
my $currentStoreRecord = undef; |
707
|
0
|
|
|
|
|
0
|
for my $actor ($o->entrustedActors) { |
708
|
0
|
0
|
0
|
|
|
0
|
if (! defined $currentStoreUrl || $currentStoreUrl ne $actor->storeUrl) { |
709
|
0
|
|
|
|
|
0
|
$currentStoreUrl = $actor->storeUrl; |
710
|
0
|
|
|
|
|
0
|
$currentStoreRecord = $record->addText($currentStoreUrl); |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
0
|
0
|
|
|
|
0
|
$linkedPublicKeys ? $currentStoreRecord->addHash($actor->hash) : $currentStoreRecord->add($actor->hash->bytes); |
714
|
|
|
|
|
|
|
} |
715
|
|
|
|
|
|
|
|
716
|
0
|
|
|
|
|
0
|
return $record; |
717
|
|
|
|
|
|
|
} |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
package CDS::ActorGroupBuilder::EntrustedActor; |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
sub new { |
722
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
723
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
724
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
725
|
|
|
|
|
|
|
|
726
|
0
|
|
|
|
|
0
|
return bless { |
727
|
|
|
|
|
|
|
hash => $hash, |
728
|
|
|
|
|
|
|
storeUrl => $storeUrl, |
729
|
|
|
|
|
|
|
}; |
730
|
|
|
|
|
|
|
} |
731
|
|
|
|
|
|
|
|
732
|
0
|
|
|
0
|
|
0
|
sub hash { shift->{hash} } |
733
|
0
|
|
|
0
|
|
0
|
sub storeUrl { shift->{storeUrl} } |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
package CDS::ActorGroupBuilder::Member; |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
sub new { |
738
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
739
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
740
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
741
|
0
|
|
|
|
|
0
|
my $revision = shift; |
742
|
0
|
|
|
|
|
0
|
my $status = shift; |
743
|
|
|
|
|
|
|
|
744
|
0
|
|
|
|
|
0
|
return bless { |
745
|
|
|
|
|
|
|
hash => $hash, |
746
|
|
|
|
|
|
|
storeUrl => $storeUrl, |
747
|
|
|
|
|
|
|
revision => $revision, |
748
|
|
|
|
|
|
|
status => $status, |
749
|
|
|
|
|
|
|
}; |
750
|
|
|
|
|
|
|
} |
751
|
|
|
|
|
|
|
|
752
|
0
|
|
|
0
|
|
0
|
sub hash { shift->{hash} } |
753
|
0
|
|
|
0
|
|
0
|
sub storeUrl { shift->{storeUrl} } |
754
|
0
|
|
|
0
|
|
0
|
sub revision { shift->{revision} } |
755
|
0
|
|
|
0
|
|
0
|
sub status { shift->{status} } |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
# The result of parsing an ACTORGROUP token (see Token.pm). |
758
|
|
|
|
|
|
|
package CDS::ActorGroupToken; |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
sub new { |
761
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
762
|
0
|
|
|
|
|
0
|
my $label = shift; |
763
|
0
|
0
|
0
|
|
|
0
|
my $actorGroup = shift; die 'wrong type '.ref($actorGroup).' for $actorGroup' if defined $actorGroup && ref $actorGroup ne 'CDS::ActorGroup'; |
|
0
|
|
|
|
|
0
|
|
764
|
|
|
|
|
|
|
|
765
|
0
|
|
|
|
|
0
|
return bless { |
766
|
|
|
|
|
|
|
label => $label, |
767
|
|
|
|
|
|
|
actorGroup => $actorGroup, |
768
|
|
|
|
|
|
|
}; |
769
|
|
|
|
|
|
|
} |
770
|
|
|
|
|
|
|
|
771
|
0
|
|
|
0
|
|
0
|
sub label { shift->{label} } |
772
|
0
|
|
|
0
|
|
0
|
sub actorGroup { shift->{actorGroup} } |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
# A public key and a store. |
775
|
|
|
|
|
|
|
package CDS::ActorOnStore; |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
sub new { |
778
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
779
|
0
|
0
|
0
|
|
|
0
|
my $publicKey = shift; die 'wrong type '.ref($publicKey).' for $publicKey' if defined $publicKey && ref $publicKey ne 'CDS::PublicKey'; |
|
0
|
|
|
|
|
0
|
|
780
|
0
|
|
|
|
|
0
|
my $store = shift; |
781
|
|
|
|
|
|
|
|
782
|
0
|
|
|
|
|
0
|
return bless { |
783
|
|
|
|
|
|
|
publicKey => $publicKey, |
784
|
|
|
|
|
|
|
store => $store |
785
|
|
|
|
|
|
|
}; |
786
|
|
|
|
|
|
|
} |
787
|
|
|
|
|
|
|
|
788
|
0
|
|
|
0
|
|
0
|
sub publicKey { shift->{publicKey} } |
789
|
0
|
|
|
0
|
|
0
|
sub store { shift->{store} } |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
sub equals { |
792
|
0
|
|
|
0
|
|
0
|
my $this = shift; |
793
|
0
|
|
|
|
|
0
|
my $that = shift; |
794
|
|
|
|
|
|
|
|
795
|
0
|
0
|
0
|
|
|
0
|
return 1 if ! defined $this && ! defined $that; |
796
|
0
|
0
|
0
|
|
|
0
|
return if ! defined $this || ! defined $that; |
797
|
0
|
|
0
|
|
|
0
|
return $this->{store}->id eq $that->{store}->id && $this->{publicKey}->{hash}->equals($that->{publicKey}->{hash}); |
798
|
|
|
|
|
|
|
} |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
package CDS::ActorWithDocument; |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
sub new { |
803
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
804
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
805
|
0
|
|
|
|
|
0
|
my $storageStore = shift; |
806
|
0
|
|
|
|
|
0
|
my $messagingStore = shift; |
807
|
0
|
|
|
|
|
0
|
my $messagingStoreUrl = shift; |
808
|
0
|
|
|
|
|
0
|
my $publicKeyCache = shift; |
809
|
|
|
|
|
|
|
|
810
|
0
|
|
|
|
|
0
|
my $o = bless { |
811
|
|
|
|
|
|
|
keyPair => $keyPair, |
812
|
|
|
|
|
|
|
storageStore => $storageStore, |
813
|
|
|
|
|
|
|
messagingStore => $messagingStore, |
814
|
|
|
|
|
|
|
messagingStoreUrl => $messagingStoreUrl, |
815
|
|
|
|
|
|
|
groupDataHandlers => [], |
816
|
|
|
|
|
|
|
}, $class; |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
# Private data on the storage store |
819
|
0
|
|
|
|
|
0
|
$o->{storagePrivateRoot} = CDS::PrivateRoot->new($keyPair, $storageStore, $o); |
820
|
0
|
|
|
|
|
0
|
$o->{groupDocument} = CDS::RootDocument->new($o->{storagePrivateRoot}, 'group data'); |
821
|
0
|
|
|
|
|
0
|
$o->{localDocument} = CDS::RootDocument->new($o->{storagePrivateRoot}, 'local data'); |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
# Private data on the messaging store |
824
|
0
|
0
|
|
|
|
0
|
$o->{messagingPrivateRoot} = $storageStore->id eq $messagingStore->id ? $o->{storagePrivateRoot} : CDS::PrivateRoot->new($keyPair, $messagingStore, $o); |
825
|
0
|
|
|
|
|
0
|
$o->{sentList} = CDS::SentList->new($o->{messagingPrivateRoot}); |
826
|
0
|
|
|
|
|
0
|
$o->{sentListReady} = 0; |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
# Group data sharing |
829
|
0
|
|
|
|
|
0
|
$o->{groupDataSharer} = CDS::GroupDataSharer->new($o); |
830
|
0
|
|
|
|
|
0
|
$o->{groupDataSharer}->addDataHandler($o->{groupDocument}->label, $o->{groupDocument}); |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
# Selectors |
833
|
0
|
|
|
|
|
0
|
$o->{groupRoot} = $o->{groupDocument}->root; |
834
|
0
|
|
|
|
|
0
|
$o->{localRoot} = $o->{localDocument}->root; |
835
|
0
|
|
|
|
|
0
|
$o->{publicDataSelector} = $o->{groupRoot}->child('public data'); |
836
|
0
|
|
|
|
|
0
|
$o->{actorGroupSelector} = $o->{groupRoot}->child('actor group'); |
837
|
0
|
|
|
|
|
0
|
$o->{actorSelector} = $o->{actorGroupSelector}->child(substr($keyPair->publicKey->hash->bytes, 0, 16)); |
838
|
0
|
|
|
|
|
0
|
$o->{entrustedActorsSelector} = $o->{groupRoot}->child('entrusted actors'); |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
# Message reader |
841
|
0
|
|
|
|
|
0
|
my $pool = CDS::MessageBoxReaderPool->new($keyPair, $publicKeyCache, $o); |
842
|
0
|
|
|
|
|
0
|
$o->{messageBoxReader} = CDS::MessageBoxReader->new($pool, CDS::ActorOnStore->new($keyPair->publicKey, $messagingStore), CDS->HOUR); |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
# Active actor group members and entrusted keys |
845
|
0
|
|
|
|
|
0
|
$o->{cachedGroupDataMembers} = {}; |
846
|
0
|
|
|
|
|
0
|
$o->{cachedEntrustedKeys} = {}; |
847
|
0
|
|
|
|
|
0
|
return $o; |
848
|
|
|
|
|
|
|
} |
849
|
|
|
|
|
|
|
|
850
|
0
|
|
|
0
|
|
0
|
sub keyPair { shift->{keyPair} } |
851
|
0
|
|
|
0
|
|
0
|
sub storageStore { shift->{storageStore} } |
852
|
0
|
|
|
0
|
|
0
|
sub messagingStore { shift->{messagingStore} } |
853
|
0
|
|
|
0
|
|
0
|
sub messagingStoreUrl { shift->{messagingStoreUrl} } |
854
|
|
|
|
|
|
|
|
855
|
0
|
|
|
0
|
|
0
|
sub storagePrivateRoot { shift->{storagePrivateRoot} } |
856
|
0
|
|
|
0
|
|
0
|
sub groupDocument { shift->{groupDocument} } |
857
|
0
|
|
|
0
|
|
0
|
sub localDocument { shift->{localDocument} } |
858
|
|
|
|
|
|
|
|
859
|
0
|
|
|
0
|
|
0
|
sub messagingPrivateRoot { shift->{messagingPrivateRoot} } |
860
|
0
|
|
|
0
|
|
0
|
sub sentList { shift->{sentList} } |
861
|
0
|
|
|
0
|
|
0
|
sub sentListReady { shift->{sentListReady} } |
862
|
|
|
|
|
|
|
|
863
|
0
|
|
|
0
|
|
0
|
sub groupDataSharer { shift->{groupDataSharer} } |
864
|
|
|
|
|
|
|
|
865
|
0
|
|
|
0
|
|
0
|
sub groupRoot { shift->{groupRoot} } |
866
|
0
|
|
|
0
|
|
0
|
sub localRoot { shift->{localRoot} } |
867
|
0
|
|
|
0
|
|
0
|
sub publicDataSelector { shift->{publicDataSelector} } |
868
|
0
|
|
|
0
|
|
0
|
sub actorGroupSelector { shift->{actorGroupSelector} } |
869
|
0
|
|
|
0
|
|
0
|
sub actorSelector { shift->{actorSelector} } |
870
|
0
|
|
|
0
|
|
0
|
sub entrustedActorsSelector { shift->{entrustedActorsSelector} } |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
### Our own actor ### |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
sub isMe { |
875
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
876
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
877
|
|
|
|
|
|
|
|
878
|
0
|
|
|
|
|
0
|
return $o->{keyPair}->publicKey->hash->equals($actorHash); |
879
|
|
|
|
|
|
|
} |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
sub setName { |
882
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
883
|
0
|
|
|
|
|
0
|
my $name = shift; |
884
|
|
|
|
|
|
|
|
885
|
0
|
|
|
|
|
0
|
$o->{actorSelector}->child('name')->set($name); |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
sub getName { |
889
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
890
|
|
|
|
|
|
|
|
891
|
0
|
|
|
|
|
0
|
return $o->{actorSelector}->child('name')->textValue; |
892
|
|
|
|
|
|
|
} |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
sub updateMyRegistration { |
895
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
896
|
|
|
|
|
|
|
|
897
|
0
|
|
|
|
|
0
|
$o->{actorSelector}->addObject($o->{keyPair}->publicKey->hash, $o->{keyPair}->publicKey->object); |
898
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->new; |
899
|
0
|
|
|
|
|
0
|
$record->add('hash')->addHash($o->{keyPair}->publicKey->hash); |
900
|
0
|
|
|
|
|
0
|
$record->add('store')->addText($o->{messagingStoreUrl}); |
901
|
0
|
|
|
|
|
0
|
$o->{actorSelector}->set($record); |
902
|
|
|
|
|
|
|
} |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
sub setMyActiveFlag { |
905
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
906
|
0
|
|
|
|
|
0
|
my $flag = shift; |
907
|
|
|
|
|
|
|
|
908
|
0
|
|
|
|
|
0
|
$o->{actorSelector}->child('active')->setBoolean($flag); |
909
|
|
|
|
|
|
|
} |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
sub setMyGroupDataFlag { |
912
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
913
|
0
|
|
|
|
|
0
|
my $flag = shift; |
914
|
|
|
|
|
|
|
|
915
|
0
|
|
|
|
|
0
|
$o->{actorSelector}->child('group data')->setBoolean($flag); |
916
|
|
|
|
|
|
|
} |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
### Actor group |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
sub isGroupMember { |
921
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
922
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
923
|
|
|
|
|
|
|
|
924
|
0
|
0
|
|
|
|
0
|
return 1 if $actorHash->equals($o->{keyPair}->publicKey->hash); |
925
|
0
|
|
0
|
|
|
0
|
my $memberSelector = $o->findMember($actorHash) // return; |
926
|
0
|
|
|
|
|
0
|
return ! $memberSelector->child('revoked')->isSet; |
927
|
|
|
|
|
|
|
} |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
sub findMember { |
930
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
931
|
0
|
0
|
0
|
|
|
0
|
my $memberHash = shift; die 'wrong type '.ref($memberHash).' for $memberHash' if defined $memberHash && ref $memberHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
932
|
|
|
|
|
|
|
|
933
|
0
|
|
|
|
|
0
|
for my $child ($o->{actorGroupSelector}->children) { |
934
|
0
|
|
|
|
|
0
|
my $record = $child->record; |
935
|
0
|
|
0
|
|
|
0
|
my $hash = $record->child('hash')->hashValue // next; |
936
|
0
|
0
|
|
|
|
0
|
next if ! $hash->equals($memberHash); |
937
|
0
|
|
|
|
|
0
|
return $child; |
938
|
|
|
|
|
|
|
} |
939
|
|
|
|
|
|
|
|
940
|
0
|
|
|
|
|
0
|
return; |
941
|
|
|
|
|
|
|
} |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
sub forgetOldIdleActors { |
944
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
945
|
0
|
|
|
|
|
0
|
my $limit = shift; |
946
|
|
|
|
|
|
|
|
947
|
0
|
|
|
|
|
0
|
for my $child ($o->{actorGroupSelector}->children) { |
948
|
0
|
0
|
|
|
|
0
|
next if $child->child('active')->booleanValue; |
949
|
0
|
0
|
|
|
|
0
|
next if $child->child('group data')->booleanValue; |
950
|
0
|
0
|
|
|
|
0
|
next if $child->revision > $limit; |
951
|
0
|
|
|
|
|
0
|
$child->forgetBranch; |
952
|
|
|
|
|
|
|
} |
953
|
|
|
|
|
|
|
} |
954
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
### Group data members |
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
sub getGroupDataMembers { |
958
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
# Update the cached list |
961
|
0
|
|
|
|
|
0
|
for my $child ($o->{actorGroupSelector}->children) { |
962
|
0
|
|
|
|
|
0
|
my $record = $child->record; |
963
|
0
|
|
|
|
|
0
|
my $hash = $record->child('hash')->hashValue; |
964
|
0
|
0
|
|
|
|
0
|
$hash = undef if $hash->equals($o->{keyPair}->publicKey->hash); |
965
|
0
|
0
|
|
|
|
0
|
$hash = undef if $child->child('revoked')->isSet; |
966
|
0
|
0
|
|
|
|
0
|
$hash = undef if ! $child->child('group data')->isSet; |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
# Remove |
969
|
0
|
0
|
|
|
|
0
|
if (! $hash) { |
970
|
0
|
|
|
|
|
0
|
delete $o->{cachedGroupDataMembers}->{$child->label}; |
971
|
0
|
|
|
|
|
0
|
next; |
972
|
|
|
|
|
|
|
} |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
# Keep |
975
|
0
|
|
|
|
|
0
|
my $member = $o->{cachedGroupDataMembers}->{$child->label}; |
976
|
0
|
|
|
|
|
0
|
my $storeUrl = $record->child('store')->textValue; |
977
|
0
|
0
|
0
|
|
|
0
|
next if $member && $member->storeUrl eq $storeUrl && $member->actorOnStore->publicKey->hash->equals($hash); |
|
|
|
0
|
|
|
|
|
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
# Verify the store |
980
|
0
|
|
|
|
|
0
|
my $store = $o->onVerifyMemberStore($storeUrl, $child); |
981
|
0
|
0
|
|
|
|
0
|
if (! $store) { |
982
|
0
|
|
|
|
|
0
|
delete $o->{cachedGroupDataMembers}->{$child->label}; |
983
|
0
|
|
|
|
|
0
|
next; |
984
|
|
|
|
|
|
|
} |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
# Reuse the public key and add |
987
|
0
|
0
|
0
|
|
|
0
|
if ($member && $member->actorOnStore->publicKey->hash->equals($hash)) { |
988
|
0
|
|
|
|
|
0
|
my $actorOnStore = CDS::ActorOnStore->new($member->actorOnStore->publicKey, $store); |
989
|
0
|
|
|
|
|
0
|
$o->{cachedEntrustedKeys}->{$child->label} = {storeUrl => $storeUrl, actorOnStore => $actorOnStore}; |
990
|
|
|
|
|
|
|
} |
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
# Get the public key and add |
993
|
0
|
|
|
|
|
0
|
my ($publicKey, $invalidReason, $storeError) = $o->{keyPair}->getPublicKey($hash, $o->{groupDocument}->unsaved); |
994
|
0
|
0
|
|
|
|
0
|
return if defined $storeError; |
995
|
0
|
0
|
|
|
|
0
|
if (defined $invalidReason) { |
996
|
0
|
|
|
|
|
0
|
delete $o->{cachedGroupDataMembers}->{$child->label}; |
997
|
0
|
|
|
|
|
0
|
next; |
998
|
|
|
|
|
|
|
} |
999
|
|
|
|
|
|
|
|
1000
|
0
|
|
|
|
|
0
|
my $actorOnStore = CDS::ActorOnStore->new($publicKey, $store); |
1001
|
0
|
|
|
|
|
0
|
$o->{cachedGroupDataMembers}->{$child->label} = {storeUrl => $storeUrl, actorOnStore => $actorOnStore}; |
1002
|
|
|
|
|
|
|
} |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
# Return the current list |
1005
|
0
|
|
|
|
|
0
|
return [map { $_->{actorOnStore} } values %{$o->{cachedGroupDataMembers}}]; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1006
|
|
|
|
|
|
|
} |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
### Entrusted actors |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
sub entrust { |
1011
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1012
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
1013
|
0
|
0
|
0
|
|
|
0
|
my $publicKey = shift; die 'wrong type '.ref($publicKey).' for $publicKey' if defined $publicKey && ref $publicKey ne 'CDS::PublicKey'; |
|
0
|
|
|
|
|
0
|
|
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
# TODO: this is not compatible with the Java implementation (which uses a record with "hash" and "store") |
1016
|
0
|
|
|
|
|
0
|
my $selector = $o->{entrustedActorsSelector}; |
1017
|
0
|
|
|
|
|
0
|
my $builder = CDS::ActorGroupBuilder->new; |
1018
|
0
|
|
|
|
|
0
|
$builder->parseEntrustedActorList($selector->record, 1); |
1019
|
0
|
|
|
|
|
0
|
$builder->removeEntrustedActor($publicKey->hash); |
1020
|
0
|
|
|
|
|
0
|
$builder->addEntrustedActor($storeUrl, $publicKey->hash); |
1021
|
0
|
|
|
|
|
0
|
$selector->addObject($publicKey->hash, $publicKey->object); |
1022
|
0
|
|
|
|
|
0
|
$selector->set($builder->entrustedActorListToRecord(1)); |
1023
|
0
|
|
|
|
|
0
|
$o->{cachedEntrustedKeys}->{$publicKey->hash->bytes} = $publicKey; |
1024
|
|
|
|
|
|
|
} |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
sub doNotEntrust { |
1027
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1028
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1029
|
|
|
|
|
|
|
|
1030
|
0
|
|
|
|
|
0
|
my $selector = $o->{entrustedActorsSelector}; |
1031
|
0
|
|
|
|
|
0
|
my $builder = CDS::ActorGroupBuilder->new; |
1032
|
0
|
|
|
|
|
0
|
$builder->parseEntrustedActorList($selector->record, 1); |
1033
|
0
|
|
|
|
|
0
|
$builder->removeEntrustedActor($hash); |
1034
|
0
|
|
|
|
|
0
|
$selector->set($builder->entrustedActorListToRecord(1)); |
1035
|
0
|
|
|
|
|
0
|
delete $o->{cachedEntrustedKeys}->{$hash->bytes}; |
1036
|
|
|
|
|
|
|
} |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
sub getEntrustedKeys { |
1039
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1040
|
|
|
|
|
|
|
|
1041
|
0
|
|
|
|
|
0
|
my $entrustedKeys = []; |
1042
|
0
|
|
|
|
|
0
|
for my $storeRecord ($o->{entrustedActorsSelector}->record->children) { |
1043
|
0
|
|
|
|
|
0
|
for my $child ($storeRecord->children) { |
1044
|
0
|
|
0
|
|
|
0
|
my $hash = $child->hash // next; |
1045
|
0
|
|
0
|
|
|
0
|
push @$entrustedKeys, $o->getEntrustedKey($hash) // next; |
1046
|
|
|
|
|
|
|
} |
1047
|
|
|
|
|
|
|
} |
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
# We could remove unused keys from $o->{cachedEntrustedKeys} here, but since this is |
1050
|
|
|
|
|
|
|
# such a rare event, and doesn't consume a lot of memory, this would be overkill. |
1051
|
|
|
|
|
|
|
|
1052
|
0
|
|
|
|
|
0
|
return $entrustedKeys; |
1053
|
|
|
|
|
|
|
} |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
sub getEntrustedKey { |
1056
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1057
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1058
|
|
|
|
|
|
|
|
1059
|
0
|
|
|
|
|
0
|
my $entrustedKey = $o->{cachedEntrustedKeys}->{$hash->bytes}; |
1060
|
0
|
0
|
|
|
|
0
|
return $entrustedKey if $entrustedKey; |
1061
|
|
|
|
|
|
|
|
1062
|
0
|
|
|
|
|
0
|
my ($publicKey, $invalidReason, $storeError) = $o->{keyPair}->getPublicKey($hash, $o->{groupDocument}->unsaved); |
1063
|
0
|
0
|
|
|
|
0
|
return if defined $storeError; |
1064
|
0
|
0
|
|
|
|
0
|
return if defined $invalidReason; |
1065
|
0
|
|
|
|
|
0
|
$o->{cachedEntrustedKeys}->{$hash->bytes} = $publicKey; |
1066
|
0
|
|
|
|
|
0
|
return $publicKey; |
1067
|
|
|
|
|
|
|
} |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
### Private data |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
sub procurePrivateData { |
1072
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1073
|
0
|
|
0
|
|
|
0
|
my $interval = shift // CDS->DAY; |
1074
|
|
|
|
|
|
|
|
1075
|
0
|
|
0
|
|
|
0
|
$o->{storagePrivateRoot}->procure($interval) // return; |
1076
|
0
|
|
0
|
|
|
0
|
$o->{groupDocument}->read // return; |
1077
|
0
|
|
0
|
|
|
0
|
$o->{localDocument}->read // return; |
1078
|
0
|
|
|
|
|
0
|
return 1; |
1079
|
|
|
|
|
|
|
} |
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
sub savePrivateDataAndShareGroupData { |
1082
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1083
|
|
|
|
|
|
|
|
1084
|
0
|
|
|
|
|
0
|
$o->{localDocument}->save; |
1085
|
0
|
|
|
|
|
0
|
$o->{groupDocument}->save; |
1086
|
0
|
|
|
|
|
0
|
$o->groupDataSharer->share; |
1087
|
0
|
|
0
|
|
|
0
|
my $entrustedKeys = $o->getEntrustedKeys // return; |
1088
|
0
|
|
|
|
|
0
|
my ($ok, $missingHash) = $o->{storagePrivateRoot}->save($entrustedKeys); |
1089
|
0
|
0
|
|
|
|
0
|
return 1 if $ok; |
1090
|
0
|
0
|
|
|
|
0
|
$o->onMissingObject($missingHash) if $missingHash; |
1091
|
0
|
|
|
|
|
0
|
return; |
1092
|
|
|
|
|
|
|
} |
1093
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
# abstract sub onVerifyMemberStore($storeUrl, $selector) |
1095
|
|
|
|
|
|
|
# abstract sub onPrivateRootReadingInvalidEntry($o, $source, $reason) |
1096
|
|
|
|
|
|
|
# abstract sub onMissingObject($missingHash) |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
### Sending messages |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
sub procureSentList { |
1101
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1102
|
0
|
|
0
|
|
|
0
|
my $interval = shift // CDS->DAY; |
1103
|
|
|
|
|
|
|
|
1104
|
0
|
|
0
|
|
|
0
|
$o->{messagingPrivateRoot}->procure($interval) // return; |
1105
|
0
|
|
0
|
|
|
0
|
$o->{sentList}->read // return; |
1106
|
0
|
|
|
|
|
0
|
$o->{sentListReady} = 1; |
1107
|
0
|
|
|
|
|
0
|
return 1; |
1108
|
|
|
|
|
|
|
} |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
sub openMessageChannel { |
1111
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1112
|
0
|
|
|
|
|
0
|
my $label = shift; |
1113
|
0
|
|
|
|
|
0
|
my $validity = shift; |
1114
|
|
|
|
|
|
|
|
1115
|
0
|
|
|
|
|
0
|
return CDS::MessageChannel->new($o, $label, $validity); |
1116
|
|
|
|
|
|
|
} |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
sub sendMessages { |
1119
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1120
|
|
|
|
|
|
|
|
1121
|
0
|
0
|
|
|
|
0
|
return 1 if ! $o->{sentList}->hasChanges; |
1122
|
0
|
|
|
|
|
0
|
$o->{sentList}->save; |
1123
|
0
|
|
0
|
|
|
0
|
my $entrustedKeys = $o->getEntrustedKeys // return; |
1124
|
0
|
|
|
|
|
0
|
my ($ok, $missingHash) = $o->{messagingPrivateRoot}->save($entrustedKeys); |
1125
|
0
|
0
|
|
|
|
0
|
return 1 if $ok; |
1126
|
0
|
0
|
|
|
|
0
|
$o->onMissingObject($missingHash) if $missingHash; |
1127
|
0
|
|
|
|
|
0
|
return; |
1128
|
|
|
|
|
|
|
} |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
### Receiving messages |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
# abstract sub onMessageBoxVerifyStore($o, $senderStoreUrl, $hash, $envelope, $senderHash) |
1133
|
|
|
|
|
|
|
# abstract sub onMessage($o, $message) |
1134
|
|
|
|
|
|
|
# abstract sub onInvalidMessage($o, $source, $reason) |
1135
|
|
|
|
|
|
|
# abstract sub onMessageBoxEntry($o, $message) |
1136
|
|
|
|
|
|
|
# abstract sub onMessageBoxInvalidEntry($o, $source, $reason) |
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
### Announcing ### |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
sub announceOnAllStores { |
1141
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1142
|
|
|
|
|
|
|
|
1143
|
0
|
|
|
|
|
0
|
$o->announce($o->{storageStore}); |
1144
|
0
|
0
|
|
|
|
0
|
$o->announce($o->{messagingStore}) if $o->{messagingStore}->id ne $o->{storageStore}->id; |
1145
|
|
|
|
|
|
|
} |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
sub announce { |
1148
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1149
|
0
|
|
|
|
|
0
|
my $store = shift; |
1150
|
|
|
|
|
|
|
|
1151
|
0
|
0
|
|
|
|
0
|
die 'probably calling old announce, which should now be announceOnAllStores' if ! defined $store; |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
# Prepare the actor group |
1154
|
0
|
|
|
|
|
0
|
my $builder = CDS::ActorGroupBuilder->new; |
1155
|
|
|
|
|
|
|
|
1156
|
0
|
|
|
|
|
0
|
my $me = $o->keyPair->publicKey->hash; |
1157
|
0
|
|
|
|
|
0
|
$builder->addMember($me, $o->messagingStoreUrl, CDS->now, 'active'); |
1158
|
0
|
|
|
|
|
0
|
for my $child ($o->actorGroupSelector->children) { |
1159
|
0
|
|
|
|
|
0
|
my $record = $child->record; |
1160
|
0
|
|
0
|
|
|
0
|
my $hash = $record->child('hash')->hashValue // next; |
1161
|
0
|
0
|
|
|
|
0
|
next if $hash->equals($me); |
1162
|
0
|
|
|
|
|
0
|
my $storeUrl = $record->child('store')->textValue; |
1163
|
0
|
|
|
|
|
0
|
my $revokedSelector = $child->child('revoked'); |
1164
|
0
|
|
|
|
|
0
|
my $activeSelector = $child->child('active'); |
1165
|
0
|
|
|
|
|
0
|
my $revision = CDS->max($child->revision, $revokedSelector->revision, $activeSelector->revision); |
1166
|
0
|
0
|
|
|
|
0
|
my $actorStatus = $revokedSelector->booleanValue ? 'revoked' : $activeSelector->booleanValue ? 'active' : 'idle'; |
|
|
0
|
|
|
|
|
|
1167
|
0
|
|
|
|
|
0
|
$builder->addMember($hash, $storeUrl, $revision, $actorStatus); |
1168
|
|
|
|
|
|
|
} |
1169
|
|
|
|
|
|
|
|
1170
|
0
|
0
|
|
|
|
0
|
$builder->parseEntrustedActorList($o->entrustedActorsSelector->record, 1) if $builder->mergeEntrustedActors($o->entrustedActorsSelector->revision); |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
# Create the card |
1173
|
0
|
|
|
|
|
0
|
my $card = $builder->toRecord(0); |
1174
|
0
|
|
|
|
|
0
|
$card->add('public key')->addHash($o->{keyPair}->publicKey->hash); |
1175
|
|
|
|
|
|
|
|
1176
|
|
|
|
|
|
|
# Add the public data |
1177
|
0
|
|
|
|
|
0
|
for my $child ($o->publicDataSelector->children) { |
1178
|
0
|
|
|
|
|
0
|
my $childRecord = $child->record; |
1179
|
0
|
|
|
|
|
0
|
$card->addRecord($childRecord->children); |
1180
|
|
|
|
|
|
|
} |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
# Create an unsaved state |
1183
|
0
|
|
|
|
|
0
|
my $unsaved = CDS::Unsaved->new($o->publicDataSelector->document->unsaved); |
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
# Add the public card and the public key |
1186
|
0
|
|
|
|
|
0
|
my $cardObject = $card->toObject; |
1187
|
0
|
|
|
|
|
0
|
my $cardHash = $cardObject->calculateHash; |
1188
|
0
|
|
|
|
|
0
|
$unsaved->state->addObject($cardHash, $cardObject); |
1189
|
0
|
|
|
|
|
0
|
$unsaved->state->addObject($me, $o->keyPair->publicKey->object); |
1190
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
# Prepare the public envelope |
1192
|
0
|
|
|
|
|
0
|
my $envelopeObject = $o->keyPair->createPublicEnvelope($cardHash)->toObject; |
1193
|
0
|
|
|
|
|
0
|
my $envelopeHash = $envelopeObject->calculateHash; |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
# Upload the objects |
1196
|
0
|
|
|
|
|
0
|
my ($missingObject, $transferStore, $transferError) = $o->keyPair->transfer([$cardHash], $unsaved, $store); |
1197
|
0
|
0
|
|
|
|
0
|
return if defined $transferError; |
1198
|
0
|
0
|
|
|
|
0
|
if ($missingObject) { |
1199
|
0
|
|
|
|
|
0
|
$missingObject->{context} = 'announce on '.$store->id; |
1200
|
0
|
|
|
|
|
0
|
$o->onMissingObject($missingObject); |
1201
|
0
|
|
|
|
|
0
|
return; |
1202
|
|
|
|
|
|
|
} |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
# Prepare to modify |
1205
|
0
|
|
|
|
|
0
|
my $modifications = CDS::StoreModifications->new; |
1206
|
0
|
|
|
|
|
0
|
$modifications->add($me, 'public', $envelopeHash, $envelopeObject); |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
# List the current cards to remove them |
1209
|
|
|
|
|
|
|
# Ignore errors, in the worst case, we are going to have multiple entries in the public box |
1210
|
0
|
|
|
|
|
0
|
my ($hashes, $error) = $store->list($me, 'public', 0, $o->keyPair); |
1211
|
0
|
0
|
|
|
|
0
|
if ($hashes) { |
1212
|
0
|
|
|
|
|
0
|
for my $hash (@$hashes) { |
1213
|
0
|
|
|
|
|
0
|
$modifications->remove($me, 'public', $hash); |
1214
|
|
|
|
|
|
|
} |
1215
|
|
|
|
|
|
|
} |
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
# Modify the public box |
1218
|
0
|
|
|
|
|
0
|
my $modifyError = $store->modify($modifications, $o->keyPair); |
1219
|
0
|
0
|
|
|
|
0
|
return if defined $modifyError; |
1220
|
0
|
|
|
|
|
0
|
return $envelopeHash, $cardHash; |
1221
|
|
|
|
|
|
|
} |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
# The result of parsing a BOX token (see Token.pm). |
1224
|
|
|
|
|
|
|
package CDS::BoxToken; |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
sub new { |
1227
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
1228
|
0
|
|
|
|
|
0
|
my $accountToken = shift; |
1229
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
1230
|
|
|
|
|
|
|
|
1231
|
0
|
|
|
|
|
0
|
return bless { |
1232
|
|
|
|
|
|
|
accountToken => $accountToken, |
1233
|
|
|
|
|
|
|
boxLabel => $boxLabel |
1234
|
|
|
|
|
|
|
}; |
1235
|
|
|
|
|
|
|
} |
1236
|
|
|
|
|
|
|
|
1237
|
0
|
|
|
0
|
|
0
|
sub accountToken { shift->{accountToken} } |
1238
|
0
|
|
|
0
|
|
0
|
sub boxLabel { shift->{boxLabel} } |
1239
|
|
|
|
|
|
|
sub url { |
1240
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1241
|
0
|
|
|
|
|
0
|
$o->{accountToken}->url.'/'.$o->{boxLabel} } |
1242
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
package CDS::CLIActor; |
1244
|
|
|
|
|
|
|
|
1245
|
1
|
|
|
1
|
|
7719
|
use parent -norequire, 'CDS::ActorWithDocument'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
sub openOrCreateDefault { |
1248
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
1249
|
0
|
|
|
|
|
0
|
my $ui = shift; |
1250
|
|
|
|
|
|
|
|
1251
|
0
|
|
|
|
|
0
|
$class->open(CDS::Configuration->getOrCreateDefault($ui)); |
1252
|
|
|
|
|
|
|
} |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
sub open { |
1255
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
1256
|
0
|
|
|
|
|
0
|
my $configuration = shift; |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
# Read the store configuration |
1259
|
0
|
|
|
|
|
0
|
my $ui = $configuration->ui; |
1260
|
0
|
|
|
|
|
0
|
my $storeManager = CDS::CLIStoreManager->new($ui); |
1261
|
|
|
|
|
|
|
|
1262
|
0
|
|
|
|
|
0
|
my $storageStoreUrl = $configuration->storageStoreUrl; |
1263
|
0
|
|
0
|
|
|
0
|
my $storageStore = $storeManager->storeForUrl($storageStoreUrl) // return $ui->error('Your storage store "', $storageStoreUrl, '" cannot be accessed. You can set this store in "', $configuration->file('store'), '".'); |
1264
|
|
|
|
|
|
|
|
1265
|
0
|
|
|
|
|
0
|
my $messagingStoreUrl = $configuration->messagingStoreUrl; |
1266
|
0
|
|
0
|
|
|
0
|
my $messagingStore = $storeManager->storeForUrl($messagingStoreUrl) // return $ui->error('Your messaging store "', $messagingStoreUrl, '" cannot be accessed. You can set this store in "', $configuration->file('messaging-store'), '".'); |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
# Read the key pair |
1269
|
0
|
|
0
|
|
|
0
|
my $keyPair = $configuration->keyPair // return $ui->error('Your key pair (', $configuration->file('key-pair'), ') is missing.'); |
1270
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
# Create the actor |
1272
|
0
|
|
|
|
|
0
|
my $publicKeyCache = CDS::PublicKeyCache->new(128); |
1273
|
0
|
|
|
|
|
0
|
my $o = $class->SUPER::new($keyPair, $storageStore, $messagingStore, $messagingStoreUrl, $publicKeyCache); |
1274
|
0
|
|
|
|
|
0
|
$o->{ui} = $ui; |
1275
|
0
|
|
|
|
|
0
|
$o->{storeManager} = $storeManager; |
1276
|
0
|
|
|
|
|
0
|
$o->{configuration} = $configuration; |
1277
|
0
|
|
|
|
|
0
|
$o->{sessionRoot} = $o->localRoot->child('sessions')->child(''.getppid); |
1278
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = CDS::KeyPairToken->new($configuration->file('key-pair'), $keyPair); |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
# Message handlers |
1281
|
0
|
|
|
|
|
0
|
$o->{messageHandlers} = {}; |
1282
|
0
|
|
|
|
|
0
|
$o->setMessageHandler('sender', \&onIgnoreMessage); |
1283
|
0
|
|
|
|
|
0
|
$o->setMessageHandler('store', \&onIgnoreMessage); |
1284
|
0
|
|
|
|
|
0
|
$o->setMessageHandler('group data', \&onGroupDataMessage); |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
# Read the private data |
1287
|
0
|
0
|
|
|
|
0
|
if (! $o->procurePrivateData) { |
1288
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
1289
|
0
|
|
|
|
|
0
|
$ui->pRed('Failed to read the local private data.'); |
1290
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
1291
|
0
|
|
|
|
|
0
|
return; |
1292
|
|
|
|
|
|
|
} |
1293
|
|
|
|
|
|
|
|
1294
|
0
|
|
|
|
|
0
|
return $o; |
1295
|
|
|
|
|
|
|
} |
1296
|
|
|
|
|
|
|
|
1297
|
0
|
|
|
0
|
|
0
|
sub ui { shift->{ui} } |
1298
|
0
|
|
|
0
|
|
0
|
sub storeManager { shift->{storeManager} } |
1299
|
0
|
|
|
0
|
|
0
|
sub configuration { shift->{configuration} } |
1300
|
0
|
|
|
0
|
|
0
|
sub sessionRoot { shift->{sessionRoot} } |
1301
|
0
|
|
|
0
|
|
0
|
sub keyPairToken { shift->{keyPairToken} } |
1302
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
### Saving |
1304
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
sub saveOrShowError { |
1306
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1307
|
|
|
|
|
|
|
|
1308
|
0
|
|
|
|
|
0
|
$o->forgetOldSessions; |
1309
|
0
|
|
|
|
|
0
|
my ($ok, $missingHash) = $o->savePrivateDataAndShareGroupData; |
1310
|
0
|
0
|
|
|
|
0
|
return if ! $ok; |
1311
|
0
|
0
|
|
|
|
0
|
return $o->onMissingObject($missingHash) if $missingHash; |
1312
|
0
|
|
|
|
|
0
|
$o->sendMessages; |
1313
|
0
|
|
|
|
|
0
|
return 1; |
1314
|
|
|
|
|
|
|
} |
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
sub onMissingObject { |
1317
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1318
|
0
|
0
|
0
|
|
|
0
|
my $missingObject = shift; die 'wrong type '.ref($missingObject).' for $missingObject' if defined $missingObject && ref $missingObject ne 'CDS::Object'; |
|
0
|
|
|
|
|
0
|
|
1319
|
|
|
|
|
|
|
|
1320
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
1321
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('The object ', $missingObject->hash->hex, ' was missing while saving data.'); |
1322
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
1323
|
0
|
|
|
|
|
0
|
$o->{ui}->p('This is a fatal error with two possible sources:'); |
1324
|
0
|
|
|
|
|
0
|
$o->{ui}->p('- A store may have lost objects, e.g. due to an error with the underlying storage, misconfiguration, or too aggressive garbage collection.'); |
1325
|
0
|
|
|
|
|
0
|
$o->{ui}->p('- The application is linking objects without properly storing them. This is an error in the application, that must be fixed by a developer.'); |
1326
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
1327
|
|
|
|
|
|
|
} |
1328
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
sub onGroupDataSharingStoreError { |
1330
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1331
|
0
|
0
|
0
|
|
|
0
|
my $recipientActorOnStore = shift; die 'wrong type '.ref($recipientActorOnStore).' for $recipientActorOnStore' if defined $recipientActorOnStore && ref $recipientActorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
0
|
|
1332
|
0
|
|
|
|
|
0
|
my $storeError = shift; |
1333
|
|
|
|
|
|
|
|
1334
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
1335
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('Unable to share the group data with ', $recipientActorOnStore->publicKey->hash->hex, '.'); |
1336
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
1337
|
|
|
|
|
|
|
} |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
### Reading |
1340
|
|
|
|
|
|
|
|
1341
|
|
|
|
|
|
|
sub onPrivateRootReadingInvalidEntry { |
1342
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1343
|
0
|
0
|
0
|
|
|
0
|
my $source = shift; die 'wrong type '.ref($source).' for $source' if defined $source && ref $source ne 'CDS::Source'; |
|
0
|
|
|
|
|
0
|
|
1344
|
0
|
|
|
|
|
0
|
my $reason = shift; |
1345
|
|
|
|
|
|
|
|
1346
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
1347
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('The envelope ', $source->hash->shortHex, ' points to invalid private data (', $reason, ').'); |
1348
|
0
|
|
|
|
|
0
|
$o->{ui}->p('This could be due to a storage system failure, a malicious attempt to delete or modify your data, or simply an application error. To investigate what is going on, the following commands may be helpful:'); |
1349
|
0
|
|
|
|
|
0
|
$o->{ui}->line(' cds open envelope ', $source->hash->hex, ' from ', $source->actorOnStore->publicKey->hash->hex, ' on ', $source->actorOnStore->store->url); |
1350
|
0
|
|
|
|
|
0
|
$o->{ui}->line(' cds show record ', $source->hash->hex, ' on ', $source->actorOnStore->store->url); |
1351
|
0
|
|
|
|
|
0
|
$o->{ui}->line(' cds list private box of ', $source->actorOnStore->publicKey->hash->hex, ' on ', $source->actorOnStore->store->url); |
1352
|
0
|
|
|
|
|
0
|
$o->{ui}->p('To remove the invalid entry, type:'); |
1353
|
0
|
|
|
|
|
0
|
$o->{ui}->line(' cds remove ', $source->hash->hex, ' from private box of ', $source->actorOnStore->publicKey->hash->hex, ' on ', $source->actorOnStore->store->url); |
1354
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
1355
|
|
|
|
|
|
|
} |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
sub onVerifyMemberStore { |
1358
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1359
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
1360
|
0
|
0
|
0
|
|
|
0
|
my $actorSelector = shift; die 'wrong type '.ref($actorSelector).' for $actorSelector' if defined $actorSelector && ref $actorSelector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
0
|
|
1361
|
0
|
|
|
|
|
0
|
$o->storeForUrl($storeUrl) } |
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
### Announcing |
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
sub registerIfNecessary { |
1366
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1367
|
|
|
|
|
|
|
|
1368
|
0
|
|
|
|
|
0
|
my $now = CDS->now; |
1369
|
0
|
0
|
|
|
|
0
|
return if $o->{actorSelector}->revision > $now - CDS->DAY; |
1370
|
0
|
|
|
|
|
0
|
$o->updateMyRegistration; |
1371
|
0
|
|
|
|
|
0
|
$o->setMyActiveFlag(1); |
1372
|
0
|
|
|
|
|
0
|
$o->setMyGroupDataFlag(1); |
1373
|
|
|
|
|
|
|
} |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
sub announceIfNecessary { |
1376
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1377
|
|
|
|
|
|
|
|
1378
|
0
|
|
|
|
|
0
|
my $state = join('', map { CDS->bytesFromUnsigned($_->revision) } sort { $a->label cmp $b->label } $o->{actorGroupSelector}->children); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1379
|
0
|
|
|
|
|
0
|
$o->announceOnStoreIfNecessary($o->{storageStore}, $state); |
1380
|
0
|
0
|
|
|
|
0
|
$o->announceOnStoreIfNecessary($o->{messagingStore}, $state) if $o->{messagingStore}->id ne $o->{storageStore}->id; |
1381
|
|
|
|
|
|
|
} |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
sub announceOnStoreIfNecessary { |
1384
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1385
|
0
|
|
|
|
|
0
|
my $store = shift; |
1386
|
0
|
|
|
|
|
0
|
my $state = shift; |
1387
|
|
|
|
|
|
|
|
1388
|
0
|
|
|
|
|
0
|
my $stateSelector = $o->{localRoot}->child('announced')->childWithText($store->id); |
1389
|
0
|
0
|
|
|
|
0
|
return if $stateSelector->bytesValue eq $state; |
1390
|
0
|
|
|
|
|
0
|
my ($envelopeHash, $cardHash) = $o->announce($store); |
1391
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->pRed('Updating the card on ', $store->url, ' failed.') if ! $envelopeHash; |
1392
|
0
|
|
|
|
|
0
|
$stateSelector->setBytes($state); |
1393
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('The card on ', $store->url, ' has been updated.'); |
1394
|
0
|
|
|
|
|
0
|
return 1; |
1395
|
|
|
|
|
|
|
} |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
### Store resolving |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
sub storeForUrl { |
1400
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1401
|
0
|
|
|
|
|
0
|
my $url = shift; |
1402
|
|
|
|
|
|
|
|
1403
|
0
|
|
|
|
|
0
|
$o->{storeManager}->setCacheStoreUrl($o->{sessionRoot}->child('use cache')->textValue); |
1404
|
0
|
|
|
|
|
0
|
return $o->{storeManager}->storeForUrl($url); |
1405
|
|
|
|
|
|
|
} |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
### Processing messages |
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
sub setMessageHandler { |
1410
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1411
|
0
|
|
|
|
|
0
|
my $type = shift; |
1412
|
0
|
|
|
|
|
0
|
my $handler = shift; |
1413
|
|
|
|
|
|
|
|
1414
|
0
|
|
|
|
|
0
|
$o->{messageHandlers}->{$type} = $handler; |
1415
|
|
|
|
|
|
|
} |
1416
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
sub readMessages { |
1418
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1419
|
|
|
|
|
|
|
|
1420
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Messages'); |
1421
|
0
|
|
|
|
|
0
|
$o->{countMessages} = 0; |
1422
|
0
|
|
|
|
|
0
|
$o->{messageBoxReader}->read; |
1423
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray('none')) if ! $o->{countMessages}; |
1424
|
|
|
|
|
|
|
} |
1425
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
sub onMessageBoxVerifyStore { |
1427
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1428
|
0
|
|
|
|
|
0
|
my $senderStoreUrl = shift; |
1429
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1430
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
1431
|
0
|
0
|
0
|
|
|
0
|
my $senderHash = shift; die 'wrong type '.ref($senderHash).' for $senderHash' if defined $senderHash && ref $senderHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1432
|
|
|
|
|
|
|
|
1433
|
0
|
|
|
|
|
0
|
return $o->storeForUrl($senderStoreUrl); |
1434
|
|
|
|
|
|
|
} |
1435
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
sub onMessageBoxEntry { |
1437
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1438
|
0
|
|
|
|
|
0
|
my $message = shift; |
1439
|
|
|
|
|
|
|
|
1440
|
0
|
|
|
|
|
0
|
$o->{countMessages} += 1; |
1441
|
|
|
|
|
|
|
|
1442
|
0
|
|
|
|
|
0
|
for my $section ($message->content->children) { |
1443
|
0
|
|
|
|
|
0
|
my $type = $section->bytes; |
1444
|
0
|
|
0
|
|
|
0
|
my $handler = $o->{messageHandlers}->{$type} // \&onUnknownMessage; |
1445
|
0
|
|
|
|
|
0
|
&$handler($o, $message, $section); |
1446
|
|
|
|
|
|
|
} |
1447
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
# 1. message processed |
1449
|
|
|
|
|
|
|
# -> source can be deleted immediately (e.g. invalid) |
1450
|
|
|
|
|
|
|
# source.discard() |
1451
|
|
|
|
|
|
|
# -> source has been merged, and will be deleted when changes have been saved |
1452
|
|
|
|
|
|
|
# document.addMergedSource(source) |
1453
|
|
|
|
|
|
|
# 2. wait for sender store |
1454
|
|
|
|
|
|
|
# -> set entry.waitForStore = senderStore |
1455
|
|
|
|
|
|
|
# 3. skip |
1456
|
|
|
|
|
|
|
# -> set entry.processed = false |
1457
|
|
|
|
|
|
|
|
1458
|
0
|
|
|
|
|
0
|
my $source = $message->source; |
1459
|
0
|
|
|
|
|
0
|
$message->source->discard; |
1460
|
|
|
|
|
|
|
} |
1461
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
sub onGroupDataMessage { |
1463
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1464
|
0
|
|
|
|
|
0
|
my $message = shift; |
1465
|
0
|
|
|
|
|
0
|
my $section = shift; |
1466
|
|
|
|
|
|
|
|
1467
|
0
|
|
|
|
|
0
|
my $ok = $o->{groupDataSharer}->processGroupDataMessage($message, $section); |
1468
|
0
|
|
|
|
|
0
|
$o->{groupDocument}->read; |
1469
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->line('Group data from ', $message->sender->publicKey->hash->hex) if $ok; |
1470
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->red('Group data from foreign actor ', $message->sender->publicKey->hash->hex, ' (ignored)')); |
1471
|
|
|
|
|
|
|
} |
1472
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
sub onIgnoreMessage { |
1474
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1475
|
0
|
|
|
|
|
0
|
my $message = shift; |
1476
|
0
|
|
|
|
|
0
|
my $section = shift; |
1477
|
|
|
|
|
|
|
} |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
sub onUnknownMessage { |
1480
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1481
|
0
|
|
|
|
|
0
|
my $message = shift; |
1482
|
0
|
|
|
|
|
0
|
my $section = shift; |
1483
|
|
|
|
|
|
|
|
1484
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->orange('Unknown message of type "', $section->asText, '" from ', $message->sender->publicKey->hash->hex)); |
1485
|
|
|
|
|
|
|
} |
1486
|
|
|
|
|
|
|
|
1487
|
|
|
|
|
|
|
sub onMessageBoxInvalidEntry { |
1488
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1489
|
0
|
0
|
0
|
|
|
0
|
my $source = shift; die 'wrong type '.ref($source).' for $source' if defined $source && ref $source ne 'CDS::Source'; |
|
0
|
|
|
|
|
0
|
|
1490
|
0
|
|
|
|
|
0
|
my $reason = shift; |
1491
|
|
|
|
|
|
|
|
1492
|
0
|
|
|
|
|
0
|
$o->{ui}->warning('Discarding invalid message ', $source->hash->hex, ' (', $reason, ').'); |
1493
|
0
|
|
|
|
|
0
|
$source->discard; |
1494
|
|
|
|
|
|
|
} |
1495
|
|
|
|
|
|
|
|
1496
|
|
|
|
|
|
|
### Remembered values |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
sub labelSelector { |
1499
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1500
|
0
|
|
|
|
|
0
|
my $label = shift; |
1501
|
|
|
|
|
|
|
|
1502
|
0
|
|
|
|
|
0
|
my $bytes = Encode::encode_utf8($label); |
1503
|
0
|
|
|
|
|
0
|
return $o->groupRoot->child('labels')->child($bytes); |
1504
|
|
|
|
|
|
|
} |
1505
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
sub remembered { |
1507
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1508
|
0
|
|
|
|
|
0
|
my $label = shift; |
1509
|
|
|
|
|
|
|
|
1510
|
0
|
|
|
|
|
0
|
return $o->labelSelector($label)->record; |
1511
|
|
|
|
|
|
|
} |
1512
|
|
|
|
|
|
|
|
1513
|
|
|
|
|
|
|
sub remember { |
1514
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1515
|
0
|
|
|
|
|
0
|
my $label = shift; |
1516
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
1517
|
|
|
|
|
|
|
|
1518
|
0
|
|
|
|
|
0
|
$o->labelSelector($label)->set($record); |
1519
|
|
|
|
|
|
|
} |
1520
|
|
|
|
|
|
|
|
1521
|
|
|
|
|
|
|
sub rememberedRecords { |
1522
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1523
|
|
|
|
|
|
|
|
1524
|
0
|
|
|
|
|
0
|
my $records = {}; |
1525
|
0
|
|
|
|
|
0
|
for my $child ($o->{groupRoot}->child('labels')->children) { |
1526
|
0
|
0
|
|
|
|
0
|
next if ! $child->isSet; |
1527
|
0
|
|
|
|
|
0
|
my $label = Encode::decode_utf8($child->label); |
1528
|
0
|
|
|
|
|
0
|
$records->{$label} = $child->record; |
1529
|
|
|
|
|
|
|
} |
1530
|
|
|
|
|
|
|
|
1531
|
0
|
|
|
|
|
0
|
return $records; |
1532
|
|
|
|
|
|
|
} |
1533
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
sub storeLabel { |
1535
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1536
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
1537
|
|
|
|
|
|
|
|
1538
|
0
|
|
|
|
|
0
|
my $records = $o->rememberedRecords; |
1539
|
0
|
|
|
|
|
0
|
for my $label (keys %$records) { |
1540
|
0
|
|
|
|
|
0
|
my $record = $records->{$label}; |
1541
|
0
|
0
|
|
|
|
0
|
next if length $record->child('actor')->bytesValue; |
1542
|
0
|
0
|
|
|
|
0
|
next if $storeUrl ne $record->child('store')->textValue; |
1543
|
0
|
|
|
|
|
0
|
return $label; |
1544
|
|
|
|
|
|
|
} |
1545
|
|
|
|
|
|
|
|
1546
|
0
|
|
|
|
|
0
|
return; |
1547
|
|
|
|
|
|
|
} |
1548
|
|
|
|
|
|
|
|
1549
|
|
|
|
|
|
|
sub actorLabel { |
1550
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1551
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1552
|
|
|
|
|
|
|
|
1553
|
0
|
|
|
|
|
0
|
my $records = $o->rememberedRecords; |
1554
|
0
|
|
|
|
|
0
|
for my $label (keys %$records) { |
1555
|
0
|
|
|
|
|
0
|
my $record = $records->{$label}; |
1556
|
0
|
0
|
|
|
|
0
|
next if $actorHash->bytes ne $record->child('actor')->bytesValue; |
1557
|
0
|
|
|
|
|
0
|
return $label; |
1558
|
|
|
|
|
|
|
} |
1559
|
|
|
|
|
|
|
|
1560
|
0
|
|
|
|
|
0
|
return; |
1561
|
|
|
|
|
|
|
} |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
sub actorLabelByHashStartBytes { |
1564
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1565
|
0
|
|
|
|
|
0
|
my $actorHashStartBytes = shift; |
1566
|
|
|
|
|
|
|
|
1567
|
0
|
|
|
|
|
0
|
my $length = length $actorHashStartBytes; |
1568
|
0
|
|
|
|
|
0
|
my $records = $o->rememberedRecords; |
1569
|
0
|
|
|
|
|
0
|
for my $label (keys %$records) { |
1570
|
0
|
|
|
|
|
0
|
my $record = $records->{$label}; |
1571
|
0
|
0
|
|
|
|
0
|
next if $actorHashStartBytes ne substr($record->child('actor')->bytesValue, 0, $length); |
1572
|
0
|
|
|
|
|
0
|
return $label; |
1573
|
|
|
|
|
|
|
} |
1574
|
|
|
|
|
|
|
|
1575
|
0
|
|
|
|
|
0
|
return; |
1576
|
|
|
|
|
|
|
} |
1577
|
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
sub accountLabel { |
1579
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1580
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
1581
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1582
|
|
|
|
|
|
|
|
1583
|
0
|
|
|
|
|
0
|
my $storeLabel; |
1584
|
|
|
|
|
|
|
my $actorLabel; |
1585
|
|
|
|
|
|
|
|
1586
|
0
|
|
|
|
|
0
|
my $records = $o->rememberedRecords; |
1587
|
0
|
|
|
|
|
0
|
for my $label (keys %$records) { |
1588
|
0
|
|
|
|
|
0
|
my $record = $records->{$label}; |
1589
|
0
|
|
|
|
|
0
|
my $actorBytes = $record->child('actor')->bytesValue; |
1590
|
|
|
|
|
|
|
|
1591
|
0
|
|
|
|
|
0
|
my $correctActor = $actorHash->bytes eq $actorBytes; |
1592
|
0
|
0
|
|
|
|
0
|
$actorLabel = $label if $correctActor; |
1593
|
|
|
|
|
|
|
|
1594
|
0
|
0
|
|
|
|
0
|
if ($storeUrl eq $record->child('store')->textValue) { |
1595
|
0
|
0
|
|
|
|
0
|
return $label if $correctActor; |
1596
|
0
|
0
|
|
|
|
0
|
$storeLabel = $label if ! length $actorBytes; |
1597
|
|
|
|
|
|
|
} |
1598
|
|
|
|
|
|
|
} |
1599
|
|
|
|
|
|
|
|
1600
|
0
|
|
|
|
|
0
|
return (undef, $storeLabel, $actorLabel); |
1601
|
|
|
|
|
|
|
} |
1602
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
sub keyPairLabel { |
1604
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1605
|
0
|
|
|
|
|
0
|
my $file = shift; |
1606
|
|
|
|
|
|
|
|
1607
|
0
|
|
|
|
|
0
|
my $records = $o->rememberedRecords; |
1608
|
0
|
|
|
|
|
0
|
for my $label (keys %$records) { |
1609
|
0
|
|
|
|
|
0
|
my $record = $records->{$label}; |
1610
|
0
|
0
|
|
|
|
0
|
next if $file ne $record->child('key pair')->textValue; |
1611
|
0
|
|
|
|
|
0
|
return $label; |
1612
|
|
|
|
|
|
|
} |
1613
|
|
|
|
|
|
|
|
1614
|
0
|
|
|
|
|
0
|
return; |
1615
|
|
|
|
|
|
|
} |
1616
|
|
|
|
|
|
|
|
1617
|
|
|
|
|
|
|
### References that can be used in commands |
1618
|
|
|
|
|
|
|
|
1619
|
|
|
|
|
|
|
sub actorReference { |
1620
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1621
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1622
|
|
|
|
|
|
|
|
1623
|
0
|
|
0
|
|
|
0
|
return $o->actorLabel($actorHash) // $actorHash->hex; |
1624
|
|
|
|
|
|
|
} |
1625
|
|
|
|
|
|
|
|
1626
|
|
|
|
|
|
|
sub storeReference { |
1627
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1628
|
0
|
|
|
|
|
0
|
my $store = shift; |
1629
|
0
|
|
|
|
|
0
|
$o->storeUrlReference($store->url); } |
1630
|
|
|
|
|
|
|
|
1631
|
|
|
|
|
|
|
sub storeUrlReference { |
1632
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1633
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
1634
|
|
|
|
|
|
|
|
1635
|
0
|
|
0
|
|
|
0
|
return $o->storeLabel($storeUrl) // $storeUrl; |
1636
|
|
|
|
|
|
|
} |
1637
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
sub accountReference { |
1639
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1640
|
0
|
|
|
|
|
0
|
my $accountToken = shift; |
1641
|
|
|
|
|
|
|
|
1642
|
0
|
|
|
|
|
0
|
my ($accountLabel, $storeLabel, $actorLabel) = $o->accountLabel($accountToken->{cliStore}->url, $accountToken->{actorHash}); |
1643
|
0
|
0
|
|
|
|
0
|
return $accountLabel if defined $accountLabel; |
1644
|
0
|
0
|
|
|
|
0
|
return defined $actorLabel ? $actorLabel : $accountToken->{actorHash}->hex, ' on ', defined $storeLabel ? $storeLabel : $accountToken->{cliStore}->url; |
|
|
0
|
|
|
|
|
|
1645
|
|
|
|
|
|
|
} |
1646
|
|
|
|
|
|
|
|
1647
|
|
|
|
|
|
|
sub boxReference { |
1648
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1649
|
0
|
|
|
|
|
0
|
my $boxToken = shift; |
1650
|
|
|
|
|
|
|
|
1651
|
0
|
|
|
|
|
0
|
return $o->boxName($boxToken->{boxLabel}), ' of ', $o->accountReference($boxToken->{accountToken}); |
1652
|
|
|
|
|
|
|
} |
1653
|
|
|
|
|
|
|
|
1654
|
|
|
|
|
|
|
sub keyPairReference { |
1655
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1656
|
0
|
|
|
|
|
0
|
my $keyPairToken = shift; |
1657
|
|
|
|
|
|
|
|
1658
|
0
|
|
0
|
|
|
0
|
return $o->keyPairLabel($keyPairToken->file) // $keyPairToken->file; |
1659
|
|
|
|
|
|
|
} |
1660
|
|
|
|
|
|
|
|
1661
|
|
|
|
|
|
|
sub blueActorReference { |
1662
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1663
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1664
|
|
|
|
|
|
|
|
1665
|
0
|
|
|
|
|
0
|
my $label = $o->actorLabel($actorHash); |
1666
|
0
|
0
|
|
|
|
0
|
return defined $label ? $o->{ui}->blue($label) : $actorHash->hex; |
1667
|
|
|
|
|
|
|
} |
1668
|
|
|
|
|
|
|
|
1669
|
|
|
|
|
|
|
sub blueStoreReference { |
1670
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1671
|
0
|
|
|
|
|
0
|
my $store = shift; |
1672
|
0
|
|
|
|
|
0
|
$o->blueStoreUrlReference($store->url); } |
1673
|
|
|
|
|
|
|
|
1674
|
|
|
|
|
|
|
sub blueStoreUrlReference { |
1675
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1676
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
1677
|
|
|
|
|
|
|
|
1678
|
0
|
|
|
|
|
0
|
my $label = $o->storeLabel($storeUrl); |
1679
|
0
|
0
|
|
|
|
0
|
return defined $label ? $o->{ui}->blue($label) : $storeUrl; |
1680
|
|
|
|
|
|
|
} |
1681
|
|
|
|
|
|
|
|
1682
|
|
|
|
|
|
|
sub blueAccountReference { |
1683
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1684
|
0
|
|
|
|
|
0
|
my $accountToken = shift; |
1685
|
|
|
|
|
|
|
|
1686
|
0
|
|
|
|
|
0
|
my ($accountLabel, $storeLabel, $actorLabel) = $o->accountLabel($accountToken->{cliStore}->url, $accountToken->{actorHash}); |
1687
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->blue($accountLabel) if defined $accountLabel; |
1688
|
0
|
0
|
|
|
|
0
|
return defined $actorLabel ? $o->{ui}->blue($actorLabel) : $accountToken->{actorHash}->hex, ' on ', defined $storeLabel ? $o->{ui}->blue($storeLabel) : $accountToken->{cliStore}->url; |
|
|
0
|
|
|
|
|
|
1689
|
|
|
|
|
|
|
} |
1690
|
|
|
|
|
|
|
|
1691
|
|
|
|
|
|
|
sub blueBoxReference { |
1692
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1693
|
0
|
|
|
|
|
0
|
my $boxToken = shift; |
1694
|
|
|
|
|
|
|
|
1695
|
0
|
|
|
|
|
0
|
return $o->boxName($boxToken->{boxLabel}), ' of ', $o->blueAccountReference($boxToken->{accountToken}); |
1696
|
|
|
|
|
|
|
} |
1697
|
|
|
|
|
|
|
|
1698
|
|
|
|
|
|
|
sub blueKeyPairReference { |
1699
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1700
|
0
|
|
|
|
|
0
|
my $keyPairToken = shift; |
1701
|
|
|
|
|
|
|
|
1702
|
0
|
|
|
|
|
0
|
my $label = $o->keyPairLabel($keyPairToken->file); |
1703
|
0
|
0
|
|
|
|
0
|
return defined $label ? $o->{ui}->blue($label) : $keyPairToken->file; |
1704
|
|
|
|
|
|
|
} |
1705
|
|
|
|
|
|
|
|
1706
|
|
|
|
|
|
|
sub boxName { |
1707
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1708
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
1709
|
|
|
|
|
|
|
|
1710
|
0
|
0
|
|
|
|
0
|
return 'private box' if $boxLabel eq 'private'; |
1711
|
0
|
0
|
|
|
|
0
|
return 'public box' if $boxLabel eq 'public'; |
1712
|
0
|
0
|
|
|
|
0
|
return 'message box' if $boxLabel eq 'messages'; |
1713
|
0
|
|
|
|
|
0
|
return $boxLabel; |
1714
|
|
|
|
|
|
|
} |
1715
|
|
|
|
|
|
|
|
1716
|
|
|
|
|
|
|
### Session |
1717
|
|
|
|
|
|
|
|
1718
|
|
|
|
|
|
|
sub forgetOldSessions { |
1719
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1720
|
|
|
|
|
|
|
|
1721
|
0
|
|
|
|
|
0
|
for my $child ($o->{sessionRoot}->parent->children) { |
1722
|
0
|
|
|
|
|
0
|
my $pid = $child->label; |
1723
|
0
|
0
|
|
|
|
0
|
next if -e '/proc/'.$pid; |
1724
|
0
|
|
|
|
|
0
|
$child->forgetBranch; |
1725
|
|
|
|
|
|
|
} |
1726
|
|
|
|
|
|
|
} |
1727
|
|
|
|
|
|
|
|
1728
|
|
|
|
|
|
|
sub selectedKeyPairToken { |
1729
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1730
|
|
|
|
|
|
|
|
1731
|
0
|
|
|
|
|
0
|
my $file = $o->{sessionRoot}->child('selected key pair')->textValue; |
1732
|
0
|
0
|
|
|
|
0
|
return if ! length $file; |
1733
|
0
|
|
0
|
|
|
0
|
my $keyPair = CDS::KeyPair->fromFile($file) // return; |
1734
|
0
|
|
|
|
|
0
|
return CDS::KeyPairToken->new($file, $keyPair); |
1735
|
|
|
|
|
|
|
} |
1736
|
|
|
|
|
|
|
|
1737
|
|
|
|
|
|
|
sub selectedStoreUrl { |
1738
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1739
|
|
|
|
|
|
|
|
1740
|
0
|
|
|
|
|
0
|
my $storeUrl = $o->{sessionRoot}->child('selected store')->textValue; |
1741
|
0
|
0
|
|
|
|
0
|
return if ! length $storeUrl; |
1742
|
0
|
|
|
|
|
0
|
return $storeUrl; |
1743
|
|
|
|
|
|
|
} |
1744
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
sub selectedStore { |
1746
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1747
|
|
|
|
|
|
|
|
1748
|
0
|
|
0
|
|
|
0
|
my $storeUrl = $o->selectedStoreUrl // return; |
1749
|
0
|
|
|
|
|
0
|
return $o->storeForUrl($storeUrl); |
1750
|
|
|
|
|
|
|
} |
1751
|
|
|
|
|
|
|
|
1752
|
|
|
|
|
|
|
sub selectedActorHash { |
1753
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1754
|
|
|
|
|
|
|
|
1755
|
0
|
|
|
|
|
0
|
return CDS::Hash->fromBytes($o->{sessionRoot}->child('selected actor')->bytesValue); |
1756
|
|
|
|
|
|
|
} |
1757
|
|
|
|
|
|
|
|
1758
|
|
|
|
|
|
|
sub preferredKeyPairToken { |
1759
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1760
|
0
|
|
0
|
|
|
0
|
$o->selectedKeyPairToken // $o->keyPairToken } |
1761
|
|
|
|
|
|
|
sub preferredStore { |
1762
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1763
|
0
|
|
0
|
|
|
0
|
$o->selectedStore // $o->storageStore } |
1764
|
|
|
|
|
|
|
sub preferredStores { |
1765
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1766
|
0
|
|
0
|
|
|
0
|
$o->selectedStore // ($o->storageStore, $o->messagingStore) } |
1767
|
|
|
|
|
|
|
sub preferredActorHash { |
1768
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1769
|
0
|
|
0
|
|
|
0
|
$o->selectedActorHash // $o->keyPair->publicKey->hash } |
1770
|
|
|
|
|
|
|
|
1771
|
|
|
|
|
|
|
### Common functions |
1772
|
|
|
|
|
|
|
|
1773
|
|
|
|
|
|
|
sub uiGetObject { |
1774
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1775
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1776
|
0
|
|
|
|
|
0
|
my $store = shift; |
1777
|
0
|
|
|
|
|
0
|
my $keyPairToken = shift; |
1778
|
|
|
|
|
|
|
|
1779
|
0
|
|
|
|
|
0
|
my ($object, $storeError) = $store->get($hash, $keyPairToken->keyPair); |
1780
|
0
|
0
|
|
|
|
0
|
return if defined $storeError; |
1781
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The object ', $hash->hex, ' does not exist on "', $store->url, '".') if ! $object; |
1782
|
0
|
|
|
|
|
0
|
return $object; |
1783
|
|
|
|
|
|
|
} |
1784
|
|
|
|
|
|
|
|
1785
|
|
|
|
|
|
|
sub uiGetRecord { |
1786
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1787
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1788
|
0
|
|
|
|
|
0
|
my $store = shift; |
1789
|
0
|
|
|
|
|
0
|
my $keyPairToken = shift; |
1790
|
|
|
|
|
|
|
|
1791
|
0
|
|
0
|
|
|
0
|
my $object = $o->uiGetObject($hash, $store, $keyPairToken) // return; |
1792
|
0
|
|
0
|
|
|
0
|
return CDS::Record->fromObject($object) // return $o->{ui}->error('The object ', $hash->hex, ' is not a record.'); |
1793
|
|
|
|
|
|
|
} |
1794
|
|
|
|
|
|
|
|
1795
|
|
|
|
|
|
|
sub uiGetPublicKey { |
1796
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1797
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1798
|
0
|
|
|
|
|
0
|
my $store = shift; |
1799
|
0
|
|
|
|
|
0
|
my $keyPairToken = shift; |
1800
|
|
|
|
|
|
|
|
1801
|
0
|
|
0
|
|
|
0
|
my $object = $o->uiGetObject($hash, $store, $keyPairToken) // return; |
1802
|
0
|
|
0
|
|
|
0
|
return CDS::PublicKey->fromObject($object) // return $o->{ui}->error('The object ', $hash->hex, ' is not a public key.'); |
1803
|
|
|
|
|
|
|
} |
1804
|
|
|
|
|
|
|
|
1805
|
|
|
|
|
|
|
sub isEnvelope { |
1806
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1807
|
0
|
0
|
0
|
|
|
0
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
0
|
|
1808
|
|
|
|
|
|
|
|
1809
|
0
|
|
0
|
|
|
0
|
my $record = CDS::Record->fromObject($object) // return; |
1810
|
0
|
0
|
|
|
|
0
|
return if ! $record->contains('signed'); |
1811
|
0
|
|
|
|
|
0
|
my $signatureRecord = $record->child('signature')->firstChild; |
1812
|
0
|
0
|
|
|
|
0
|
return if ! $signatureRecord->hash; |
1813
|
0
|
0
|
|
|
|
0
|
return if ! length $signatureRecord->bytes; |
1814
|
0
|
|
|
|
|
0
|
return 1; |
1815
|
|
|
|
|
|
|
} |
1816
|
|
|
|
|
|
|
|
1817
|
|
|
|
|
|
|
package CDS::CLIStoreManager; |
1818
|
|
|
|
|
|
|
|
1819
|
|
|
|
|
|
|
sub new { |
1820
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
1821
|
0
|
|
|
|
|
0
|
my $ui = shift; |
1822
|
|
|
|
|
|
|
|
1823
|
0
|
|
|
|
|
0
|
return bless {ui => $ui, failedStores => {}}; |
1824
|
|
|
|
|
|
|
} |
1825
|
|
|
|
|
|
|
|
1826
|
0
|
|
|
0
|
|
0
|
sub ui { shift->{ui} } |
1827
|
|
|
|
|
|
|
|
1828
|
|
|
|
|
|
|
sub rawStoreForUrl { |
1829
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1830
|
0
|
|
|
|
|
0
|
my $url = shift; |
1831
|
|
|
|
|
|
|
|
1832
|
0
|
0
|
|
|
|
0
|
return if ! $url; |
1833
|
|
|
|
|
|
|
return |
1834
|
0
|
|
0
|
|
|
0
|
CDS::FolderStore->forUrl($url) // |
|
|
|
0
|
|
|
|
|
1835
|
|
|
|
|
|
|
CDS::HTTPStore->forUrl($url) // |
1836
|
|
|
|
|
|
|
undef; |
1837
|
|
|
|
|
|
|
} |
1838
|
|
|
|
|
|
|
|
1839
|
|
|
|
|
|
|
sub storeForUrl { |
1840
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1841
|
0
|
|
|
|
|
0
|
my $url = shift; |
1842
|
|
|
|
|
|
|
|
1843
|
0
|
|
|
|
|
0
|
my $store = $o->rawStoreForUrl($url); |
1844
|
0
|
|
|
|
|
0
|
my $progressStore = CDS::UI::ProgressStore->new($store, $url, $o->{ui}); |
1845
|
0
|
0
|
|
|
|
0
|
my $cachedStore = defined $o->{cacheStore} ? CDS::ObjectCache->new($progressStore, $o->{cacheStore}) : $progressStore; |
1846
|
0
|
|
|
|
|
0
|
return CDS::ErrorHandlingStore->new($cachedStore, $url, $o); |
1847
|
|
|
|
|
|
|
} |
1848
|
|
|
|
|
|
|
|
1849
|
|
|
|
|
|
|
sub onStoreSuccess { |
1850
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1851
|
0
|
|
|
|
|
0
|
my $store = shift; |
1852
|
0
|
|
|
|
|
0
|
my $function = shift; |
1853
|
|
|
|
|
|
|
|
1854
|
0
|
|
|
|
|
0
|
delete $o->{failedStores}->{$store->store->id}; |
1855
|
|
|
|
|
|
|
} |
1856
|
|
|
|
|
|
|
|
1857
|
|
|
|
|
|
|
sub onStoreError { |
1858
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1859
|
0
|
|
|
|
|
0
|
my $store = shift; |
1860
|
0
|
|
|
|
|
0
|
my $function = shift; |
1861
|
0
|
|
|
|
|
0
|
my $error = shift; |
1862
|
|
|
|
|
|
|
|
1863
|
0
|
|
|
|
|
0
|
$o->{failedStores}->{$store->store->id} = 1; |
1864
|
0
|
|
|
|
|
0
|
$o->{ui}->error('The store "', $store->{url}, '" reports: ', $error); |
1865
|
|
|
|
|
|
|
} |
1866
|
|
|
|
|
|
|
|
1867
|
|
|
|
|
|
|
sub hasStoreError { |
1868
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1869
|
0
|
|
|
|
|
0
|
my $store = shift; |
1870
|
0
|
|
|
|
|
0
|
my $function = shift; |
1871
|
|
|
|
|
|
|
|
1872
|
0
|
0
|
|
|
|
0
|
return if ! $o->{failedStores}->{$store->store->id}; |
1873
|
0
|
|
|
|
|
0
|
$o->{ui}->error('Ignoring store "', $store->{url}, '", because it previously reported errors.'); |
1874
|
0
|
|
|
|
|
0
|
return 1; |
1875
|
|
|
|
|
|
|
} |
1876
|
|
|
|
|
|
|
|
1877
|
|
|
|
|
|
|
sub setCacheStoreUrl { |
1878
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1879
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
1880
|
|
|
|
|
|
|
|
1881
|
0
|
0
|
0
|
|
|
0
|
return if ($storeUrl // '') eq ($o->{cacheStoreUrl} // ''); |
|
|
|
0
|
|
|
|
|
1882
|
0
|
|
|
|
|
0
|
$o->{cacheStoreUrl} = $storeUrl; |
1883
|
0
|
|
|
|
|
0
|
$o->{cacheStore} = $o->rawStoreForUrl($storeUrl); |
1884
|
|
|
|
|
|
|
} |
1885
|
|
|
|
|
|
|
|
1886
|
|
|
|
|
|
|
package CDS::CheckSignatureStore; |
1887
|
|
|
|
|
|
|
|
1888
|
|
|
|
|
|
|
sub new { |
1889
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1890
|
0
|
|
|
|
|
0
|
my $store = shift; |
1891
|
0
|
|
|
|
|
0
|
my $objects = shift; |
1892
|
|
|
|
|
|
|
|
1893
|
0
|
|
0
|
|
|
0
|
return bless { |
1894
|
|
|
|
|
|
|
store => $store, |
1895
|
|
|
|
|
|
|
id => "Check signature store\n".$store->id, |
1896
|
|
|
|
|
|
|
objects => $objects // {}, |
1897
|
|
|
|
|
|
|
}; |
1898
|
|
|
|
|
|
|
} |
1899
|
|
|
|
|
|
|
|
1900
|
0
|
|
|
0
|
|
0
|
sub id { shift->{id} } |
1901
|
|
|
|
|
|
|
|
1902
|
|
|
|
|
|
|
sub get { |
1903
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1904
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1905
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
1906
|
|
|
|
|
|
|
|
1907
|
0
|
|
0
|
|
|
0
|
my $entry = $o->{objects}->{$hash->bytes} // return $o->{store}->get($hash); |
1908
|
0
|
|
|
|
|
0
|
return $entry->{object}; |
1909
|
|
|
|
|
|
|
} |
1910
|
|
|
|
|
|
|
|
1911
|
|
|
|
|
|
|
sub book { |
1912
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1913
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1914
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
1915
|
|
|
|
|
|
|
|
1916
|
0
|
|
|
|
|
0
|
return exists $o->{objects}->{$hash->bytes}; |
1917
|
|
|
|
|
|
|
} |
1918
|
|
|
|
|
|
|
|
1919
|
|
|
|
|
|
|
sub put { |
1920
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1921
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1922
|
0
|
0
|
0
|
|
|
0
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
0
|
|
1923
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
1924
|
|
|
|
|
|
|
|
1925
|
0
|
|
|
|
|
0
|
$o->{objects}->{$hash->bytes} = {hash => $hash, object => $object}; |
1926
|
0
|
|
|
|
|
0
|
return; |
1927
|
|
|
|
|
|
|
} |
1928
|
|
|
|
|
|
|
|
1929
|
|
|
|
|
|
|
sub list { |
1930
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1931
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1932
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
1933
|
0
|
|
|
|
|
0
|
my $timeout = shift; |
1934
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
1935
|
|
|
|
|
|
|
|
1936
|
0
|
|
|
|
|
0
|
return 'This store only handles objects.'; |
1937
|
|
|
|
|
|
|
} |
1938
|
|
|
|
|
|
|
|
1939
|
|
|
|
|
|
|
sub add { |
1940
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1941
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1942
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
1943
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1944
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
1945
|
|
|
|
|
|
|
|
1946
|
0
|
|
|
|
|
0
|
return 'This store only handles objects.'; |
1947
|
|
|
|
|
|
|
} |
1948
|
|
|
|
|
|
|
|
1949
|
|
|
|
|
|
|
sub remove { |
1950
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1951
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1952
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
1953
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
1954
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
1955
|
|
|
|
|
|
|
|
1956
|
0
|
|
|
|
|
0
|
return 'This store only handles objects.'; |
1957
|
|
|
|
|
|
|
} |
1958
|
|
|
|
|
|
|
|
1959
|
|
|
|
|
|
|
sub modify { |
1960
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
1961
|
0
|
|
|
|
|
0
|
my $modifications = shift; |
1962
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
1963
|
|
|
|
|
|
|
|
1964
|
0
|
|
|
|
|
0
|
return $modifications->executeIndividually($o, $keyPair); |
1965
|
|
|
|
|
|
|
} |
1966
|
|
|
|
|
|
|
|
1967
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
1968
|
|
|
|
|
|
|
package CDS::Commands::ActorGroup; |
1969
|
|
|
|
|
|
|
|
1970
|
|
|
|
|
|
|
sub register { |
1971
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
1972
|
0
|
|
|
|
|
0
|
my $cds = shift; |
1973
|
0
|
|
|
|
|
0
|
my $help = shift; |
1974
|
|
|
|
|
|
|
|
1975
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
1976
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
1977
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
1978
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
1979
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
1980
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
1981
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
1982
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
1983
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
1984
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
1985
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&show}); |
1986
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
1987
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
1988
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
1989
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&joinMember}); |
1990
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&setMember}); |
1991
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(0); |
1992
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'show'); |
1993
|
0
|
|
|
|
|
0
|
$cds->addArrow($node003, 1, 0, 'join'); |
1994
|
0
|
|
|
|
|
0
|
$cds->addArrow($node004, 1, 0, 'set'); |
1995
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'actor'); |
1996
|
0
|
|
|
|
|
0
|
$node000->addArrow($node009, 1, 0, 'group'); |
1997
|
0
|
|
|
|
|
0
|
$node001->addArrow($node002, 1, 0, 'actor'); |
1998
|
0
|
|
|
|
|
0
|
$node002->addArrow($node010, 1, 0, 'group'); |
1999
|
0
|
|
|
|
|
0
|
$node003->addArrow($node005, 1, 0, 'member'); |
2000
|
0
|
|
|
|
|
0
|
$node004->addArrow($node007, 1, 0, 'member'); |
2001
|
0
|
|
|
|
|
0
|
$node005->addDefault($node006); |
2002
|
0
|
|
|
|
|
0
|
$node005->addArrow($node011, 1, 0, 'ACTOR', \&collectActor); |
2003
|
0
|
|
|
|
|
0
|
$node006->addArrow($node006, 1, 0, 'ACCOUNT', \&collectAccount); |
2004
|
0
|
|
|
|
|
0
|
$node006->addArrow($node014, 1, 1, 'ACCOUNT', \&collectAccount); |
2005
|
0
|
|
|
|
|
0
|
$node007->addDefault($node008); |
2006
|
0
|
|
|
|
|
0
|
$node008->addArrow($node008, 1, 0, 'ACTOR', \&collectActor1); |
2007
|
0
|
|
|
|
|
0
|
$node008->addArrow($node013, 1, 0, 'ACTOR', \&collectActor1); |
2008
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'on'); |
2009
|
0
|
|
|
|
|
0
|
$node012->addArrow($node014, 1, 0, 'STORE', \&collectStore); |
2010
|
0
|
|
|
|
|
0
|
$node013->addArrow($node015, 1, 0, 'active', \&collectActive); |
2011
|
0
|
|
|
|
|
0
|
$node013->addArrow($node015, 1, 0, 'backup', \&collectBackup); |
2012
|
0
|
|
|
|
|
0
|
$node013->addArrow($node015, 1, 0, 'idle', \&collectIdle); |
2013
|
0
|
|
|
|
|
0
|
$node013->addArrow($node015, 1, 0, 'revoked', \&collectRevoked); |
2014
|
0
|
|
|
|
|
0
|
$node014->addArrow($node016, 1, 0, 'and'); |
2015
|
0
|
|
|
|
|
0
|
$node016->addDefault($node005); |
2016
|
|
|
|
|
|
|
} |
2017
|
|
|
|
|
|
|
|
2018
|
|
|
|
|
|
|
sub collectAccount { |
2019
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2020
|
0
|
|
|
|
|
0
|
my $label = shift; |
2021
|
0
|
|
|
|
|
0
|
my $value = shift; |
2022
|
|
|
|
|
|
|
|
2023
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, $value; |
|
0
|
|
|
|
|
0
|
|
2024
|
|
|
|
|
|
|
} |
2025
|
|
|
|
|
|
|
|
2026
|
|
|
|
|
|
|
sub collectActive { |
2027
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2028
|
0
|
|
|
|
|
0
|
my $label = shift; |
2029
|
0
|
|
|
|
|
0
|
my $value = shift; |
2030
|
|
|
|
|
|
|
|
2031
|
0
|
|
|
|
|
0
|
$o->{status} = 'active'; |
2032
|
|
|
|
|
|
|
} |
2033
|
|
|
|
|
|
|
|
2034
|
|
|
|
|
|
|
sub collectActor { |
2035
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2036
|
0
|
|
|
|
|
0
|
my $label = shift; |
2037
|
0
|
|
|
|
|
0
|
my $value = shift; |
2038
|
|
|
|
|
|
|
|
2039
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value; |
2040
|
|
|
|
|
|
|
} |
2041
|
|
|
|
|
|
|
|
2042
|
|
|
|
|
|
|
sub collectActor1 { |
2043
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2044
|
0
|
|
|
|
|
0
|
my $label = shift; |
2045
|
0
|
|
|
|
|
0
|
my $value = shift; |
2046
|
|
|
|
|
|
|
|
2047
|
0
|
|
|
|
|
0
|
push @{$o->{actorHashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
2048
|
|
|
|
|
|
|
} |
2049
|
|
|
|
|
|
|
|
2050
|
|
|
|
|
|
|
sub collectBackup { |
2051
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2052
|
0
|
|
|
|
|
0
|
my $label = shift; |
2053
|
0
|
|
|
|
|
0
|
my $value = shift; |
2054
|
|
|
|
|
|
|
|
2055
|
0
|
|
|
|
|
0
|
$o->{status} = 'backup'; |
2056
|
|
|
|
|
|
|
} |
2057
|
|
|
|
|
|
|
|
2058
|
|
|
|
|
|
|
sub collectIdle { |
2059
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2060
|
0
|
|
|
|
|
0
|
my $label = shift; |
2061
|
0
|
|
|
|
|
0
|
my $value = shift; |
2062
|
|
|
|
|
|
|
|
2063
|
0
|
|
|
|
|
0
|
$o->{status} = 'idle'; |
2064
|
|
|
|
|
|
|
} |
2065
|
|
|
|
|
|
|
|
2066
|
|
|
|
|
|
|
sub collectRevoked { |
2067
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2068
|
0
|
|
|
|
|
0
|
my $label = shift; |
2069
|
0
|
|
|
|
|
0
|
my $value = shift; |
2070
|
|
|
|
|
|
|
|
2071
|
0
|
|
|
|
|
0
|
$o->{status} = 'revoked'; |
2072
|
|
|
|
|
|
|
} |
2073
|
|
|
|
|
|
|
|
2074
|
|
|
|
|
|
|
sub collectStore { |
2075
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2076
|
0
|
|
|
|
|
0
|
my $label = shift; |
2077
|
0
|
|
|
|
|
0
|
my $value = shift; |
2078
|
|
|
|
|
|
|
|
2079
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, CDS::AccountToken->new($value, $o->{actorHash}); |
|
0
|
|
|
|
|
0
|
|
2080
|
0
|
|
|
|
|
0
|
delete $o->{actorHash}; |
2081
|
|
|
|
|
|
|
} |
2082
|
|
|
|
|
|
|
|
2083
|
|
|
|
|
|
|
sub new { |
2084
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
2085
|
0
|
|
|
|
|
0
|
my $actor = shift; |
2086
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
2087
|
|
|
|
|
|
|
|
2088
|
|
|
|
|
|
|
# END AUTOGENERATED |
2089
|
|
|
|
|
|
|
|
2090
|
|
|
|
|
|
|
# HTML FOLDER NAME actor-group |
2091
|
|
|
|
|
|
|
# HTML TITLE Actor group |
2092
|
|
|
|
|
|
|
sub help { |
2093
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2094
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2095
|
|
|
|
|
|
|
|
2096
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
2097
|
0
|
|
|
|
|
0
|
$ui->space; |
2098
|
0
|
|
|
|
|
0
|
$ui->command('cds show actor group'); |
2099
|
0
|
|
|
|
|
0
|
$ui->p('Shows all members of our actor group and the entrusted keys.'); |
2100
|
0
|
|
|
|
|
0
|
$ui->space; |
2101
|
0
|
|
|
|
|
0
|
$ui->command('cds join ACCOUNT*'); |
2102
|
0
|
|
|
|
|
0
|
$ui->command('cds join ACTOR on STORE'); |
2103
|
0
|
|
|
|
|
0
|
$ui->p('Adds a member to our actor group. To complete the association, the new member must join us, too.'); |
2104
|
0
|
|
|
|
|
0
|
$ui->space; |
2105
|
0
|
|
|
|
|
0
|
$ui->command('cds set member ACTOR* active'); |
2106
|
0
|
|
|
|
|
0
|
$ui->command('cds set member ACTOR* backup'); |
2107
|
0
|
|
|
|
|
0
|
$ui->command('cds set member ACTOR* idle'); |
2108
|
0
|
|
|
|
|
0
|
$ui->command('cds set member ACTOR* revoked'); |
2109
|
0
|
|
|
|
|
0
|
$ui->p('Changes the status of a member to one of the following:'); |
2110
|
0
|
|
|
|
|
0
|
$ui->p($ui->bold('Active members'), ' share the group data among themselves, and are advertised to receive messages.'); |
2111
|
0
|
|
|
|
|
0
|
$ui->p($ui->bold('Backup members'), ' share the group data (like active members), but are publicly advertised as not processing messages (like idle members). This is suitable for backup actors.'); |
2112
|
0
|
|
|
|
|
0
|
$ui->p($ui->bold('Idle members'), ' are part of the group, but advertised as not processing messages. They generally do not have the latest group data, and may have no group data at all. Idle members may reactivate themselves, or get reactivated by any active member of the group.'); |
2113
|
0
|
|
|
|
|
0
|
$ui->p($ui->bold('Revoked members'), ' have explicitly been removed from the group, e.g. because their private key (or device) got lost. Revoked members can be reactivated by any active member of the group.'); |
2114
|
0
|
|
|
|
|
0
|
$ui->p('Note that changing the status does not start or stop the corresponding actor, but just change how it is regarded by others. The status of each member should reflect its actual behavior.'); |
2115
|
0
|
|
|
|
|
0
|
$ui->space; |
2116
|
0
|
|
|
|
|
0
|
$ui->p('After modifying the actor group members, you should "cds announce" yourself to publish the changes.'); |
2117
|
0
|
|
|
|
|
0
|
$ui->space; |
2118
|
|
|
|
|
|
|
} |
2119
|
|
|
|
|
|
|
|
2120
|
|
|
|
|
|
|
sub show { |
2121
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2122
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2123
|
|
|
|
|
|
|
|
2124
|
0
|
|
|
|
|
0
|
my $hasMembers = 0; |
2125
|
0
|
|
|
|
|
0
|
for my $actorSelector ($o->{actor}->actorGroupSelector->children) { |
2126
|
0
|
|
|
|
|
0
|
my $record = $actorSelector->record; |
2127
|
0
|
|
0
|
|
|
0
|
my $hash = $record->child('hash')->hashValue // next; |
2128
|
0
|
0
|
|
|
|
0
|
next if substr($hash->bytes, 0, length $actorSelector->label) ne $actorSelector->label; |
2129
|
0
|
|
|
|
|
0
|
my $storeUrl = $record->child('store')->textValue; |
2130
|
0
|
|
|
|
|
0
|
my $revisionText = $o->{ui}->niceDateTimeLocal($actorSelector->revision); |
2131
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray($revisionText), ' ', $o->coloredType7($actorSelector), ' ', $hash->hex, ' on ', $storeUrl); |
2132
|
0
|
|
|
|
|
0
|
$hasMembers = 1; |
2133
|
|
|
|
|
|
|
} |
2134
|
|
|
|
|
|
|
|
2135
|
0
|
0
|
|
|
|
0
|
return if $hasMembers; |
2136
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->blue('(just you)')); |
2137
|
|
|
|
|
|
|
} |
2138
|
|
|
|
|
|
|
|
2139
|
|
|
|
|
|
|
sub type { |
2140
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2141
|
0
|
0
|
0
|
|
|
0
|
my $actorSelector = shift; die 'wrong type '.ref($actorSelector).' for $actorSelector' if defined $actorSelector && ref $actorSelector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
0
|
|
2142
|
|
|
|
|
|
|
|
2143
|
0
|
|
|
|
|
0
|
my $groupData = $actorSelector->child('group data')->isSet; |
2144
|
0
|
|
|
|
|
0
|
my $active = $actorSelector->child('active')->isSet; |
2145
|
0
|
|
|
|
|
0
|
my $revoked = $actorSelector->child('revoked')->isSet; |
2146
|
|
|
|
|
|
|
return |
2147
|
0
|
0
|
0
|
|
|
0
|
$revoked ? 'revoked' : |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2148
|
|
|
|
|
|
|
$active && $groupData ? 'active' : |
2149
|
|
|
|
|
|
|
$groupData ? 'backup' : |
2150
|
|
|
|
|
|
|
$active ? 'weird' : |
2151
|
|
|
|
|
|
|
'idle'; |
2152
|
|
|
|
|
|
|
} |
2153
|
|
|
|
|
|
|
|
2154
|
|
|
|
|
|
|
sub coloredType7 { |
2155
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2156
|
0
|
0
|
0
|
|
|
0
|
my $actorSelector = shift; die 'wrong type '.ref($actorSelector).' for $actorSelector' if defined $actorSelector && ref $actorSelector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
0
|
|
2157
|
|
|
|
|
|
|
|
2158
|
0
|
|
|
|
|
0
|
my $groupData = $actorSelector->child('group data')->isSet; |
2159
|
0
|
|
|
|
|
0
|
my $active = $actorSelector->child('active')->isSet; |
2160
|
0
|
|
|
|
|
0
|
my $revoked = $actorSelector->child('revoked')->isSet; |
2161
|
|
|
|
|
|
|
return |
2162
|
|
|
|
|
|
|
$revoked ? $o->{ui}->red('revoked') : |
2163
|
|
|
|
|
|
|
$active && $groupData ? $o->{ui}->green('active ') : |
2164
|
|
|
|
|
|
|
$groupData ? $o->{ui}->blue('backup ') : |
2165
|
|
|
|
|
|
|
$active ? $o->{ui}->orange('weird ') : |
2166
|
0
|
0
|
0
|
|
|
0
|
$o->{ui}->gray('idle '); |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2167
|
|
|
|
|
|
|
} |
2168
|
|
|
|
|
|
|
|
2169
|
|
|
|
|
|
|
sub joinMember { |
2170
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2171
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2172
|
|
|
|
|
|
|
|
2173
|
0
|
|
|
|
|
0
|
$o->{accountTokens} = []; |
2174
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
2175
|
|
|
|
|
|
|
|
2176
|
0
|
|
|
|
|
0
|
my $selector = $o->{actor}->actorGroupSelector; |
2177
|
0
|
|
|
|
|
0
|
for my $accountToken (@{$o->{accountTokens}}) { |
|
0
|
|
|
|
|
0
|
|
2178
|
0
|
|
|
|
|
0
|
my $actorHash = $accountToken->actorHash; |
2179
|
|
|
|
|
|
|
|
2180
|
|
|
|
|
|
|
# Get the public key |
2181
|
0
|
|
|
|
|
0
|
my ($publicKey, $invalidReason, $storeError) = $o->{actor}->keyPair->getPublicKey($actorHash, $accountToken->cliStore); |
2182
|
0
|
0
|
|
|
|
0
|
if (defined $storeError) { |
2183
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('Unable to get the public key of ', $actorHash->hex, ' from ', $accountToken->cliStore->url, ': ', $storeError); |
2184
|
0
|
|
|
|
|
0
|
next; |
2185
|
|
|
|
|
|
|
} |
2186
|
|
|
|
|
|
|
|
2187
|
0
|
0
|
|
|
|
0
|
if (defined $invalidReason) { |
2188
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('Unable to get the public key of ', $actorHash->hex, ' from ', $accountToken->cliStore->url, ': ', $invalidReason); |
2189
|
0
|
|
|
|
|
0
|
next; |
2190
|
|
|
|
|
|
|
} |
2191
|
|
|
|
|
|
|
|
2192
|
|
|
|
|
|
|
# Add or update this member |
2193
|
0
|
|
|
|
|
0
|
my $label = substr($actorHash->bytes, 0, 16); |
2194
|
0
|
|
|
|
|
0
|
my $actorSelector = $selector->child($label); |
2195
|
0
|
|
|
|
|
0
|
my $wasMember = $actorSelector->isSet; |
2196
|
|
|
|
|
|
|
|
2197
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->new; |
2198
|
0
|
|
|
|
|
0
|
$record->add('hash')->addHash($actorHash); |
2199
|
0
|
|
|
|
|
0
|
$record->add('store')->addText($accountToken->cliStore->url); |
2200
|
0
|
|
|
|
|
0
|
$actorSelector->set($record); |
2201
|
0
|
|
|
|
|
0
|
$actorSelector->addObject($publicKey->hash, $publicKey->object); |
2202
|
|
|
|
|
|
|
|
2203
|
0
|
0
|
|
|
|
0
|
$o->{ui}->pGreen('Updated ', $o->type($actorSelector), ' member ', $actorHash->hex, '.') if $wasMember; |
2204
|
0
|
0
|
|
|
|
0
|
$o->{ui}->pGreen('Added ', $actorHash->hex, ' as ', $o->type($actorSelector), ' member of the actor group.') if ! $wasMember; |
2205
|
|
|
|
|
|
|
} |
2206
|
|
|
|
|
|
|
|
2207
|
|
|
|
|
|
|
# Save |
2208
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
2209
|
|
|
|
|
|
|
} |
2210
|
|
|
|
|
|
|
|
2211
|
|
|
|
|
|
|
sub setFlag { |
2212
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2213
|
0
|
0
|
0
|
|
|
0
|
my $actorSelector = shift; die 'wrong type '.ref($actorSelector).' for $actorSelector' if defined $actorSelector && ref $actorSelector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
0
|
|
2214
|
0
|
|
|
|
|
0
|
my $label = shift; |
2215
|
0
|
|
|
|
|
0
|
my $value = shift; |
2216
|
|
|
|
|
|
|
|
2217
|
0
|
|
|
|
|
0
|
my $child = $actorSelector->child($label); |
2218
|
0
|
0
|
|
|
|
0
|
if ($value) { |
2219
|
0
|
|
|
|
|
0
|
$child->setBoolean(1); |
2220
|
|
|
|
|
|
|
} else { |
2221
|
0
|
|
|
|
|
0
|
$child->clear; |
2222
|
|
|
|
|
|
|
} |
2223
|
|
|
|
|
|
|
} |
2224
|
|
|
|
|
|
|
|
2225
|
|
|
|
|
|
|
sub setMember { |
2226
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2227
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2228
|
|
|
|
|
|
|
|
2229
|
0
|
|
|
|
|
0
|
$o->{actorHashes} = []; |
2230
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
2231
|
|
|
|
|
|
|
|
2232
|
0
|
|
|
|
|
0
|
my $selector = $o->{actor}->actorGroupSelector; |
2233
|
0
|
|
|
|
|
0
|
for my $actorHash (@{$o->{actorHashes}}) { |
|
0
|
|
|
|
|
0
|
|
2234
|
0
|
|
|
|
|
0
|
my $label = substr($actorHash->bytes, 0, 16); |
2235
|
0
|
|
|
|
|
0
|
my $actorSelector = $selector->child($label); |
2236
|
|
|
|
|
|
|
|
2237
|
0
|
|
|
|
|
0
|
my $record = $actorSelector->record; |
2238
|
0
|
|
|
|
|
0
|
my $hash = $record->child('hash')->hashValue; |
2239
|
0
|
0
|
|
|
|
0
|
if (! $hash) { |
2240
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed($actorHash->hex, ' is not a member of our actor group.'); |
2241
|
0
|
|
|
|
|
0
|
next; |
2242
|
|
|
|
|
|
|
} |
2243
|
|
|
|
|
|
|
|
2244
|
0
|
|
0
|
|
|
0
|
$o->setFlag($actorSelector, 'group data', $o->{status} eq 'active' || $o->{status} eq 'backup'); |
2245
|
0
|
|
|
|
|
0
|
$o->setFlag($actorSelector, 'active', $o->{status} eq 'active'); |
2246
|
0
|
|
|
|
|
0
|
$o->setFlag($actorSelector, 'revoked', $o->{status} eq 'revoked'); |
2247
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen($actorHash->hex, ' is now ', $o->type($actorSelector), '.'); |
2248
|
|
|
|
|
|
|
} |
2249
|
|
|
|
|
|
|
|
2250
|
|
|
|
|
|
|
# Save |
2251
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
2252
|
|
|
|
|
|
|
} |
2253
|
|
|
|
|
|
|
|
2254
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
2255
|
|
|
|
|
|
|
package CDS::Commands::Announce; |
2256
|
|
|
|
|
|
|
|
2257
|
|
|
|
|
|
|
sub register { |
2258
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
2259
|
0
|
|
|
|
|
0
|
my $cds = shift; |
2260
|
0
|
|
|
|
|
0
|
my $help = shift; |
2261
|
|
|
|
|
|
|
|
2262
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
2263
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&announceMe}); |
2264
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(1); |
2265
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
2266
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
2267
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
2268
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
2269
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
2270
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
2271
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
2272
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
2273
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
2274
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
2275
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(1); |
2276
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(0); |
2277
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(0); |
2278
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(0); |
2279
|
0
|
|
|
|
|
0
|
my $node017 = CDS::Parser::Node->new(0, {constructor => \&new, function => \&announceKeyPair}); |
2280
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'announce'); |
2281
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'announce'); |
2282
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'announce'); |
2283
|
0
|
|
|
|
|
0
|
$node002->addArrow($node003, 1, 0, 'KEYPAIR', \&collectKeypair); |
2284
|
0
|
|
|
|
|
0
|
$node003->addArrow($node004, 1, 0, 'on'); |
2285
|
0
|
|
|
|
|
0
|
$node004->addArrow($node005, 1, 0, 'STORE', \&collectStore); |
2286
|
0
|
|
|
|
|
0
|
$node005->addArrow($node006, 1, 0, 'without'); |
2287
|
0
|
|
|
|
|
0
|
$node005->addArrow($node007, 1, 0, 'with'); |
2288
|
0
|
|
|
|
|
0
|
$node005->addDefault($node017); |
2289
|
0
|
|
|
|
|
0
|
$node006->addArrow($node006, 1, 0, 'ACTOR', \&collectActor); |
2290
|
0
|
|
|
|
|
0
|
$node006->addArrow($node017, 1, 0, 'ACTOR', \&collectActor); |
2291
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'active', \&collectActive); |
2292
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'entrusted', \&collectEntrusted); |
2293
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'idle', \&collectIdle); |
2294
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'revoked', \&collectRevoked); |
2295
|
0
|
|
|
|
|
0
|
$node008->addDefault($node009); |
2296
|
0
|
|
|
|
|
0
|
$node008->addDefault($node010); |
2297
|
0
|
|
|
|
|
0
|
$node009->addArrow($node009, 1, 0, 'ACCOUNT', \&collectAccount); |
2298
|
0
|
|
|
|
|
0
|
$node009->addArrow($node013, 1, 1, 'ACCOUNT', \&collectAccount); |
2299
|
0
|
|
|
|
|
0
|
$node010->addArrow($node010, 1, 0, 'ACTOR', \&collectActor1); |
2300
|
0
|
|
|
|
|
0
|
$node010->addArrow($node011, 1, 0, 'ACTOR', \&collectActor1); |
2301
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'on'); |
2302
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'STORE', \&collectStore1); |
2303
|
0
|
|
|
|
|
0
|
$node013->addArrow($node014, 1, 0, 'but'); |
2304
|
0
|
|
|
|
|
0
|
$node013->addArrow($node016, 1, 0, 'and'); |
2305
|
0
|
|
|
|
|
0
|
$node013->addDefault($node017); |
2306
|
0
|
|
|
|
|
0
|
$node014->addArrow($node015, 1, 0, 'without'); |
2307
|
0
|
|
|
|
|
0
|
$node015->addArrow($node015, 1, 0, 'ACTOR', \&collectActor); |
2308
|
0
|
|
|
|
|
0
|
$node015->addArrow($node017, 1, 0, 'ACTOR', \&collectActor); |
2309
|
0
|
|
|
|
|
0
|
$node016->addDefault($node007); |
2310
|
|
|
|
|
|
|
} |
2311
|
|
|
|
|
|
|
|
2312
|
|
|
|
|
|
|
sub collectAccount { |
2313
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2314
|
0
|
|
|
|
|
0
|
my $label = shift; |
2315
|
0
|
|
|
|
|
0
|
my $value = shift; |
2316
|
|
|
|
|
|
|
|
2317
|
0
|
|
|
|
|
0
|
push @{$o->{with}}, {status => $o->{status}, accountToken => $value}; |
|
0
|
|
|
|
|
0
|
|
2318
|
|
|
|
|
|
|
} |
2319
|
|
|
|
|
|
|
|
2320
|
|
|
|
|
|
|
sub collectActive { |
2321
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2322
|
0
|
|
|
|
|
0
|
my $label = shift; |
2323
|
0
|
|
|
|
|
0
|
my $value = shift; |
2324
|
|
|
|
|
|
|
|
2325
|
0
|
|
|
|
|
0
|
$o->{status} = 'active'; |
2326
|
|
|
|
|
|
|
} |
2327
|
|
|
|
|
|
|
|
2328
|
|
|
|
|
|
|
sub collectActor { |
2329
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2330
|
0
|
|
|
|
|
0
|
my $label = shift; |
2331
|
0
|
|
|
|
|
0
|
my $value = shift; |
2332
|
|
|
|
|
|
|
|
2333
|
0
|
|
|
|
|
0
|
$o->{without}->{$value->bytes} = $value; |
2334
|
|
|
|
|
|
|
} |
2335
|
|
|
|
|
|
|
|
2336
|
|
|
|
|
|
|
sub collectActor1 { |
2337
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2338
|
0
|
|
|
|
|
0
|
my $label = shift; |
2339
|
0
|
|
|
|
|
0
|
my $value = shift; |
2340
|
|
|
|
|
|
|
|
2341
|
0
|
|
|
|
|
0
|
push @{$o->{actorHashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
2342
|
|
|
|
|
|
|
} |
2343
|
|
|
|
|
|
|
|
2344
|
|
|
|
|
|
|
sub collectEntrusted { |
2345
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2346
|
0
|
|
|
|
|
0
|
my $label = shift; |
2347
|
0
|
|
|
|
|
0
|
my $value = shift; |
2348
|
|
|
|
|
|
|
|
2349
|
0
|
|
|
|
|
0
|
$o->{status} = 'entrusted'; |
2350
|
|
|
|
|
|
|
} |
2351
|
|
|
|
|
|
|
|
2352
|
|
|
|
|
|
|
sub collectIdle { |
2353
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2354
|
0
|
|
|
|
|
0
|
my $label = shift; |
2355
|
0
|
|
|
|
|
0
|
my $value = shift; |
2356
|
|
|
|
|
|
|
|
2357
|
0
|
|
|
|
|
0
|
$o->{status} = 'idle'; |
2358
|
|
|
|
|
|
|
} |
2359
|
|
|
|
|
|
|
|
2360
|
|
|
|
|
|
|
sub collectKeypair { |
2361
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2362
|
0
|
|
|
|
|
0
|
my $label = shift; |
2363
|
0
|
|
|
|
|
0
|
my $value = shift; |
2364
|
|
|
|
|
|
|
|
2365
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
2366
|
|
|
|
|
|
|
} |
2367
|
|
|
|
|
|
|
|
2368
|
|
|
|
|
|
|
sub collectRevoked { |
2369
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2370
|
0
|
|
|
|
|
0
|
my $label = shift; |
2371
|
0
|
|
|
|
|
0
|
my $value = shift; |
2372
|
|
|
|
|
|
|
|
2373
|
0
|
|
|
|
|
0
|
$o->{status} = 'revoked'; |
2374
|
|
|
|
|
|
|
} |
2375
|
|
|
|
|
|
|
|
2376
|
|
|
|
|
|
|
sub collectStore { |
2377
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2378
|
0
|
|
|
|
|
0
|
my $label = shift; |
2379
|
0
|
|
|
|
|
0
|
my $value = shift; |
2380
|
|
|
|
|
|
|
|
2381
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
2382
|
|
|
|
|
|
|
} |
2383
|
|
|
|
|
|
|
|
2384
|
|
|
|
|
|
|
sub collectStore1 { |
2385
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2386
|
0
|
|
|
|
|
0
|
my $label = shift; |
2387
|
0
|
|
|
|
|
0
|
my $value = shift; |
2388
|
|
|
|
|
|
|
|
2389
|
0
|
|
|
|
|
0
|
for my $actorHash (@{$o->{actorHashes}}) { |
|
0
|
|
|
|
|
0
|
|
2390
|
0
|
|
|
|
|
0
|
my $accountToken = CDS::AccountToken->new($value, $actorHash); |
2391
|
0
|
|
|
|
|
0
|
push @{$o->{with}}, {status => $o->{status}, accountToken => $accountToken}; |
|
0
|
|
|
|
|
0
|
|
2392
|
|
|
|
|
|
|
} |
2393
|
|
|
|
|
|
|
|
2394
|
0
|
|
|
|
|
0
|
$o->{actorHashes} = []; |
2395
|
|
|
|
|
|
|
} |
2396
|
|
|
|
|
|
|
|
2397
|
|
|
|
|
|
|
sub new { |
2398
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
2399
|
0
|
|
|
|
|
0
|
my $actor = shift; |
2400
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
2401
|
|
|
|
|
|
|
|
2402
|
|
|
|
|
|
|
# END AUTOGENERATED |
2403
|
|
|
|
|
|
|
|
2404
|
|
|
|
|
|
|
# HTML FOLDER NAME announce |
2405
|
|
|
|
|
|
|
# HTML TITLE Announce |
2406
|
|
|
|
|
|
|
sub help { |
2407
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2408
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2409
|
|
|
|
|
|
|
|
2410
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
2411
|
0
|
|
|
|
|
0
|
$ui->space; |
2412
|
0
|
|
|
|
|
0
|
$ui->command('cds announce'); |
2413
|
0
|
|
|
|
|
0
|
$ui->p('Announces yourself on your accounts.'); |
2414
|
0
|
|
|
|
|
0
|
$ui->space; |
2415
|
0
|
|
|
|
|
0
|
$ui->command('cds announce KEYPAIR on STORE'); |
2416
|
0
|
|
|
|
|
0
|
$ui->command('… with (active|idle|revoked|entrusted) ACCOUNT*'); |
2417
|
0
|
|
|
|
|
0
|
$ui->command('… with (active|idle|revoked|entrusted) ACTOR* on STORE'); |
2418
|
0
|
|
|
|
|
0
|
$ui->command('… without ACTOR*'); |
2419
|
0
|
|
|
|
|
0
|
$ui->command('… with … and … and … but without …'); |
2420
|
0
|
|
|
|
|
0
|
$ui->p('Updates the public card of the indicated key pair on the indicated store. The indicated accounts are added or removed from the actor group on the card.'); |
2421
|
0
|
|
|
|
|
0
|
$ui->p('If no card exists, a minimalistic card is created.'); |
2422
|
0
|
|
|
|
|
0
|
$ui->p('Use this with care, as the generated card may not be compliant with the card produced by the actor.'); |
2423
|
0
|
|
|
|
|
0
|
$ui->space; |
2424
|
|
|
|
|
|
|
} |
2425
|
|
|
|
|
|
|
|
2426
|
|
|
|
|
|
|
sub announceMe { |
2427
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2428
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2429
|
|
|
|
|
|
|
|
2430
|
0
|
|
|
|
|
0
|
$o->announceOnStore($o->{actor}->storageStore); |
2431
|
0
|
0
|
|
|
|
0
|
$o->announceOnStore($o->{actor}->messagingStore) if $o->{actor}->messagingStore->id ne $o->{actor}->storageStore->id; |
2432
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
2433
|
|
|
|
|
|
|
} |
2434
|
|
|
|
|
|
|
|
2435
|
|
|
|
|
|
|
sub announceOnStore { |
2436
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2437
|
0
|
|
|
|
|
0
|
my $store = shift; |
2438
|
|
|
|
|
|
|
|
2439
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
2440
|
0
|
|
|
|
|
0
|
$o->{ui}->title($store->url); |
2441
|
0
|
|
|
|
|
0
|
my ($envelopeHash, $cardHash, $invalidReason, $storeError) = $o->{actor}->announce($store); |
2442
|
0
|
0
|
|
|
|
0
|
return if defined $storeError; |
2443
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->pRed($invalidReason) if defined $invalidReason; |
2444
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Announced'); |
2445
|
|
|
|
|
|
|
} |
2446
|
|
|
|
|
|
|
|
2447
|
|
|
|
|
|
|
sub announceKeyPair { |
2448
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2449
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2450
|
|
|
|
|
|
|
|
2451
|
0
|
|
|
|
|
0
|
$o->{actors} = []; |
2452
|
0
|
|
|
|
|
0
|
$o->{with} = []; |
2453
|
0
|
|
|
|
|
0
|
$o->{without} = {}; |
2454
|
0
|
|
|
|
|
0
|
$o->{now} = CDS->now; |
2455
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
2456
|
|
|
|
|
|
|
|
2457
|
|
|
|
|
|
|
# List |
2458
|
0
|
|
|
|
|
0
|
$o->{keyPair} = $o->{keyPairToken}->keyPair; |
2459
|
0
|
|
|
|
|
0
|
my ($hashes, $listError) = $o->{store}->list($o->{keyPair}->publicKey->hash, 'public', 0, $o->{keyPair}); |
2460
|
0
|
0
|
|
|
|
0
|
return if defined $listError; |
2461
|
|
|
|
|
|
|
|
2462
|
|
|
|
|
|
|
# Check if there are more than one cards |
2463
|
0
|
0
|
|
|
|
0
|
if (scalar @$hashes > 1) { |
2464
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
2465
|
0
|
|
|
|
|
0
|
$o->{ui}->p('This account contains more than one public card:'); |
2466
|
0
|
|
|
|
|
0
|
$o->{ui}->pushIndent; |
2467
|
0
|
|
|
|
|
0
|
for my $hash (@$hashes) { |
2468
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds show card ', $hash->hex, ' on ', $o->{storeUrl})); |
2469
|
|
|
|
|
|
|
} |
2470
|
0
|
|
|
|
|
0
|
$o->{ui}->popIndent; |
2471
|
0
|
|
|
|
|
0
|
$o->{ui}->p('Remove all but the most recent card. Cards can be removed as follows:'); |
2472
|
0
|
|
|
|
|
0
|
my $keyPairReference = $o->{actor}->blueKeyPairReference($o->{keyPairToken}); |
2473
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds remove ', 'HASH', ' on ', $o->{storeUrl}, ' using ', $keyPairReference)); |
2474
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
2475
|
0
|
|
|
|
|
0
|
return; |
2476
|
|
|
|
|
|
|
} |
2477
|
|
|
|
|
|
|
|
2478
|
|
|
|
|
|
|
# Read the card |
2479
|
0
|
0
|
|
|
|
0
|
my $cardRecord = scalar @$hashes ? $o->readCard($hashes->[0]) : CDS::Record->new; |
2480
|
0
|
0
|
|
|
|
0
|
return if ! $cardRecord; |
2481
|
|
|
|
|
|
|
|
2482
|
|
|
|
|
|
|
# Parse |
2483
|
0
|
|
|
|
|
0
|
my $builder = CDS::ActorGroupBuilder->new; |
2484
|
0
|
|
|
|
|
0
|
$builder->parse($cardRecord, 0); |
2485
|
|
|
|
|
|
|
|
2486
|
|
|
|
|
|
|
# Apply the changes |
2487
|
0
|
|
|
|
|
0
|
for my $change (@{$o->{with}}) { |
|
0
|
|
|
|
|
0
|
|
2488
|
0
|
0
|
|
|
|
0
|
if ($change->{status} eq 'entrusted') { |
2489
|
0
|
|
|
|
|
0
|
$builder->addEntrustedActor($change->{accountToken}->cliStore->url, $change->{accountToken}->actorHash); |
2490
|
0
|
|
|
|
|
0
|
$builder->{entrustedActorsRevision} = $o->{now}; |
2491
|
|
|
|
|
|
|
} else { |
2492
|
0
|
|
|
|
|
0
|
$builder->addMember($change->{accountToken}->cliStore->url, $change->{accountToken}->actorHash, $o->{now}, $change->{status}); |
2493
|
|
|
|
|
|
|
} |
2494
|
|
|
|
|
|
|
} |
2495
|
|
|
|
|
|
|
|
2496
|
0
|
|
|
|
|
0
|
for my $hash (values %{$o->{without}}) { |
|
0
|
|
|
|
|
0
|
|
2497
|
0
|
|
|
|
|
0
|
$builder->removeEntrustedActor($hash) |
2498
|
|
|
|
|
|
|
} |
2499
|
|
|
|
|
|
|
|
2500
|
0
|
|
|
|
|
0
|
for my $member ($builder->members) { |
2501
|
0
|
0
|
|
|
|
0
|
next if ! $o->{without}->{$member->hash->bytes}; |
2502
|
0
|
|
|
|
|
0
|
$builder->removeMember($member->storeUrl, $member->hash); |
2503
|
|
|
|
|
|
|
} |
2504
|
|
|
|
|
|
|
|
2505
|
|
|
|
|
|
|
# Write the new card |
2506
|
0
|
|
|
|
|
0
|
my $newCard = $builder->toRecord(0); |
2507
|
0
|
|
|
|
|
0
|
$newCard->add('public key')->addHash($o->{keyPair}->publicKey->hash); |
2508
|
|
|
|
|
|
|
|
2509
|
0
|
|
|
|
|
0
|
for my $child ($cardRecord->children) { |
2510
|
0
|
0
|
|
|
|
0
|
if ($child->bytes eq 'actor group') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2511
|
|
|
|
|
|
|
} elsif ($child->bytes eq 'entrusted actors') { |
2512
|
|
|
|
|
|
|
} elsif ($child->bytes eq 'public key') { |
2513
|
|
|
|
|
|
|
} else { |
2514
|
0
|
|
|
|
|
0
|
$newCard->addRecord($child); |
2515
|
|
|
|
|
|
|
} |
2516
|
|
|
|
|
|
|
} |
2517
|
|
|
|
|
|
|
|
2518
|
0
|
|
|
|
|
0
|
$o->announce($newCard, $hashes); |
2519
|
|
|
|
|
|
|
} |
2520
|
|
|
|
|
|
|
|
2521
|
|
|
|
|
|
|
sub readCard { |
2522
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2523
|
0
|
0
|
0
|
|
|
0
|
my $envelopeHash = shift; die 'wrong type '.ref($envelopeHash).' for $envelopeHash' if defined $envelopeHash && ref $envelopeHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
2524
|
|
|
|
|
|
|
|
2525
|
|
|
|
|
|
|
# Open the envelope |
2526
|
0
|
|
|
|
|
0
|
my ($object, $storeError) = $o->{store}->get($envelopeHash, $o->{keyPair}); |
2527
|
0
|
0
|
|
|
|
0
|
return if defined $storeError; |
2528
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('Envelope object ', $envelopeHash->hex, ' not found.') if ! $object; |
2529
|
|
|
|
|
|
|
|
2530
|
0
|
|
0
|
|
|
0
|
my $envelope = CDS::Record->fromObject($object) // return $o->{ui}->error($envelopeHash->hex, ' is not a record.'); |
2531
|
0
|
|
0
|
|
|
0
|
my $cardHash = $envelope->child('content')->hashValue // return $o->{ui}->error($envelopeHash->hex, ' is not a valid envelope, because it has no content hash.'); |
2532
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error($envelopeHash->hex, ' has an invalid signature.') if ! CDS->verifyEnvelopeSignature($envelope, $o->{keyPair}->publicKey, $cardHash); |
2533
|
|
|
|
|
|
|
|
2534
|
|
|
|
|
|
|
# Read the card |
2535
|
0
|
|
|
|
|
0
|
my ($cardObject, $storeError1) = $o->{store}->get($cardHash, $o->{keyPair}); |
2536
|
0
|
0
|
|
|
|
0
|
return if defined $storeError1; |
2537
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('Card object ', $cardHash->hex, ' not found.') if ! $cardObject; |
2538
|
|
|
|
|
|
|
|
2539
|
0
|
|
0
|
|
|
0
|
return CDS::Record->fromObject($cardObject) // return $o->{ui}->error($cardHash->hex, ' is not a record.'); |
2540
|
|
|
|
|
|
|
} |
2541
|
|
|
|
|
|
|
|
2542
|
|
|
|
|
|
|
sub applyChanges { |
2543
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2544
|
0
|
0
|
0
|
|
|
0
|
my $actorGroup = shift; die 'wrong type '.ref($actorGroup).' for $actorGroup' if defined $actorGroup && ref $actorGroup ne 'CDS::ActorGroup'; |
|
0
|
|
|
|
|
0
|
|
2545
|
0
|
|
|
|
|
0
|
my $status = shift; |
2546
|
0
|
|
|
|
|
0
|
my $accounts = shift; |
2547
|
|
|
|
|
|
|
|
2548
|
0
|
|
|
|
|
0
|
for my $account (@$accounts) { |
2549
|
0
|
|
|
|
|
0
|
$actorGroup->{$account->url} = {storeUrl => $account->cliStore->url, actorHash => $account->actorHash, revision => $o->{now}, status => $status}; |
2550
|
|
|
|
|
|
|
} |
2551
|
|
|
|
|
|
|
} |
2552
|
|
|
|
|
|
|
|
2553
|
|
|
|
|
|
|
sub announce { |
2554
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2555
|
0
|
|
|
|
|
0
|
my $card = shift; |
2556
|
0
|
|
|
|
|
0
|
my $sourceHashes = shift; |
2557
|
|
|
|
|
|
|
|
2558
|
0
|
|
|
|
|
0
|
my $inMemoryStore = CDS::InMemoryStore->create; |
2559
|
|
|
|
|
|
|
|
2560
|
|
|
|
|
|
|
# Serialize the card |
2561
|
0
|
|
|
|
|
0
|
my $cardObject = $card->toObject; |
2562
|
0
|
|
|
|
|
0
|
my $cardHash = $cardObject->calculateHash; |
2563
|
0
|
|
|
|
|
0
|
$inMemoryStore->put($cardHash, $cardObject); |
2564
|
0
|
|
|
|
|
0
|
$inMemoryStore->put($o->{keyPair}->publicKey->hash, $o->{keyPair}->publicKey->object); |
2565
|
|
|
|
|
|
|
|
2566
|
|
|
|
|
|
|
# Prepare the public envelope |
2567
|
0
|
|
|
|
|
0
|
my $envelopeObject = $o->{keyPair}->createPublicEnvelope($cardHash)->toObject; |
2568
|
0
|
|
|
|
|
0
|
my $envelopeHash = $envelopeObject->calculateHash; |
2569
|
0
|
|
|
|
|
0
|
$inMemoryStore->put($envelopeHash, $envelopeObject); |
2570
|
|
|
|
|
|
|
|
2571
|
|
|
|
|
|
|
# Transfer |
2572
|
0
|
|
|
|
|
0
|
my ($missingHash, $failedStore, $storeError) = $o->{keyPair}->transfer([$envelopeHash], $inMemoryStore, $o->{store}); |
2573
|
0
|
0
|
|
|
|
0
|
return if $storeError; |
2574
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->pRed('Object ', $missingHash, ' is missing.') if $missingHash; |
2575
|
|
|
|
|
|
|
|
2576
|
|
|
|
|
|
|
# Modify |
2577
|
0
|
|
|
|
|
0
|
my $modifications = CDS::StoreModifications->new; |
2578
|
0
|
|
|
|
|
0
|
$modifications->add($o->{keyPair}->publicKey->hash, 'public', $envelopeHash); |
2579
|
0
|
|
|
|
|
0
|
for my $hash (@$sourceHashes) { |
2580
|
0
|
|
|
|
|
0
|
$modifications->remove($o->{keyPair}->publicKey->hash, 'public', $hash); |
2581
|
|
|
|
|
|
|
} |
2582
|
|
|
|
|
|
|
|
2583
|
0
|
|
|
|
|
0
|
my $modifyError = $o->{store}->modify($modifications, $o->{keyPair}); |
2584
|
0
|
0
|
|
|
|
0
|
return if $modifyError; |
2585
|
|
|
|
|
|
|
|
2586
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Announced on ', $o->{store}->url, '.'); |
2587
|
|
|
|
|
|
|
} |
2588
|
|
|
|
|
|
|
|
2589
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
2590
|
|
|
|
|
|
|
package CDS::Commands::Book; |
2591
|
|
|
|
|
|
|
|
2592
|
|
|
|
|
|
|
sub register { |
2593
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
2594
|
0
|
|
|
|
|
0
|
my $cds = shift; |
2595
|
0
|
|
|
|
|
0
|
my $help = shift; |
2596
|
|
|
|
|
|
|
|
2597
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
2598
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
2599
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
2600
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
2601
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
2602
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
2603
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&book}); |
2604
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 1, 0, 'book'); |
2605
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'book'); |
2606
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'book'); |
2607
|
0
|
|
|
|
|
0
|
$help->addArrow($node003, 1, 0, 'book'); |
2608
|
0
|
|
|
|
|
0
|
$node000->addArrow($node000, 1, 0, 'HASH', \&collectHash); |
2609
|
0
|
|
|
|
|
0
|
$node000->addArrow($node004, 1, 0, 'HASH', \&collectHash); |
2610
|
0
|
|
|
|
|
0
|
$node001->addArrow($node001, 1, 0, 'OBJECT', \&collectObject); |
2611
|
0
|
|
|
|
|
0
|
$node001->addArrow($node006, 1, 0, 'OBJECT', \&collectObject); |
2612
|
0
|
|
|
|
|
0
|
$node002->addArrow($node002, 1, 0, 'HASH', \&collectHash); |
2613
|
0
|
|
|
|
|
0
|
$node002->addArrow($node006, 1, 0, 'HASH', \&collectHash); |
2614
|
0
|
|
|
|
|
0
|
$node004->addArrow($node005, 1, 0, 'on'); |
2615
|
0
|
|
|
|
|
0
|
$node005->addArrow($node005, 1, 0, 'STORE', \&collectStore); |
2616
|
0
|
|
|
|
|
0
|
$node005->addArrow($node006, 1, 0, 'STORE', \&collectStore); |
2617
|
|
|
|
|
|
|
} |
2618
|
|
|
|
|
|
|
|
2619
|
|
|
|
|
|
|
sub collectHash { |
2620
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2621
|
0
|
|
|
|
|
0
|
my $label = shift; |
2622
|
0
|
|
|
|
|
0
|
my $value = shift; |
2623
|
|
|
|
|
|
|
|
2624
|
0
|
|
|
|
|
0
|
push @{$o->{hashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
2625
|
|
|
|
|
|
|
} |
2626
|
|
|
|
|
|
|
|
2627
|
|
|
|
|
|
|
sub collectObject { |
2628
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2629
|
0
|
|
|
|
|
0
|
my $label = shift; |
2630
|
0
|
|
|
|
|
0
|
my $value = shift; |
2631
|
|
|
|
|
|
|
|
2632
|
0
|
|
|
|
|
0
|
push @{$o->{objectTokens}}, $value; |
|
0
|
|
|
|
|
0
|
|
2633
|
|
|
|
|
|
|
} |
2634
|
|
|
|
|
|
|
|
2635
|
|
|
|
|
|
|
sub collectStore { |
2636
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2637
|
0
|
|
|
|
|
0
|
my $label = shift; |
2638
|
0
|
|
|
|
|
0
|
my $value = shift; |
2639
|
|
|
|
|
|
|
|
2640
|
0
|
|
|
|
|
0
|
push @{$o->{stores}}, $value; |
|
0
|
|
|
|
|
0
|
|
2641
|
|
|
|
|
|
|
} |
2642
|
|
|
|
|
|
|
|
2643
|
|
|
|
|
|
|
sub new { |
2644
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
2645
|
0
|
|
|
|
|
0
|
my $actor = shift; |
2646
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
2647
|
|
|
|
|
|
|
|
2648
|
|
|
|
|
|
|
# END AUTOGENERATED |
2649
|
|
|
|
|
|
|
|
2650
|
|
|
|
|
|
|
# HTML FOLDER NAME store-book |
2651
|
|
|
|
|
|
|
# HTML TITLE Book |
2652
|
|
|
|
|
|
|
sub help { |
2653
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2654
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2655
|
|
|
|
|
|
|
|
2656
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
2657
|
0
|
|
|
|
|
0
|
$ui->space; |
2658
|
0
|
|
|
|
|
0
|
$ui->command('cds book OBJECT*'); |
2659
|
0
|
|
|
|
|
0
|
$ui->command('cds book HASH* on STORE*'); |
2660
|
0
|
|
|
|
|
0
|
$ui->p('Books all indicated objects and reports whether booking as successful.'); |
2661
|
0
|
|
|
|
|
0
|
$ui->space; |
2662
|
0
|
|
|
|
|
0
|
$ui->command('cds book HASH*'); |
2663
|
0
|
|
|
|
|
0
|
$ui->p('As above, but uses the selected store.'); |
2664
|
0
|
|
|
|
|
0
|
$ui->space; |
2665
|
|
|
|
|
|
|
} |
2666
|
|
|
|
|
|
|
|
2667
|
|
|
|
|
|
|
sub book { |
2668
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2669
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2670
|
|
|
|
|
|
|
|
2671
|
0
|
|
|
|
|
0
|
$o->{keyPair} = $o->{actor}->preferredKeyPairToken->keyPair; |
2672
|
0
|
|
|
|
|
0
|
$o->{hashes} = []; |
2673
|
0
|
|
|
|
|
0
|
$o->{stores} = []; |
2674
|
0
|
|
|
|
|
0
|
$o->{objectTokens} = []; |
2675
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
2676
|
|
|
|
|
|
|
|
2677
|
|
|
|
|
|
|
# Use the selected store |
2678
|
0
|
0
|
|
|
|
0
|
push @{$o->{stores}}, $o->{actor}->preferredStore if ! scalar @{$o->{stores}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
2679
|
|
|
|
|
|
|
|
2680
|
|
|
|
|
|
|
# Book all hashes on all stores |
2681
|
0
|
|
|
|
|
0
|
my %triedStores; |
2682
|
0
|
|
|
|
|
0
|
for my $store (@{$o->{stores}}) { |
|
0
|
|
|
|
|
0
|
|
2683
|
0
|
0
|
|
|
|
0
|
next if $triedStores{$store->url}; |
2684
|
0
|
|
|
|
|
0
|
$triedStores{$store->url} = 1; |
2685
|
0
|
|
|
|
|
0
|
for my $hash (@{$o->{hashes}}) { |
|
0
|
|
|
|
|
0
|
|
2686
|
0
|
|
|
|
|
0
|
$o->process($store, $hash); |
2687
|
|
|
|
|
|
|
} |
2688
|
|
|
|
|
|
|
} |
2689
|
|
|
|
|
|
|
|
2690
|
|
|
|
|
|
|
# Book the direct object references |
2691
|
0
|
|
|
|
|
0
|
for my $objectToken (@{$o->{objectTokens}}) { |
|
0
|
|
|
|
|
0
|
|
2692
|
0
|
|
|
|
|
0
|
$o->process($objectToken->cliStore, $objectToken->hash); |
2693
|
|
|
|
|
|
|
} |
2694
|
|
|
|
|
|
|
|
2695
|
|
|
|
|
|
|
# Warn the user if no key pair is selected |
2696
|
0
|
0
|
|
|
|
0
|
return if ! $o->{hasErrors}; |
2697
|
0
|
0
|
|
|
|
0
|
return if $o->{keyPair}; |
2698
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
2699
|
0
|
|
|
|
|
0
|
$o->{ui}->warning('Since no key pair is selected, the bookings were requested without signature. Stores are more likely to accept signed bookings. To add a signature, select a key pair using "cds use …", or create your key pair using "cds create my key pair".'); |
2700
|
|
|
|
|
|
|
} |
2701
|
|
|
|
|
|
|
|
2702
|
|
|
|
|
|
|
sub process { |
2703
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2704
|
0
|
|
|
|
|
0
|
my $store = shift; |
2705
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
2706
|
|
|
|
|
|
|
|
2707
|
|
|
|
|
|
|
# Upload the object |
2708
|
0
|
|
|
|
|
0
|
my $success = $store->book($hash, $o->{keyPair}); |
2709
|
0
|
0
|
|
|
|
0
|
if ($success) { |
2710
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->green('OK '), $hash->hex, ' on ', $store->url); |
2711
|
|
|
|
|
|
|
} else { |
2712
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->red('not found '), $hash->hex, ' on ', $store->url); |
2713
|
0
|
|
|
|
|
0
|
$o->{hasErrors} = 1; |
2714
|
|
|
|
|
|
|
} |
2715
|
|
|
|
|
|
|
} |
2716
|
|
|
|
|
|
|
|
2717
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
2718
|
|
|
|
|
|
|
package CDS::Commands::CheckKeyPair; |
2719
|
|
|
|
|
|
|
|
2720
|
|
|
|
|
|
|
sub register { |
2721
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
2722
|
0
|
|
|
|
|
0
|
my $cds = shift; |
2723
|
0
|
|
|
|
|
0
|
my $help = shift; |
2724
|
|
|
|
|
|
|
|
2725
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
2726
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
2727
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
2728
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
2729
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
2730
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
2731
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
2732
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
2733
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
2734
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
2735
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
2736
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&checkKeyPair}); |
2737
|
0
|
|
|
|
|
0
|
$cds->addArrow($node004, 1, 0, 'check'); |
2738
|
0
|
|
|
|
|
0
|
$cds->addArrow($node005, 1, 0, 'fix'); |
2739
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'check'); |
2740
|
0
|
|
|
|
|
0
|
$help->addArrow($node001, 1, 0, 'fix'); |
2741
|
0
|
|
|
|
|
0
|
$node000->addArrow($node002, 1, 0, 'key'); |
2742
|
0
|
|
|
|
|
0
|
$node001->addArrow($node003, 1, 0, 'key'); |
2743
|
0
|
|
|
|
|
0
|
$node002->addArrow($node010, 1, 0, 'pair'); |
2744
|
0
|
|
|
|
|
0
|
$node003->addArrow($node010, 1, 0, 'pair'); |
2745
|
0
|
|
|
|
|
0
|
$node004->addArrow($node006, 1, 0, 'key'); |
2746
|
0
|
|
|
|
|
0
|
$node005->addArrow($node007, 1, 0, 'key'); |
2747
|
0
|
|
|
|
|
0
|
$node006->addArrow($node008, 1, 0, 'pair'); |
2748
|
0
|
|
|
|
|
0
|
$node007->addArrow($node009, 1, 0, 'pair'); |
2749
|
0
|
|
|
|
|
0
|
$node008->addArrow($node011, 1, 0, 'FILE', \&collectFile); |
2750
|
0
|
|
|
|
|
0
|
$node009->addArrow($node011, 1, 0, 'FILE', \&collectFile1); |
2751
|
|
|
|
|
|
|
} |
2752
|
|
|
|
|
|
|
|
2753
|
|
|
|
|
|
|
sub collectFile { |
2754
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2755
|
0
|
|
|
|
|
0
|
my $label = shift; |
2756
|
0
|
|
|
|
|
0
|
my $value = shift; |
2757
|
|
|
|
|
|
|
|
2758
|
0
|
|
|
|
|
0
|
$o->{file} = $value; |
2759
|
|
|
|
|
|
|
} |
2760
|
|
|
|
|
|
|
|
2761
|
|
|
|
|
|
|
sub collectFile1 { |
2762
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2763
|
0
|
|
|
|
|
0
|
my $label = shift; |
2764
|
0
|
|
|
|
|
0
|
my $value = shift; |
2765
|
|
|
|
|
|
|
|
2766
|
0
|
|
|
|
|
0
|
$o->{file} = $value; |
2767
|
0
|
|
|
|
|
0
|
$o->{fix} = 1; |
2768
|
|
|
|
|
|
|
} |
2769
|
|
|
|
|
|
|
|
2770
|
|
|
|
|
|
|
sub new { |
2771
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
2772
|
0
|
|
|
|
|
0
|
my $actor = shift; |
2773
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
2774
|
|
|
|
|
|
|
|
2775
|
|
|
|
|
|
|
# END AUTOGENERATED |
2776
|
|
|
|
|
|
|
|
2777
|
|
|
|
|
|
|
# HTML FOLDER NAME check-key-pair |
2778
|
|
|
|
|
|
|
# HTML TITLE Check key pair |
2779
|
|
|
|
|
|
|
sub help { |
2780
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2781
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2782
|
|
|
|
|
|
|
|
2783
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
2784
|
0
|
|
|
|
|
0
|
$ui->space; |
2785
|
0
|
|
|
|
|
0
|
$ui->command('cds check key pair FILE'); |
2786
|
0
|
|
|
|
|
0
|
$ui->p('Checks if the key pair FILE is complete, i.e. that a valid private key and a matching public key exists.'); |
2787
|
0
|
|
|
|
|
0
|
$ui->space; |
2788
|
|
|
|
|
|
|
} |
2789
|
|
|
|
|
|
|
|
2790
|
|
|
|
|
|
|
sub checkKeyPair { |
2791
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2792
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2793
|
|
|
|
|
|
|
|
2794
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
2795
|
|
|
|
|
|
|
|
2796
|
|
|
|
|
|
|
# Check if we have a complete private key |
2797
|
0
|
|
0
|
|
|
0
|
my $bytes = CDS->readBytesFromFile($o->{file}) // return $o->{ui}->error('The file "', $o->{file}, '" cannot be read.'); |
2798
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->fromObject(CDS::Object->fromBytes($bytes)); |
2799
|
|
|
|
|
|
|
|
2800
|
0
|
|
|
|
|
0
|
my $rsaKey = $record->child('rsa key'); |
2801
|
0
|
|
|
|
|
0
|
my $e = $rsaKey->child('e')->bytesValue; |
2802
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The exponent "e" of the private key is missing.') if ! length $e; |
2803
|
0
|
|
|
|
|
0
|
my $p = $rsaKey->child('p')->bytesValue; |
2804
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The prime "p" of the private key is missing.') if ! length $p; |
2805
|
0
|
|
|
|
|
0
|
my $q = $rsaKey->child('q')->bytesValue; |
2806
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The prime "q" of the private key is missing.') if ! length $q; |
2807
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('The private key is complete.'); |
2808
|
|
|
|
|
|
|
|
2809
|
|
|
|
|
|
|
# Derive the public key |
2810
|
0
|
|
|
|
|
0
|
my $privateKey = CDS::C::privateKeyNew($e, $p, $q); |
2811
|
0
|
|
|
|
|
0
|
my $publicKey = CDS::C::publicKeyFromPrivateKey($privateKey); |
2812
|
0
|
|
|
|
|
0
|
my $n = CDS::C::publicKeyN($publicKey); |
2813
|
|
|
|
|
|
|
|
2814
|
|
|
|
|
|
|
# Check if we have a matching public key |
2815
|
0
|
|
|
|
|
0
|
my $publicKeyObjectBytes = $record->child('public key object')->bytesValue; |
2816
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The public key is missing.') if ! length $publicKeyObjectBytes; |
2817
|
0
|
|
0
|
|
|
0
|
$o->{publicKeyObject} = CDS::Object->fromBytes($publicKeyObjectBytes) // return $o->{ui}->error('The public key is is not a valid Condensation object.'); |
2818
|
0
|
|
|
|
|
0
|
$o->{publicKeyHash} = $o->{publicKeyObject}->calculateHash; |
2819
|
0
|
|
|
|
|
0
|
my $publicKeyRecord = CDS::Record->fromObject($o->{publicKeyObject}); |
2820
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The public key is not a valid record.') if ! $publicKeyRecord; |
2821
|
0
|
|
|
|
|
0
|
my $publicN = $publicKeyRecord->child('n')->bytesValue; |
2822
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The modulus "n" of the public key is missing.') if ! length $publicN; |
2823
|
0
|
|
0
|
|
|
0
|
my $publicE = $publicKeyRecord->child('e')->bytesValue // $o->{ui}->error('The public key is incomplete.'); |
2824
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The exponent "e" of the public key is missing.') if ! length $publicE; |
2825
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The exponent "e" of the public key does not match the exponent "e" of the private key.') if $publicE ne $e; |
2826
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The modulus "n" of the public key does not correspond to the primes "p" and "q" of the private key.') if $publicN ne $n; |
2827
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('The public key ', $o->{publicKeyHash}->hex, ' is complete.'); |
2828
|
|
|
|
|
|
|
|
2829
|
|
|
|
|
|
|
# At this point, the configuration looks good, and we can load the key pair |
2830
|
0
|
|
0
|
|
|
0
|
CDS::KeyPair->fromRecord($record) // $o->{ui}->error('Your key pair looks complete, but could not be loaded.'); |
2831
|
|
|
|
|
|
|
} |
2832
|
|
|
|
|
|
|
|
2833
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
2834
|
|
|
|
|
|
|
package CDS::Commands::CollectGarbage; |
2835
|
|
|
|
|
|
|
|
2836
|
|
|
|
|
|
|
sub register { |
2837
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
2838
|
0
|
|
|
|
|
0
|
my $cds = shift; |
2839
|
0
|
|
|
|
|
0
|
my $help = shift; |
2840
|
|
|
|
|
|
|
|
2841
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
2842
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
2843
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
2844
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
2845
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0, {constructor => \&new, function => \&collectGarbage}); |
2846
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
2847
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0, {constructor => \&new, function => \&reportGarbage}); |
2848
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
2849
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&collectGarbage}); |
2850
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&reportGarbage}); |
2851
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'report'); |
2852
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'collect'); |
2853
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'collect'); |
2854
|
0
|
|
|
|
|
0
|
$node000->addArrow($node003, 1, 0, 'garbage'); |
2855
|
0
|
|
|
|
|
0
|
$node001->addArrow($node006, 1, 0, 'garbage'); |
2856
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 1, 0, 'garbage'); |
2857
|
0
|
|
|
|
|
0
|
$node004->addArrow($node005, 1, 0, 'of'); |
2858
|
0
|
|
|
|
|
0
|
$node004->addDefault($node008); |
2859
|
0
|
|
|
|
|
0
|
$node005->addArrow($node008, 1, 0, 'STORE', \&collectStore); |
2860
|
0
|
|
|
|
|
0
|
$node006->addArrow($node007, 1, 0, 'of'); |
2861
|
0
|
|
|
|
|
0
|
$node006->addDefault($node009); |
2862
|
0
|
|
|
|
|
0
|
$node007->addArrow($node009, 1, 0, 'STORE', \&collectStore); |
2863
|
|
|
|
|
|
|
} |
2864
|
|
|
|
|
|
|
|
2865
|
|
|
|
|
|
|
sub collectStore { |
2866
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2867
|
0
|
|
|
|
|
0
|
my $label = shift; |
2868
|
0
|
|
|
|
|
0
|
my $value = shift; |
2869
|
|
|
|
|
|
|
|
2870
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
2871
|
|
|
|
|
|
|
} |
2872
|
|
|
|
|
|
|
|
2873
|
|
|
|
|
|
|
sub new { |
2874
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
2875
|
0
|
|
|
|
|
0
|
my $actor = shift; |
2876
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
2877
|
|
|
|
|
|
|
|
2878
|
|
|
|
|
|
|
# END AUTOGENERATED |
2879
|
|
|
|
|
|
|
|
2880
|
|
|
|
|
|
|
# HTML FOLDER NAME collect-garbage |
2881
|
|
|
|
|
|
|
# HTML TITLE Garbage collection |
2882
|
|
|
|
|
|
|
sub help { |
2883
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2884
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2885
|
|
|
|
|
|
|
|
2886
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
2887
|
0
|
|
|
|
|
0
|
$ui->space; |
2888
|
0
|
|
|
|
|
0
|
$ui->command('cds collect garbage [of STORE]'); |
2889
|
0
|
|
|
|
|
0
|
$ui->p('Runs garbage collection. STORE must be a folder store. Objects not in use, and older than 1 day are removed from the store.'); |
2890
|
0
|
|
|
|
|
0
|
$ui->p('If no store is provided, garbage collection is run on the selected store, or the actor\'s storage store.'); |
2891
|
0
|
|
|
|
|
0
|
$ui->space; |
2892
|
0
|
|
|
|
|
0
|
$ui->p('The store must not be written to while garbage collection is running. Objects booked during garbage collection may get deleted, and leave the store in a corrupt state. Reading from the store is fine.'); |
2893
|
0
|
|
|
|
|
0
|
$ui->space; |
2894
|
0
|
|
|
|
|
0
|
$ui->command('cds report garbage [of STORE]'); |
2895
|
0
|
|
|
|
|
0
|
$ui->p('As above, but reports obsolete objects rather than deleting them. A protocol (shell script) is written to ".garbage" in the store folder.'); |
2896
|
0
|
|
|
|
|
0
|
$ui->space; |
2897
|
|
|
|
|
|
|
} |
2898
|
|
|
|
|
|
|
|
2899
|
|
|
|
|
|
|
sub collectGarbage { |
2900
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2901
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2902
|
|
|
|
|
|
|
|
2903
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
2904
|
0
|
|
|
|
|
0
|
$o->run(CDS::Commands::CollectGarbage::Delete->new($o->{ui})); |
2905
|
|
|
|
|
|
|
} |
2906
|
|
|
|
|
|
|
|
2907
|
|
|
|
|
|
|
sub wrapUpDeletion { |
2908
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2909
|
|
|
|
|
|
|
} |
2910
|
|
|
|
|
|
|
|
2911
|
|
|
|
|
|
|
sub reportGarbage { |
2912
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2913
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
2914
|
|
|
|
|
|
|
|
2915
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
2916
|
0
|
|
|
|
|
0
|
$o->run(CDS::Commands::CollectGarbage::Report->new($o->{ui})); |
2917
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
2918
|
|
|
|
|
|
|
} |
2919
|
|
|
|
|
|
|
|
2920
|
|
|
|
|
|
|
# Creates a folder with the selected permissions. |
2921
|
|
|
|
|
|
|
sub run { |
2922
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
2923
|
0
|
|
|
|
|
0
|
my $handler = shift; |
2924
|
|
|
|
|
|
|
|
2925
|
|
|
|
|
|
|
# Prepare |
2926
|
0
|
|
0
|
|
|
0
|
my $store = $o->{store} // $o->{actor}->selectedStore // $o->{actor}->storageStore; |
|
|
|
0
|
|
|
|
|
2927
|
0
|
|
0
|
|
|
0
|
my $folderStore = CDS::FolderStore->forUrl($store->url) // return $o->{ui}->error('"', $store->url, '" is not a folder store.'); |
2928
|
0
|
|
0
|
|
|
0
|
$handler->initialize($folderStore) // return; |
2929
|
|
|
|
|
|
|
|
2930
|
0
|
|
|
|
|
0
|
$o->{storeFolder} = $folderStore->folder; |
2931
|
0
|
|
|
|
|
0
|
$o->{accountsFolder} = $folderStore->folder.'/accounts'; |
2932
|
0
|
|
|
|
|
0
|
$o->{objectsFolder} = $folderStore->folder.'/objects'; |
2933
|
0
|
|
|
|
|
0
|
my $dateLimit = time - 86400; |
2934
|
0
|
|
|
|
|
0
|
my $envelopeExpirationLimit = time * 1000; |
2935
|
|
|
|
|
|
|
|
2936
|
|
|
|
|
|
|
# Read the tree index |
2937
|
0
|
|
|
|
|
0
|
$o->readIndex; |
2938
|
|
|
|
|
|
|
|
2939
|
|
|
|
|
|
|
# Process all accounts |
2940
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
2941
|
0
|
|
|
|
|
0
|
$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'); |
2942
|
0
|
|
|
|
|
0
|
$o->startProgress('accounts'); |
2943
|
0
|
|
|
|
|
0
|
$o->{usedHashes} = {}; |
2944
|
0
|
|
|
|
|
0
|
$o->{missingObjects} = {}; |
2945
|
0
|
|
|
|
|
0
|
$o->{brokenOrigins} = {}; |
2946
|
0
|
|
|
|
|
0
|
my $countAccounts = 0; |
2947
|
0
|
|
|
|
|
0
|
my $countKeptEnvelopes = 0; |
2948
|
0
|
|
|
|
|
0
|
my $countDeletedEnvelopes = 0; |
2949
|
0
|
|
|
|
|
0
|
for my $accountHash (sort { $$a cmp $$b } $folderStore->accounts) { |
|
0
|
|
|
|
|
0
|
|
2950
|
|
|
|
|
|
|
# This would be the private key, but we don't use it right now |
2951
|
0
|
|
|
|
|
0
|
$o->{usedHashes}->{$accountHash->hex} = 1; |
2952
|
|
|
|
|
|
|
|
2953
|
0
|
|
|
|
|
0
|
my $newestDate = 0; |
2954
|
0
|
|
|
|
|
0
|
my %sizeByBox; |
2955
|
0
|
|
|
|
|
0
|
my $accountFolder = $o->{accountsFolder}.'/'.$accountHash->hex; |
2956
|
0
|
|
|
|
|
0
|
foreach my $boxLabel (CDS->listFolder($accountFolder)) { |
2957
|
0
|
0
|
|
|
|
0
|
next if $boxLabel =~ /^\./; |
2958
|
0
|
|
|
|
|
0
|
my $boxFolder = $accountFolder.'/'.$boxLabel; |
2959
|
0
|
|
|
|
|
0
|
my $date = &lastModified($boxFolder); |
2960
|
0
|
0
|
|
|
|
0
|
$newestDate = $date if $newestDate < $date; |
2961
|
0
|
|
|
|
|
0
|
my $size = 0; |
2962
|
0
|
|
|
|
|
0
|
foreach my $filename (CDS->listFolder($boxFolder)) { |
2963
|
0
|
0
|
|
|
|
0
|
next if $filename =~ /^\./; |
2964
|
0
|
|
|
|
|
0
|
my $hash = pack('H*', $filename); |
2965
|
0
|
|
|
|
|
0
|
my $file = $boxFolder.'/'.$filename; |
2966
|
|
|
|
|
|
|
|
2967
|
0
|
|
|
|
|
0
|
my $timestamp = $o->envelopeExpiration($hash, $boxFolder); |
2968
|
0
|
0
|
0
|
|
|
0
|
if ($timestamp > 0 && $timestamp < $envelopeExpirationLimit) { |
2969
|
0
|
|
|
|
|
0
|
$countDeletedEnvelopes += 1; |
2970
|
0
|
|
0
|
|
|
0
|
$handler->deleteEnvelope($file) // return; |
2971
|
0
|
|
|
|
|
0
|
next; |
2972
|
|
|
|
|
|
|
} |
2973
|
|
|
|
|
|
|
|
2974
|
0
|
|
|
|
|
0
|
$countKeptEnvelopes += 1; |
2975
|
0
|
|
|
|
|
0
|
my $date = &lastModified($file); |
2976
|
0
|
0
|
|
|
|
0
|
$newestDate = $date if $newestDate < $date; |
2977
|
0
|
|
|
|
|
0
|
$size += $o->traverse($hash, $boxFolder); |
2978
|
|
|
|
|
|
|
} |
2979
|
0
|
|
|
|
|
0
|
$sizeByBox{$boxLabel} = $size; |
2980
|
|
|
|
|
|
|
} |
2981
|
|
|
|
|
|
|
|
2982
|
|
|
|
|
|
|
$o->{ui}->line($accountHash->hex, ' ', |
2983
|
|
|
|
|
|
|
$o->{ui}->right(10, $o->{ui}->niceFileSize($sizeByBox{'messages'} || 0)), ' ', |
2984
|
|
|
|
|
|
|
$o->{ui}->right(10, $o->{ui}->niceFileSize($sizeByBox{'private'} || 0)), ' ', |
2985
|
|
|
|
|
|
|
$o->{ui}->right(10, $o->{ui}->niceFileSize($sizeByBox{'public'} || 0)), ' ', |
2986
|
0
|
0
|
0
|
|
|
0
|
$newestDate == 0 ? 'never' : $o->{ui}->niceDateTime($newestDate * 1000)); |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
2987
|
|
|
|
|
|
|
|
2988
|
0
|
|
|
|
|
0
|
$countAccounts += 1; |
2989
|
|
|
|
|
|
|
} |
2990
|
|
|
|
|
|
|
|
2991
|
0
|
|
|
|
|
0
|
$o->{ui}->line($countAccounts, ' accounts traversed'); |
2992
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
2993
|
|
|
|
|
|
|
|
2994
|
|
|
|
|
|
|
# Mark all objects that are younger than 1 day (so that objects being uploaded right now but not linked yet remain) |
2995
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Objects'); |
2996
|
0
|
|
|
|
|
0
|
$o->startProgress('objects'); |
2997
|
|
|
|
|
|
|
|
2998
|
0
|
|
|
|
|
0
|
my %objects; |
2999
|
0
|
|
|
|
|
0
|
my @topFolders = sort grep {$_ !~ /^\./} CDS->listFolder($o->{objectsFolder}); |
|
0
|
|
|
|
|
0
|
|
3000
|
0
|
|
|
|
|
0
|
foreach my $topFolder (@topFolders) { |
3001
|
0
|
|
|
|
|
0
|
my @files = sort grep {$_ !~ /^\./} CDS->listFolder($o->{objectsFolder}.'/'.$topFolder); |
|
0
|
|
|
|
|
0
|
|
3002
|
0
|
|
|
|
|
0
|
foreach my $filename (@files) { |
3003
|
0
|
|
|
|
|
0
|
$o->incrementProgress; |
3004
|
0
|
|
|
|
|
0
|
my $hash = pack 'H*', $topFolder.$filename; |
3005
|
0
|
|
|
|
|
0
|
my @s = stat $o->{objectsFolder}.'/'.$topFolder.'/'.$filename; |
3006
|
0
|
|
|
|
|
0
|
$objects{$hash} = $s[7]; |
3007
|
0
|
0
|
|
|
|
0
|
next if $s[9] < $dateLimit; |
3008
|
0
|
|
|
|
|
0
|
$o->traverse($hash, 'recent object'); |
3009
|
|
|
|
|
|
|
} |
3010
|
|
|
|
|
|
|
} |
3011
|
|
|
|
|
|
|
|
3012
|
0
|
|
|
|
|
0
|
$o->{ui}->line(scalar keys %objects, ' objects traversed'); |
3013
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
3014
|
|
|
|
|
|
|
|
3015
|
|
|
|
|
|
|
# Delete all unmarked objects, and add the marked objects to the new tree index |
3016
|
0
|
|
|
|
|
0
|
my $index = CDS::Record->new; |
3017
|
0
|
|
|
|
|
0
|
my $countKeptObjects = 0; |
3018
|
0
|
|
|
|
|
0
|
my $sizeKeptObjects = 0; |
3019
|
0
|
|
|
|
|
0
|
my $countDeletedObjects = 0; |
3020
|
0
|
|
|
|
|
0
|
my $sizeDeletedObjects = 0; |
3021
|
|
|
|
|
|
|
|
3022
|
0
|
|
|
|
|
0
|
$handler->startDeletion; |
3023
|
0
|
|
|
|
|
0
|
$o->startProgress('delete-objects'); |
3024
|
0
|
|
|
|
|
0
|
for my $hash (keys %objects) { |
3025
|
0
|
|
|
|
|
0
|
my $size = $objects{$hash}; |
3026
|
0
|
0
|
|
|
|
0
|
if (exists $o->{usedHashes}->{$hash}) { |
3027
|
0
|
|
|
|
|
0
|
$countKeptObjects += 1; |
3028
|
0
|
|
|
|
|
0
|
$sizeKeptObjects += $size; |
3029
|
0
|
|
|
|
|
0
|
my $entry = $o->{index}->{$hash}; |
3030
|
0
|
0
|
|
|
|
0
|
$index->addRecord($entry) if $entry; |
3031
|
|
|
|
|
|
|
} else { |
3032
|
0
|
|
|
|
|
0
|
$o->incrementProgress; |
3033
|
0
|
|
|
|
|
0
|
$countDeletedObjects += 1; |
3034
|
0
|
|
|
|
|
0
|
$sizeDeletedObjects += $size; |
3035
|
0
|
|
|
|
|
0
|
my $hashHex = unpack 'H*', $hash; |
3036
|
0
|
|
|
|
|
0
|
my $file = $o->{objectsFolder}.'/'.substr($hashHex, 0, 2).'/'.substr($hashHex, 2); |
3037
|
0
|
|
0
|
|
|
0
|
$handler->deleteObject($file) // return; |
3038
|
|
|
|
|
|
|
} |
3039
|
|
|
|
|
|
|
} |
3040
|
|
|
|
|
|
|
|
3041
|
|
|
|
|
|
|
# Write the new tree index |
3042
|
0
|
|
|
|
|
0
|
CDS->writeBytesToFile($o->{storeFolder}.'/.index-new', $index->toObject->bytes); |
3043
|
0
|
|
|
|
|
0
|
rename $o->{storeFolder}.'/.index-new', $o->{storeFolder}.'/.index'; |
3044
|
|
|
|
|
|
|
|
3045
|
|
|
|
|
|
|
# Show what has been done |
3046
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
3047
|
0
|
|
|
|
|
0
|
$o->{ui}->line($countDeletedEnvelopes, ' ', $handler->{deletedEnvelopesText}); |
3048
|
0
|
|
|
|
|
0
|
$o->{ui}->line($countKeptEnvelopes, ' ', $handler->{keptEnvelopesText}); |
3049
|
0
|
|
|
|
|
0
|
my $line1 = $countDeletedObjects.' '.$handler->{deletedObjectsText}; |
3050
|
0
|
|
|
|
|
0
|
my $line2 = $countKeptObjects.' '.$handler->{keptObjectsText}; |
3051
|
0
|
|
|
|
|
0
|
my $maxLength = CDS->max(length $line1, length $line2); |
3052
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->left($maxLength, $line1), ' ', $o->{ui}->gray($o->{ui}->niceFileSize($sizeDeletedObjects))); |
3053
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->left($maxLength, $line2), ' ', $o->{ui}->gray($o->{ui}->niceFileSize($sizeKeptObjects))); |
3054
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
3055
|
0
|
|
|
|
|
0
|
$handler->wrapUp; |
3056
|
|
|
|
|
|
|
|
3057
|
0
|
|
|
|
|
0
|
my $missing = scalar keys %{$o->{missingObjects}}; |
|
0
|
|
|
|
|
0
|
|
3058
|
0
|
0
|
|
|
|
0
|
if ($missing) { |
3059
|
0
|
|
|
|
|
0
|
$o->{ui}->warning($missing, ' objects are referenced from other objects, but missing:'); |
3060
|
|
|
|
|
|
|
|
3061
|
0
|
|
|
|
|
0
|
my $count = 0; |
3062
|
0
|
|
|
|
|
0
|
for my $hashBytes (sort keys %{$o->{missingObjects}}) { |
|
0
|
|
|
|
|
0
|
|
3063
|
0
|
|
|
|
|
0
|
$o->{ui}->warning(' ', unpack('H*', $hashBytes)); |
3064
|
|
|
|
|
|
|
|
3065
|
0
|
|
|
|
|
0
|
$count += 1; |
3066
|
0
|
0
|
0
|
|
|
0
|
if ($missing > 10 && $count > 5) { |
3067
|
0
|
|
|
|
|
0
|
$o->{ui}->warning(' …'); |
3068
|
0
|
|
|
|
|
0
|
last; |
3069
|
|
|
|
|
|
|
} |
3070
|
|
|
|
|
|
|
} |
3071
|
|
|
|
|
|
|
|
3072
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
3073
|
0
|
|
|
|
|
0
|
$o->{ui}->warning('The missing objects are from the following origins:'); |
3074
|
0
|
|
|
|
|
0
|
for my $origin (sort keys %{$o->{brokenOrigins}}) { |
|
0
|
|
|
|
|
0
|
|
3075
|
0
|
|
|
|
|
0
|
$o->{ui}->line(' ', $o->{ui}->orange($origin)); |
3076
|
|
|
|
|
|
|
} |
3077
|
|
|
|
|
|
|
|
3078
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
3079
|
|
|
|
|
|
|
} |
3080
|
|
|
|
|
|
|
} |
3081
|
|
|
|
|
|
|
|
3082
|
|
|
|
|
|
|
sub traverse { |
3083
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3084
|
0
|
|
|
|
|
0
|
my $hashBytes = shift; |
3085
|
0
|
|
|
|
|
0
|
my $origin = shift; |
3086
|
|
|
|
|
|
|
|
3087
|
0
|
0
|
|
|
|
0
|
return $o->{usedHashes}->{$hashBytes} if exists $o->{usedHashes}->{$hashBytes}; |
3088
|
|
|
|
|
|
|
|
3089
|
|
|
|
|
|
|
# Get index information about the object |
3090
|
0
|
|
0
|
|
|
0
|
my $record = $o->index($hashBytes, $origin) // return 0; |
3091
|
0
|
|
|
|
|
0
|
my $size = $record->nthChild(0)->asInteger; |
3092
|
|
|
|
|
|
|
|
3093
|
|
|
|
|
|
|
# Process children |
3094
|
0
|
|
|
|
|
0
|
my $pos = 0; |
3095
|
0
|
|
|
|
|
0
|
my $hashes = $record->nthChild(1)->bytes; |
3096
|
0
|
|
|
|
|
0
|
while ($pos < length $hashes) { |
3097
|
0
|
|
|
|
|
0
|
$size += $o->traverse(substr($hashes, $pos, 32), $origin); |
3098
|
0
|
|
|
|
|
0
|
$pos += 32; |
3099
|
|
|
|
|
|
|
} |
3100
|
|
|
|
|
|
|
|
3101
|
|
|
|
|
|
|
# Keep the size for future use |
3102
|
0
|
|
|
|
|
0
|
$o->{usedHashes}->{$hashBytes} = $size; |
3103
|
0
|
|
|
|
|
0
|
return $size; |
3104
|
|
|
|
|
|
|
} |
3105
|
|
|
|
|
|
|
|
3106
|
|
|
|
|
|
|
sub readIndex { |
3107
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3108
|
|
|
|
|
|
|
|
3109
|
0
|
|
|
|
|
0
|
$o->{index} = {}; |
3110
|
0
|
|
|
|
|
0
|
my $file = $o->{storeFolder}.'/.index'; |
3111
|
0
|
|
0
|
|
|
0
|
my $record = CDS::Record->fromObject(CDS::Object->fromBytes(CDS->readBytesFromFile($file))) // return; |
3112
|
0
|
|
|
|
|
0
|
for my $child ($record->children) { |
3113
|
0
|
|
|
|
|
0
|
$o->{index}->{$child->bytes} = $child; |
3114
|
|
|
|
|
|
|
} |
3115
|
|
|
|
|
|
|
} |
3116
|
|
|
|
|
|
|
|
3117
|
|
|
|
|
|
|
sub index { |
3118
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3119
|
0
|
|
|
|
|
0
|
my $hashBytes = shift; |
3120
|
0
|
|
|
|
|
0
|
my $origin = shift; |
3121
|
|
|
|
|
|
|
|
3122
|
0
|
|
|
|
|
0
|
$o->incrementProgress; |
3123
|
|
|
|
|
|
|
|
3124
|
|
|
|
|
|
|
# Report a known result |
3125
|
0
|
0
|
|
|
|
0
|
if ($o->{missingObjects}->{$hashBytes}) { |
3126
|
0
|
|
|
|
|
0
|
$o->{brokenOrigins}->{$origin} = 1; |
3127
|
0
|
|
|
|
|
0
|
return; |
3128
|
|
|
|
|
|
|
} |
3129
|
|
|
|
|
|
|
|
3130
|
0
|
0
|
|
|
|
0
|
return $o->{index}->{$hashBytes} if exists $o->{index}->{$hashBytes}; |
3131
|
|
|
|
|
|
|
|
3132
|
|
|
|
|
|
|
# Object file |
3133
|
0
|
|
|
|
|
0
|
my $hashHex = unpack 'H*', $hashBytes; |
3134
|
0
|
|
|
|
|
0
|
my $file = $o->{objectsFolder}.'/'.substr($hashHex, 0, 2).'/'.substr($hashHex, 2); |
3135
|
|
|
|
|
|
|
|
3136
|
|
|
|
|
|
|
# Size and existence |
3137
|
0
|
|
|
|
|
0
|
my @s = stat $file; |
3138
|
0
|
0
|
|
|
|
0
|
if (! scalar @s) { |
3139
|
0
|
|
|
|
|
0
|
$o->{missingObjects}->{$hashBytes} = 1; |
3140
|
0
|
|
|
|
|
0
|
$o->{brokenOrigins}->{$origin} = 1; |
3141
|
0
|
|
|
|
|
0
|
return; |
3142
|
|
|
|
|
|
|
} |
3143
|
0
|
|
|
|
|
0
|
my $size = $s[7]; |
3144
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('Unexpected: object ', $hashHex, ' has ', $size, ' bytes') if $size < 4; |
3145
|
|
|
|
|
|
|
|
3146
|
|
|
|
|
|
|
# Read header |
3147
|
0
|
|
|
|
|
0
|
open O, '<', $file; |
3148
|
0
|
|
|
|
|
0
|
read O, my $buffer, 4; |
3149
|
0
|
|
|
|
|
0
|
my $links = unpack 'L>', $buffer; |
3150
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('Unexpected: object ', $hashHex, ' has ', $links, ' references') if $links > 160000; |
3151
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('Unexpected: object ', $hashHex, ' is too small for ', $links, ' references') if 4 + $links * 32 > $s[7]; |
3152
|
0
|
|
|
|
|
0
|
my $hashes = ''; |
3153
|
0
|
0
|
|
|
|
0
|
read O, $hashes, $links * 32 if $links > 0; |
3154
|
0
|
|
|
|
|
0
|
close O; |
3155
|
|
|
|
|
|
|
|
3156
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('Incomplete read: ', length $hashes, ' out of ', $links * 32, ' bytes received.') if length $hashes != $links * 32; |
3157
|
|
|
|
|
|
|
|
3158
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->new($hashBytes); |
3159
|
0
|
|
|
|
|
0
|
$record->addInteger($size); |
3160
|
0
|
|
|
|
|
0
|
$record->add($hashes); |
3161
|
0
|
|
|
|
|
0
|
return $o->{index}->{$hashBytes} = $record; |
3162
|
|
|
|
|
|
|
} |
3163
|
|
|
|
|
|
|
|
3164
|
|
|
|
|
|
|
sub envelopeExpiration { |
3165
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3166
|
0
|
|
|
|
|
0
|
my $hashBytes = shift; |
3167
|
0
|
|
|
|
|
0
|
my $origin = shift; |
3168
|
|
|
|
|
|
|
|
3169
|
0
|
|
0
|
|
|
0
|
my $entry = $o->index($hashBytes, $origin) // return 0; |
3170
|
0
|
0
|
|
|
|
0
|
return $entry->nthChild(2)->asInteger if scalar $entry->children > 2; |
3171
|
|
|
|
|
|
|
|
3172
|
|
|
|
|
|
|
# Object file |
3173
|
0
|
|
|
|
|
0
|
my $hashHex = unpack 'H*', $hashBytes; |
3174
|
0
|
|
|
|
|
0
|
my $file = $o->{objectsFolder}.'/'.substr($hashHex, 0, 2).'/'.substr($hashHex, 2); |
3175
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->fromObject(CDS::Object->fromBytes(CDS->readBytesFromFile($file))); |
3176
|
0
|
|
|
|
|
0
|
my $expires = $record->child('expires')->integerValue; |
3177
|
0
|
|
|
|
|
0
|
$entry->addInteger($expires); |
3178
|
0
|
|
|
|
|
0
|
return $expires; |
3179
|
|
|
|
|
|
|
} |
3180
|
|
|
|
|
|
|
|
3181
|
|
|
|
|
|
|
sub startProgress { |
3182
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3183
|
0
|
|
|
|
|
0
|
my $title = shift; |
3184
|
|
|
|
|
|
|
|
3185
|
0
|
|
|
|
|
0
|
$o->{progress} = 0; |
3186
|
0
|
|
|
|
|
0
|
$o->{progressTitle} = $title; |
3187
|
0
|
|
|
|
|
0
|
$o->{ui}->progress($o->{progress}, ' ', $o->{progressTitle}); |
3188
|
|
|
|
|
|
|
} |
3189
|
|
|
|
|
|
|
|
3190
|
|
|
|
|
|
|
sub incrementProgress { |
3191
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3192
|
|
|
|
|
|
|
|
3193
|
0
|
|
|
|
|
0
|
$o->{progress} += 1; |
3194
|
0
|
0
|
|
|
|
0
|
return if $o->{progress} % 100; |
3195
|
0
|
|
|
|
|
0
|
$o->{ui}->progress($o->{progress}, ' ', $o->{progressTitle}); |
3196
|
|
|
|
|
|
|
} |
3197
|
|
|
|
|
|
|
|
3198
|
|
|
|
|
|
|
sub lastModified { |
3199
|
0
|
|
|
0
|
|
0
|
my $file = shift; |
3200
|
|
|
|
|
|
|
|
3201
|
0
|
|
|
|
|
0
|
my @s = stat $file; |
3202
|
0
|
0
|
|
|
|
0
|
return scalar @s ? $s[9] : 0; |
3203
|
|
|
|
|
|
|
} |
3204
|
|
|
|
|
|
|
|
3205
|
|
|
|
|
|
|
package CDS::Commands::CollectGarbage::Delete; |
3206
|
|
|
|
|
|
|
|
3207
|
|
|
|
|
|
|
sub new { |
3208
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
3209
|
0
|
|
|
|
|
0
|
my $ui = shift; |
3210
|
|
|
|
|
|
|
|
3211
|
0
|
|
|
|
|
0
|
return bless { |
3212
|
|
|
|
|
|
|
ui => $ui, |
3213
|
|
|
|
|
|
|
deletedEnvelopesText => 'expired envelopes deleted', |
3214
|
|
|
|
|
|
|
keptEnvelopesText => 'envelopes kept', |
3215
|
|
|
|
|
|
|
deletedObjectsText => 'objects deleted', |
3216
|
|
|
|
|
|
|
keptObjectsText => 'objects kept', |
3217
|
|
|
|
|
|
|
}; |
3218
|
|
|
|
|
|
|
} |
3219
|
|
|
|
|
|
|
|
3220
|
|
|
|
|
|
|
sub initialize { |
3221
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3222
|
0
|
|
|
|
|
0
|
my $folder = shift; |
3223
|
0
|
|
|
|
|
0
|
1 } |
3224
|
|
|
|
|
|
|
|
3225
|
|
|
|
|
|
|
sub startDeletion { |
3226
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3227
|
|
|
|
|
|
|
|
3228
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Deleting obsolete objects'); |
3229
|
|
|
|
|
|
|
} |
3230
|
|
|
|
|
|
|
|
3231
|
|
|
|
|
|
|
sub deleteEnvelope { |
3232
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3233
|
0
|
|
|
|
|
0
|
my $file = shift; |
3234
|
0
|
|
|
|
|
0
|
$o->deleteObject($file) } |
3235
|
|
|
|
|
|
|
|
3236
|
|
|
|
|
|
|
sub deleteObject { |
3237
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3238
|
0
|
|
|
|
|
0
|
my $file = shift; |
3239
|
|
|
|
|
|
|
|
3240
|
0
|
|
0
|
|
|
0
|
unlink $file // return $o->{ui}->error('Unable to delete "', $file, '". Giving up …'); |
3241
|
0
|
|
|
|
|
0
|
return 1; |
3242
|
|
|
|
|
|
|
} |
3243
|
|
|
|
|
|
|
|
3244
|
|
|
|
|
|
|
sub wrapUp { |
3245
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3246
|
|
|
|
|
|
|
} |
3247
|
|
|
|
|
|
|
|
3248
|
|
|
|
|
|
|
package CDS::Commands::CollectGarbage::Report; |
3249
|
|
|
|
|
|
|
|
3250
|
|
|
|
|
|
|
sub new { |
3251
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
3252
|
0
|
|
|
|
|
0
|
my $ui = shift; |
3253
|
|
|
|
|
|
|
|
3254
|
0
|
|
|
|
|
0
|
return bless { |
3255
|
|
|
|
|
|
|
ui => $ui, |
3256
|
|
|
|
|
|
|
countReported => 0, |
3257
|
|
|
|
|
|
|
deletedEnvelopesText => 'envelopes have expired', |
3258
|
|
|
|
|
|
|
keptEnvelopesText => 'envelopes are in use', |
3259
|
|
|
|
|
|
|
deletedObjectsText => 'objects can be deleted', |
3260
|
|
|
|
|
|
|
keptObjectsText => 'objects are in use', |
3261
|
|
|
|
|
|
|
}; |
3262
|
|
|
|
|
|
|
} |
3263
|
|
|
|
|
|
|
|
3264
|
|
|
|
|
|
|
sub initialize { |
3265
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3266
|
0
|
|
|
|
|
0
|
my $folderStore = shift; |
3267
|
|
|
|
|
|
|
|
3268
|
0
|
|
|
|
|
0
|
$o->{file} = $folderStore->folder.'/.garbage'; |
3269
|
0
|
0
|
|
|
|
0
|
open($o->{fh}, '>', $o->{file}) || return $o->{ui}->error('Failed to open ', $o->{file}, ' for writing.'); |
3270
|
0
|
|
|
|
|
0
|
return 1; |
3271
|
|
|
|
|
|
|
} |
3272
|
|
|
|
|
|
|
|
3273
|
|
|
|
|
|
|
sub startDeletion { |
3274
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3275
|
|
|
|
|
|
|
|
3276
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Deleting obsolete objects'); |
3277
|
|
|
|
|
|
|
} |
3278
|
|
|
|
|
|
|
|
3279
|
|
|
|
|
|
|
sub deleteEnvelope { |
3280
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3281
|
0
|
|
|
|
|
0
|
my $file = shift; |
3282
|
0
|
|
|
|
|
0
|
$o->deleteObject($file) } |
3283
|
|
|
|
|
|
|
|
3284
|
|
|
|
|
|
|
sub deleteObject { |
3285
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3286
|
0
|
|
|
|
|
0
|
my $file = shift; |
3287
|
|
|
|
|
|
|
|
3288
|
0
|
|
|
|
|
0
|
my $fh = $o->{fh}; |
3289
|
0
|
|
|
|
|
0
|
print $fh 'rm ', $file, "\n"; |
3290
|
0
|
|
|
|
|
0
|
$o->{countReported} += 1; |
3291
|
0
|
0
|
|
|
|
0
|
print $fh 'echo ', $o->{countReported}, ' files deleted', "\n" if $o->{countReported} % 100 == 0; |
3292
|
0
|
|
|
|
|
0
|
return 1; |
3293
|
|
|
|
|
|
|
} |
3294
|
|
|
|
|
|
|
|
3295
|
|
|
|
|
|
|
sub wrapUp { |
3296
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3297
|
|
|
|
|
|
|
|
3298
|
0
|
|
|
|
|
0
|
close $o->{fh}; |
3299
|
0
|
0
|
|
|
|
0
|
if ($o->{countReported} == 0) { |
3300
|
0
|
|
|
|
|
0
|
unlink $o->{file}; |
3301
|
|
|
|
|
|
|
} else { |
3302
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
3303
|
0
|
|
|
|
|
0
|
$o->{ui}->p('The report was written to ', $o->{file}, '.'); |
3304
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
3305
|
|
|
|
|
|
|
} |
3306
|
|
|
|
|
|
|
} |
3307
|
|
|
|
|
|
|
|
3308
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
3309
|
|
|
|
|
|
|
package CDS::Commands::CreateKeyPair; |
3310
|
|
|
|
|
|
|
|
3311
|
|
|
|
|
|
|
sub register { |
3312
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
3313
|
0
|
|
|
|
|
0
|
my $cds = shift; |
3314
|
0
|
|
|
|
|
0
|
my $help = shift; |
3315
|
|
|
|
|
|
|
|
3316
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
3317
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
3318
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
3319
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
3320
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
3321
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
3322
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&createKeyPair}); |
3323
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'create'); |
3324
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'create'); |
3325
|
0
|
|
|
|
|
0
|
$node000->addArrow($node001, 1, 0, 'key'); |
3326
|
0
|
|
|
|
|
0
|
$node001->addArrow($node005, 1, 0, 'pair'); |
3327
|
0
|
|
|
|
|
0
|
$node002->addArrow($node003, 1, 0, 'key'); |
3328
|
0
|
|
|
|
|
0
|
$node003->addArrow($node004, 1, 0, 'pair'); |
3329
|
0
|
|
|
|
|
0
|
$node004->addArrow($node006, 1, 0, 'FILENAME', \&collectFilename); |
3330
|
|
|
|
|
|
|
} |
3331
|
|
|
|
|
|
|
|
3332
|
|
|
|
|
|
|
sub collectFilename { |
3333
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3334
|
0
|
|
|
|
|
0
|
my $label = shift; |
3335
|
0
|
|
|
|
|
0
|
my $value = shift; |
3336
|
|
|
|
|
|
|
|
3337
|
0
|
|
|
|
|
0
|
$o->{filename} = $value; |
3338
|
|
|
|
|
|
|
} |
3339
|
|
|
|
|
|
|
|
3340
|
|
|
|
|
|
|
sub new { |
3341
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
3342
|
0
|
|
|
|
|
0
|
my $actor = shift; |
3343
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
3344
|
|
|
|
|
|
|
|
3345
|
|
|
|
|
|
|
# END AUTOGENERATED |
3346
|
|
|
|
|
|
|
|
3347
|
|
|
|
|
|
|
# HTML FOLDER NAME create-key-pair |
3348
|
|
|
|
|
|
|
# HTML TITLE Create key pair |
3349
|
|
|
|
|
|
|
sub help { |
3350
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3351
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
3352
|
|
|
|
|
|
|
|
3353
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
3354
|
0
|
|
|
|
|
0
|
$ui->space; |
3355
|
0
|
|
|
|
|
0
|
$ui->command('cds create key pair FILENAME'); |
3356
|
0
|
|
|
|
|
0
|
$ui->p('Generates a key pair, and writes it to FILENAME.'); |
3357
|
0
|
|
|
|
|
0
|
$ui->space; |
3358
|
0
|
|
|
|
|
0
|
$ui->title('Related commands'); |
3359
|
0
|
|
|
|
|
0
|
$ui->line(' cds select …'); |
3360
|
0
|
|
|
|
|
0
|
$ui->line(' cds use …'); |
3361
|
0
|
|
|
|
|
0
|
$ui->line(' cds entrust …'); |
3362
|
0
|
|
|
|
|
0
|
$ui->line(' cds drop …'); |
3363
|
0
|
|
|
|
|
0
|
$ui->space; |
3364
|
|
|
|
|
|
|
} |
3365
|
|
|
|
|
|
|
|
3366
|
|
|
|
|
|
|
sub createKeyPair { |
3367
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3368
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
3369
|
|
|
|
|
|
|
|
3370
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
3371
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('The file "', $o->{filename}, '" exists.') if -e $o->{filename}; |
3372
|
0
|
|
|
|
|
0
|
my $keyPair = CDS::KeyPair->generate; |
3373
|
0
|
|
0
|
|
|
0
|
$keyPair->writeToFile($o->{filename}) // return $o->{ui}->error('Failed to write the key pair file "', $o->{filename}, '".'); |
3374
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Key pair "', $o->{filename}, '" created.'); |
3375
|
|
|
|
|
|
|
} |
3376
|
|
|
|
|
|
|
|
3377
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
3378
|
|
|
|
|
|
|
package CDS::Commands::Curl; |
3379
|
|
|
|
|
|
|
|
3380
|
|
|
|
|
|
|
sub register { |
3381
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
3382
|
0
|
|
|
|
|
0
|
my $cds = shift; |
3383
|
0
|
|
|
|
|
0
|
my $help = shift; |
3384
|
|
|
|
|
|
|
|
3385
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
3386
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(1); |
3387
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
3388
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
3389
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
3390
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
3391
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
3392
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
3393
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
3394
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
3395
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
3396
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
3397
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
3398
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlGet}); |
3399
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(0); |
3400
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(0); |
3401
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlPut}); |
3402
|
0
|
|
|
|
|
0
|
my $node017 = CDS::Parser::Node->new(0); |
3403
|
0
|
|
|
|
|
0
|
my $node018 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlBook}); |
3404
|
0
|
|
|
|
|
0
|
my $node019 = CDS::Parser::Node->new(0); |
3405
|
0
|
|
|
|
|
0
|
my $node020 = CDS::Parser::Node->new(0); |
3406
|
0
|
|
|
|
|
0
|
my $node021 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlList}); |
3407
|
0
|
|
|
|
|
0
|
my $node022 = CDS::Parser::Node->new(0); |
3408
|
0
|
|
|
|
|
0
|
my $node023 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlGet}); |
3409
|
0
|
|
|
|
|
0
|
my $node024 = CDS::Parser::Node->new(0); |
3410
|
0
|
|
|
|
|
0
|
my $node025 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlPut}); |
3411
|
0
|
|
|
|
|
0
|
my $node026 = CDS::Parser::Node->new(0); |
3412
|
0
|
|
|
|
|
0
|
my $node027 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlBook}); |
3413
|
0
|
|
|
|
|
0
|
my $node028 = CDS::Parser::Node->new(0); |
3414
|
0
|
|
|
|
|
0
|
my $node029 = CDS::Parser::Node->new(1); |
3415
|
0
|
|
|
|
|
0
|
my $node030 = CDS::Parser::Node->new(0); |
3416
|
0
|
|
|
|
|
0
|
my $node031 = CDS::Parser::Node->new(0); |
3417
|
0
|
|
|
|
|
0
|
my $node032 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlList}); |
3418
|
0
|
|
|
|
|
0
|
my $node033 = CDS::Parser::Node->new(0); |
3419
|
0
|
|
|
|
|
0
|
my $node034 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlGet}); |
3420
|
0
|
|
|
|
|
0
|
my $node035 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlPut}); |
3421
|
0
|
|
|
|
|
0
|
my $node036 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlBook}); |
3422
|
0
|
|
|
|
|
0
|
my $node037 = CDS::Parser::Node->new(1); |
3423
|
0
|
|
|
|
|
0
|
my $node038 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlList}); |
3424
|
0
|
|
|
|
|
0
|
my $node039 = CDS::Parser::Node->new(0); |
3425
|
0
|
|
|
|
|
0
|
my $node040 = CDS::Parser::Node->new(0); |
3426
|
0
|
|
|
|
|
0
|
my $node041 = CDS::Parser::Node->new(0); |
3427
|
0
|
|
|
|
|
0
|
my $node042 = CDS::Parser::Node->new(0); |
3428
|
0
|
|
|
|
|
0
|
my $node043 = CDS::Parser::Node->new(0); |
3429
|
0
|
|
|
|
|
0
|
my $node044 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlList}); |
3430
|
0
|
|
|
|
|
0
|
my $node045 = CDS::Parser::Node->new(1); |
3431
|
0
|
|
|
|
|
0
|
my $node046 = CDS::Parser::Node->new(0); |
3432
|
0
|
|
|
|
|
0
|
my $node047 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlModify}); |
3433
|
0
|
|
|
|
|
0
|
my $node048 = CDS::Parser::Node->new(0); |
3434
|
0
|
|
|
|
|
0
|
my $node049 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlModify}); |
3435
|
0
|
|
|
|
|
0
|
my $node050 = CDS::Parser::Node->new(0); |
3436
|
0
|
|
|
|
|
0
|
my $node051 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&curlModify}); |
3437
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'curl'); |
3438
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'curl'); |
3439
|
0
|
|
|
|
|
0
|
$node001->addArrow($node002, 1, 0, 'get'); |
3440
|
0
|
|
|
|
|
0
|
$node001->addArrow($node003, 1, 0, 'put'); |
3441
|
0
|
|
|
|
|
0
|
$node001->addArrow($node004, 1, 0, 'book'); |
3442
|
0
|
|
|
|
|
0
|
$node001->addArrow($node005, 1, 0, 'get'); |
3443
|
0
|
|
|
|
|
0
|
$node001->addArrow($node006, 1, 0, 'book'); |
3444
|
0
|
|
|
|
|
0
|
$node001->addArrow($node007, 1, 0, 'list'); |
3445
|
0
|
|
|
|
|
0
|
$node001->addArrow($node007, 1, 0, 'watch', \&collectWatch); |
3446
|
0
|
|
|
|
|
0
|
$node001->addDefault($node011); |
3447
|
0
|
|
|
|
|
0
|
$node002->addArrow($node013, 1, 0, 'HASH', \&collectHash); |
3448
|
0
|
|
|
|
|
0
|
$node003->addArrow($node016, 1, 0, 'FILE', \&collectFile); |
3449
|
0
|
|
|
|
|
0
|
$node004->addArrow($node018, 1, 0, 'HASH', \&collectHash); |
3450
|
0
|
|
|
|
|
0
|
$node005->addArrow($node023, 1, 0, 'OBJECT', \&collectObject); |
3451
|
0
|
|
|
|
|
0
|
$node006->addArrow($node027, 1, 0, 'OBJECT', \&collectObject); |
3452
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'message'); |
3453
|
0
|
|
|
|
|
0
|
$node007->addArrow($node009, 1, 0, 'private'); |
3454
|
0
|
|
|
|
|
0
|
$node007->addArrow($node010, 1, 0, 'public'); |
3455
|
0
|
|
|
|
|
0
|
$node007->addArrow($node021, 0, 0, 'messages', \&collectMessages); |
3456
|
0
|
|
|
|
|
0
|
$node007->addArrow($node021, 0, 0, 'private', \&collectPrivate); |
3457
|
0
|
|
|
|
|
0
|
$node007->addArrow($node021, 0, 0, 'public', \&collectPublic); |
3458
|
0
|
|
|
|
|
0
|
$node008->addArrow($node021, 1, 0, 'box', \&collectMessages); |
3459
|
0
|
|
|
|
|
0
|
$node009->addArrow($node021, 1, 0, 'box', \&collectPrivate); |
3460
|
0
|
|
|
|
|
0
|
$node010->addArrow($node021, 1, 0, 'box', \&collectPublic); |
3461
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'remove'); |
3462
|
0
|
|
|
|
|
0
|
$node011->addArrow($node020, 1, 0, 'add'); |
3463
|
0
|
|
|
|
|
0
|
$node012->addArrow($node012, 1, 0, 'HASH', \&collectHash1); |
3464
|
0
|
|
|
|
|
0
|
$node012->addArrow($node037, 1, 0, 'HASH', \&collectHash1); |
3465
|
0
|
|
|
|
|
0
|
$node013->addArrow($node014, 1, 0, 'from'); |
3466
|
0
|
|
|
|
|
0
|
$node013->addArrow($node015, 0, 0, 'on'); |
3467
|
0
|
|
|
|
|
0
|
$node013->addDefault($node023); |
3468
|
0
|
|
|
|
|
0
|
$node014->addArrow($node023, 1, 0, 'STORE', \&collectStore); |
3469
|
0
|
|
|
|
|
0
|
$node015->addArrow($node023, 0, 0, 'STORE', \&collectStore); |
3470
|
0
|
|
|
|
|
0
|
$node016->addArrow($node017, 1, 0, 'onto'); |
3471
|
0
|
|
|
|
|
0
|
$node016->addDefault($node025); |
3472
|
0
|
|
|
|
|
0
|
$node017->addArrow($node025, 1, 0, 'STORE', \&collectStore); |
3473
|
0
|
|
|
|
|
0
|
$node018->addArrow($node019, 1, 0, 'on'); |
3474
|
0
|
|
|
|
|
0
|
$node018->addDefault($node027); |
3475
|
0
|
|
|
|
|
0
|
$node019->addArrow($node027, 1, 0, 'STORE', \&collectStore); |
3476
|
0
|
|
|
|
|
0
|
$node020->addArrow($node029, 1, 0, 'FILE', \&collectFile1); |
3477
|
0
|
|
|
|
|
0
|
$node020->addArrow($node029, 1, 0, 'HASH', \&collectHash2); |
3478
|
0
|
|
|
|
|
0
|
$node021->addArrow($node022, 1, 0, 'of'); |
3479
|
0
|
|
|
|
|
0
|
$node022->addArrow($node032, 1, 0, 'ACTOR', \&collectActor); |
3480
|
0
|
|
|
|
|
0
|
$node023->addArrow($node024, 1, 0, 'using'); |
3481
|
0
|
|
|
|
|
0
|
$node024->addArrow($node034, 1, 0, 'KEYPAIR', \&collectKeypair); |
3482
|
0
|
|
|
|
|
0
|
$node025->addArrow($node026, 1, 0, 'using'); |
3483
|
0
|
|
|
|
|
0
|
$node026->addArrow($node035, 1, 0, 'KEYPAIR', \&collectKeypair); |
3484
|
0
|
|
|
|
|
0
|
$node027->addArrow($node028, 1, 0, 'using'); |
3485
|
0
|
|
|
|
|
0
|
$node028->addArrow($node036, 1, 0, 'KEYPAIR', \&collectKeypair); |
3486
|
0
|
|
|
|
|
0
|
$node029->addDefault($node020); |
3487
|
0
|
|
|
|
|
0
|
$node029->addArrow($node030, 1, 0, 'and'); |
3488
|
0
|
|
|
|
|
0
|
$node029->addArrow($node040, 1, 0, 'to'); |
3489
|
0
|
|
|
|
|
0
|
$node030->addArrow($node031, 1, 0, 'remove'); |
3490
|
0
|
|
|
|
|
0
|
$node031->addArrow($node031, 1, 0, 'HASH', \&collectHash1); |
3491
|
0
|
|
|
|
|
0
|
$node031->addArrow($node037, 1, 0, 'HASH', \&collectHash1); |
3492
|
0
|
|
|
|
|
0
|
$node032->addArrow($node033, 1, 0, 'on'); |
3493
|
0
|
|
|
|
|
0
|
$node033->addArrow($node038, 1, 0, 'STORE', \&collectStore); |
3494
|
0
|
|
|
|
|
0
|
$node037->addArrow($node040, 1, 0, 'from'); |
3495
|
0
|
|
|
|
|
0
|
$node038->addArrow($node039, 1, 0, 'using'); |
3496
|
0
|
|
|
|
|
0
|
$node039->addArrow($node044, 1, 0, 'KEYPAIR', \&collectKeypair); |
3497
|
0
|
|
|
|
|
0
|
$node040->addArrow($node041, 1, 0, 'message'); |
3498
|
0
|
|
|
|
|
0
|
$node040->addArrow($node042, 1, 0, 'private'); |
3499
|
0
|
|
|
|
|
0
|
$node040->addArrow($node043, 1, 0, 'public'); |
3500
|
0
|
|
|
|
|
0
|
$node040->addArrow($node045, 0, 0, 'messages', \&collectMessages1); |
3501
|
0
|
|
|
|
|
0
|
$node040->addArrow($node045, 0, 0, 'private', \&collectPrivate1); |
3502
|
0
|
|
|
|
|
0
|
$node040->addArrow($node045, 0, 0, 'public', \&collectPublic1); |
3503
|
0
|
|
|
|
|
0
|
$node041->addArrow($node045, 1, 0, 'box', \&collectMessages1); |
3504
|
0
|
|
|
|
|
0
|
$node042->addArrow($node045, 1, 0, 'box', \&collectPrivate1); |
3505
|
0
|
|
|
|
|
0
|
$node043->addArrow($node045, 1, 0, 'box', \&collectPublic1); |
3506
|
0
|
|
|
|
|
0
|
$node045->addArrow($node046, 1, 0, 'of'); |
3507
|
0
|
|
|
|
|
0
|
$node045->addDefault($node047); |
3508
|
0
|
|
|
|
|
0
|
$node046->addArrow($node047, 1, 0, 'ACTOR', \&collectActor1); |
3509
|
0
|
|
|
|
|
0
|
$node047->addArrow($node011, 1, 0, 'and', \&collectAnd); |
3510
|
0
|
|
|
|
|
0
|
$node047->addArrow($node048, 1, 0, 'on'); |
3511
|
0
|
|
|
|
|
0
|
$node048->addArrow($node049, 1, 0, 'STORE', \&collectStore); |
3512
|
0
|
|
|
|
|
0
|
$node049->addArrow($node050, 1, 0, 'using'); |
3513
|
0
|
|
|
|
|
0
|
$node050->addArrow($node051, 1, 0, 'KEYPAIR', \&collectKeypair); |
3514
|
|
|
|
|
|
|
} |
3515
|
|
|
|
|
|
|
|
3516
|
|
|
|
|
|
|
sub collectActor { |
3517
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3518
|
0
|
|
|
|
|
0
|
my $label = shift; |
3519
|
0
|
|
|
|
|
0
|
my $value = shift; |
3520
|
|
|
|
|
|
|
|
3521
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value; |
3522
|
|
|
|
|
|
|
} |
3523
|
|
|
|
|
|
|
|
3524
|
|
|
|
|
|
|
sub collectActor1 { |
3525
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3526
|
0
|
|
|
|
|
0
|
my $label = shift; |
3527
|
0
|
|
|
|
|
0
|
my $value = shift; |
3528
|
|
|
|
|
|
|
|
3529
|
0
|
|
|
|
|
0
|
$o->{currentBatch}->{actorHash} = $value; |
3530
|
|
|
|
|
|
|
} |
3531
|
|
|
|
|
|
|
|
3532
|
|
|
|
|
|
|
sub collectAnd { |
3533
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3534
|
0
|
|
|
|
|
0
|
my $label = shift; |
3535
|
0
|
|
|
|
|
0
|
my $value = shift; |
3536
|
|
|
|
|
|
|
|
3537
|
0
|
|
|
|
|
0
|
push @{$o->{batches}}, $o->{currentBatch}; |
|
0
|
|
|
|
|
0
|
|
3538
|
|
|
|
|
|
|
$o->{currentBatch} = { |
3539
|
0
|
|
|
|
|
0
|
addHashes => [], |
3540
|
|
|
|
|
|
|
addEnvelopes => [], |
3541
|
|
|
|
|
|
|
removeHashes => [] |
3542
|
|
|
|
|
|
|
}; |
3543
|
|
|
|
|
|
|
} |
3544
|
|
|
|
|
|
|
|
3545
|
|
|
|
|
|
|
sub collectFile { |
3546
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3547
|
0
|
|
|
|
|
0
|
my $label = shift; |
3548
|
0
|
|
|
|
|
0
|
my $value = shift; |
3549
|
|
|
|
|
|
|
|
3550
|
0
|
|
|
|
|
0
|
$o->{file} = $value; |
3551
|
|
|
|
|
|
|
} |
3552
|
|
|
|
|
|
|
|
3553
|
|
|
|
|
|
|
sub collectFile1 { |
3554
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3555
|
0
|
|
|
|
|
0
|
my $label = shift; |
3556
|
0
|
|
|
|
|
0
|
my $value = shift; |
3557
|
|
|
|
|
|
|
|
3558
|
0
|
|
|
|
|
0
|
push @{$o->{currentBatch}->{addFiles}}, $value; |
|
0
|
|
|
|
|
0
|
|
3559
|
|
|
|
|
|
|
} |
3560
|
|
|
|
|
|
|
|
3561
|
|
|
|
|
|
|
sub collectHash { |
3562
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3563
|
0
|
|
|
|
|
0
|
my $label = shift; |
3564
|
0
|
|
|
|
|
0
|
my $value = shift; |
3565
|
|
|
|
|
|
|
|
3566
|
0
|
|
|
|
|
0
|
$o->{hash} = $value; |
3567
|
|
|
|
|
|
|
} |
3568
|
|
|
|
|
|
|
|
3569
|
|
|
|
|
|
|
sub collectHash1 { |
3570
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3571
|
0
|
|
|
|
|
0
|
my $label = shift; |
3572
|
0
|
|
|
|
|
0
|
my $value = shift; |
3573
|
|
|
|
|
|
|
|
3574
|
0
|
|
|
|
|
0
|
push @{$o->{currentBatch}->{removeHashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
3575
|
|
|
|
|
|
|
} |
3576
|
|
|
|
|
|
|
|
3577
|
|
|
|
|
|
|
sub collectHash2 { |
3578
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3579
|
0
|
|
|
|
|
0
|
my $label = shift; |
3580
|
0
|
|
|
|
|
0
|
my $value = shift; |
3581
|
|
|
|
|
|
|
|
3582
|
0
|
|
|
|
|
0
|
push @{$o->{currentBatch}->{addHashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
3583
|
|
|
|
|
|
|
} |
3584
|
|
|
|
|
|
|
|
3585
|
|
|
|
|
|
|
sub collectKeypair { |
3586
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3587
|
0
|
|
|
|
|
0
|
my $label = shift; |
3588
|
0
|
|
|
|
|
0
|
my $value = shift; |
3589
|
|
|
|
|
|
|
|
3590
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
3591
|
|
|
|
|
|
|
} |
3592
|
|
|
|
|
|
|
|
3593
|
|
|
|
|
|
|
sub collectMessages { |
3594
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3595
|
0
|
|
|
|
|
0
|
my $label = shift; |
3596
|
0
|
|
|
|
|
0
|
my $value = shift; |
3597
|
|
|
|
|
|
|
|
3598
|
0
|
|
|
|
|
0
|
$o->{boxLabel} = 'messages'; |
3599
|
|
|
|
|
|
|
} |
3600
|
|
|
|
|
|
|
|
3601
|
|
|
|
|
|
|
sub collectMessages1 { |
3602
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3603
|
0
|
|
|
|
|
0
|
my $label = shift; |
3604
|
0
|
|
|
|
|
0
|
my $value = shift; |
3605
|
|
|
|
|
|
|
|
3606
|
0
|
|
|
|
|
0
|
$o->{currentBatch}->{boxLabel} = 'messages'; |
3607
|
|
|
|
|
|
|
} |
3608
|
|
|
|
|
|
|
|
3609
|
|
|
|
|
|
|
sub collectObject { |
3610
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3611
|
0
|
|
|
|
|
0
|
my $label = shift; |
3612
|
0
|
|
|
|
|
0
|
my $value = shift; |
3613
|
|
|
|
|
|
|
|
3614
|
0
|
|
|
|
|
0
|
$o->{hash} = $value->hash; |
3615
|
0
|
|
|
|
|
0
|
$o->{store} = $value->cliStore; |
3616
|
|
|
|
|
|
|
} |
3617
|
|
|
|
|
|
|
|
3618
|
|
|
|
|
|
|
sub collectPrivate { |
3619
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3620
|
0
|
|
|
|
|
0
|
my $label = shift; |
3621
|
0
|
|
|
|
|
0
|
my $value = shift; |
3622
|
|
|
|
|
|
|
|
3623
|
0
|
|
|
|
|
0
|
$o->{boxLabel} = 'private'; |
3624
|
|
|
|
|
|
|
} |
3625
|
|
|
|
|
|
|
|
3626
|
|
|
|
|
|
|
sub collectPrivate1 { |
3627
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3628
|
0
|
|
|
|
|
0
|
my $label = shift; |
3629
|
0
|
|
|
|
|
0
|
my $value = shift; |
3630
|
|
|
|
|
|
|
|
3631
|
0
|
|
|
|
|
0
|
$o->{currentBatch}->{boxLabel} = 'private'; |
3632
|
|
|
|
|
|
|
} |
3633
|
|
|
|
|
|
|
|
3634
|
|
|
|
|
|
|
sub collectPublic { |
3635
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3636
|
0
|
|
|
|
|
0
|
my $label = shift; |
3637
|
0
|
|
|
|
|
0
|
my $value = shift; |
3638
|
|
|
|
|
|
|
|
3639
|
0
|
|
|
|
|
0
|
$o->{boxLabel} = 'public'; |
3640
|
|
|
|
|
|
|
} |
3641
|
|
|
|
|
|
|
|
3642
|
|
|
|
|
|
|
sub collectPublic1 { |
3643
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3644
|
0
|
|
|
|
|
0
|
my $label = shift; |
3645
|
0
|
|
|
|
|
0
|
my $value = shift; |
3646
|
|
|
|
|
|
|
|
3647
|
0
|
|
|
|
|
0
|
$o->{currentBatch}->{boxLabel} = 'public'; |
3648
|
|
|
|
|
|
|
} |
3649
|
|
|
|
|
|
|
|
3650
|
|
|
|
|
|
|
sub collectStore { |
3651
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3652
|
0
|
|
|
|
|
0
|
my $label = shift; |
3653
|
0
|
|
|
|
|
0
|
my $value = shift; |
3654
|
|
|
|
|
|
|
|
3655
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
3656
|
|
|
|
|
|
|
} |
3657
|
|
|
|
|
|
|
|
3658
|
|
|
|
|
|
|
sub collectWatch { |
3659
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3660
|
0
|
|
|
|
|
0
|
my $label = shift; |
3661
|
0
|
|
|
|
|
0
|
my $value = shift; |
3662
|
|
|
|
|
|
|
|
3663
|
0
|
|
|
|
|
0
|
$o->{watchTimeout} = 60000; |
3664
|
|
|
|
|
|
|
} |
3665
|
|
|
|
|
|
|
|
3666
|
|
|
|
|
|
|
sub new { |
3667
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
3668
|
0
|
|
|
|
|
0
|
my $actor = shift; |
3669
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
3670
|
|
|
|
|
|
|
|
3671
|
|
|
|
|
|
|
# END AUTOGENERATED |
3672
|
|
|
|
|
|
|
|
3673
|
|
|
|
|
|
|
# HTML FOLDER NAME curl |
3674
|
|
|
|
|
|
|
# HTML TITLE Curl |
3675
|
|
|
|
|
|
|
sub help { |
3676
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3677
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
3678
|
|
|
|
|
|
|
|
3679
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
3680
|
0
|
|
|
|
|
0
|
$ui->space; |
3681
|
0
|
|
|
|
|
0
|
$ui->p($ui->blue('cds curl'), ' prepares and executes a CURL command line for a HTTP store request. This is helpful for debugging a HTTP store implementation. Outside of low-level debugging, it is more convenient to use the "cds get|put|list|add|remove …" commands, which are richer in functionality, and work on all stores.'); |
3682
|
0
|
|
|
|
|
0
|
$ui->space; |
3683
|
0
|
|
|
|
|
0
|
$ui->command('cds curl get OBJECT'); |
3684
|
0
|
|
|
|
|
0
|
$ui->command('cds curl get HASH [from|on STORE]'); |
3685
|
0
|
|
|
|
|
0
|
$ui->p('Downloads an object with a GET request on an object store.'); |
3686
|
0
|
|
|
|
|
0
|
$ui->space; |
3687
|
0
|
|
|
|
|
0
|
$ui->command('cds curl put FILE [onto STORE]'); |
3688
|
0
|
|
|
|
|
0
|
$ui->p('Uploads an object with a PUT request on an object store.'); |
3689
|
0
|
|
|
|
|
0
|
$ui->space; |
3690
|
0
|
|
|
|
|
0
|
$ui->command('cds curl book OBJECT'); |
3691
|
0
|
|
|
|
|
0
|
$ui->command('cds curl book HASH [on STORE]'); |
3692
|
0
|
|
|
|
|
0
|
$ui->p('Books an object with a POST request on an object store.'); |
3693
|
0
|
|
|
|
|
0
|
$ui->space; |
3694
|
0
|
|
|
|
|
0
|
$ui->command('cds curl list message box of ACTOR [on STORE]'); |
3695
|
0
|
|
|
|
|
0
|
$ui->command('cds curl list private box of ACTOR [on STORE]'); |
3696
|
0
|
|
|
|
|
0
|
$ui->command('cds curl list public box of ACTOR [on STORE]'); |
3697
|
0
|
|
|
|
|
0
|
$ui->p('Lists the indicated box with a GET request on an account store.'); |
3698
|
0
|
|
|
|
|
0
|
$ui->space; |
3699
|
0
|
|
|
|
|
0
|
$ui->command('cds curl watch message box of ACTOR [on STORE]'); |
3700
|
0
|
|
|
|
|
0
|
$ui->command('cds curl watch private box of ACTOR [on STORE]'); |
3701
|
0
|
|
|
|
|
0
|
$ui->command('cds curl watch public box of ACTOR [on STORE]'); |
3702
|
0
|
|
|
|
|
0
|
$ui->p('As above, but with a watch timeout of 60 second.'); |
3703
|
0
|
|
|
|
|
0
|
$ui->space; |
3704
|
0
|
|
|
|
|
0
|
$ui->command('cds curl add (FILE|HASH)* to (message|private|public) box of ACTOR [and …] [on STORE]'); |
3705
|
0
|
|
|
|
|
0
|
$ui->command('cds curl remove HASH* from (message|private|public) box of ACTOR [and …] [on STORE]'); |
3706
|
0
|
|
|
|
|
0
|
$ui->p('Modifies the indicated boxes with a POST request on an account store. Multiple modifications to different boxes may be chained using "and". All modifications are submitted using a single request, which is optionally signed (see below).'); |
3707
|
0
|
|
|
|
|
0
|
$ui->space; |
3708
|
0
|
|
|
|
|
0
|
$ui->command('… using KEYPAIR'); |
3709
|
0
|
|
|
|
|
0
|
$ui->p('Signs the request using KEYPAIR instead of the actor\'s key pair. The store may or may not verify the signature.'); |
3710
|
0
|
|
|
|
|
0
|
$ui->p('For debugging purposes, information about the signature is stored as ".cds-curl-bytes-to-sign", ".cds-curl-hash-to-sign", and ".cds-curl-signature" in the current folder. Note that signatures are valid for 1-2 minutes only. After that, servers will reject them to guard against replay attacks.'); |
3711
|
0
|
|
|
|
|
0
|
$ui->space; |
3712
|
|
|
|
|
|
|
} |
3713
|
|
|
|
|
|
|
|
3714
|
|
|
|
|
|
|
sub curlGet { |
3715
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3716
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
3717
|
|
|
|
|
|
|
|
3718
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
3719
|
0
|
0
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken if ! $o->{keyPairToken}; |
3720
|
0
|
0
|
|
|
|
0
|
$o->{store} = $o->{actor}->preferredStore if ! $o->{store}; |
3721
|
|
|
|
|
|
|
|
3722
|
0
|
|
|
|
|
0
|
my $objectToken = CDS::ObjectToken->new($o->{store}, $o->{hash}); |
3723
|
0
|
|
|
|
|
0
|
$o->curlRequest('GET', $objectToken->url, ['--output', $o->{hash}->hex]); |
3724
|
|
|
|
|
|
|
} |
3725
|
|
|
|
|
|
|
|
3726
|
|
|
|
|
|
|
sub curlPut { |
3727
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3728
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
3729
|
|
|
|
|
|
|
|
3730
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
3731
|
0
|
0
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken if ! $o->{keyPairToken}; |
3732
|
0
|
0
|
|
|
|
0
|
$o->{store} = $o->{actor}->preferredStore if ! $o->{store}; |
3733
|
|
|
|
|
|
|
|
3734
|
0
|
|
0
|
|
|
0
|
my $bytes = CDS->readBytesFromFile($o->{file}) // return $o->{ui}->error('Unable to read "', $o->{file}, '".'); |
3735
|
0
|
|
|
|
|
0
|
my $hash = CDS::Hash->calculateFor($bytes); |
3736
|
0
|
|
|
|
|
0
|
my $objectToken = CDS::ObjectToken->new($o->{store}, $hash); |
3737
|
0
|
|
|
|
|
0
|
$o->curlRequest('PUT', $objectToken->url, ['--data-binary', '@'.$o->{file}, '-H', 'Content-Type: application/condensation-object']); |
3738
|
|
|
|
|
|
|
} |
3739
|
|
|
|
|
|
|
|
3740
|
|
|
|
|
|
|
sub curlBook { |
3741
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3742
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
3743
|
|
|
|
|
|
|
|
3744
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
3745
|
0
|
0
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken if ! $o->{keyPairToken}; |
3746
|
0
|
0
|
|
|
|
0
|
$o->{store} = $o->{actor}->preferredStore if ! $o->{store}; |
3747
|
|
|
|
|
|
|
|
3748
|
0
|
|
|
|
|
0
|
my $objectToken = CDS::ObjectToken->new($o->{store}, $o->{hash}); |
3749
|
0
|
|
|
|
|
0
|
$o->curlRequest('POST', $objectToken->url, []); |
3750
|
|
|
|
|
|
|
} |
3751
|
|
|
|
|
|
|
|
3752
|
|
|
|
|
|
|
sub curlList { |
3753
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3754
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
3755
|
|
|
|
|
|
|
|
3756
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
3757
|
0
|
0
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken if ! $o->{keyPairToken}; |
3758
|
0
|
0
|
|
|
|
0
|
$o->{store} = $o->{actor}->preferredStore if ! $o->{store}; |
3759
|
0
|
0
|
|
|
|
0
|
$o->{actorHash} = $o->{actor}->preferredActorHash if ! $o->{actorHash}; |
3760
|
|
|
|
|
|
|
|
3761
|
0
|
|
|
|
|
0
|
my $boxToken = CDS::BoxToken->new(CDS::AccountToken->new($o->{store}, $o->{actorHash}), $o->{boxLabel}); |
3762
|
0
|
|
|
|
|
0
|
my $args = ['--output', '.cds-curl-list']; |
3763
|
0
|
0
|
|
|
|
0
|
push @$args, '-H', 'Condensation-Watch: '.$o->{watchTimeout}.' ms' if $o->{watchTimeout}; |
3764
|
0
|
|
|
|
|
0
|
$o->curlRequest('GET', $boxToken->url, $args); |
3765
|
|
|
|
|
|
|
} |
3766
|
|
|
|
|
|
|
|
3767
|
|
|
|
|
|
|
sub curlModify { |
3768
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3769
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
3770
|
|
|
|
|
|
|
|
3771
|
|
|
|
|
|
|
$o->{currentBatch} = { |
3772
|
0
|
|
|
|
|
0
|
addHashes => [], |
3773
|
|
|
|
|
|
|
addEnvelopes => [], |
3774
|
|
|
|
|
|
|
removeHashes => [], |
3775
|
|
|
|
|
|
|
}; |
3776
|
0
|
|
|
|
|
0
|
$o->{batches} = []; |
3777
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
3778
|
0
|
0
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken if ! $o->{keyPairToken}; |
3779
|
0
|
0
|
|
|
|
0
|
$o->{store} = $o->{actor}->preferredStore if ! $o->{store}; |
3780
|
|
|
|
|
|
|
|
3781
|
|
|
|
|
|
|
# Prepare the modifications |
3782
|
0
|
|
|
|
|
0
|
my $modifications = CDS::StoreModifications->new; |
3783
|
|
|
|
|
|
|
|
3784
|
0
|
|
|
|
|
0
|
for my $batch (@{$o->{batches}}, $o->{currentBatch}) { |
|
0
|
|
|
|
|
0
|
|
3785
|
0
|
0
|
|
|
|
0
|
$batch->{actorHash} = $o->{actor}->preferredActorHash if ! $batch->{actorHash}; |
3786
|
|
|
|
|
|
|
|
3787
|
0
|
|
|
|
|
0
|
for my $hash (@{$batch->{addHashes}}) { |
|
0
|
|
|
|
|
0
|
|
3788
|
0
|
|
|
|
|
0
|
$modifications->add($batch->{actorHash}, $batch->{boxLabel}, $hash); |
3789
|
|
|
|
|
|
|
} |
3790
|
|
|
|
|
|
|
|
3791
|
0
|
|
|
|
|
0
|
for my $file (@{$batch->{addFiles}}) { |
|
0
|
|
|
|
|
0
|
|
3792
|
0
|
|
0
|
|
|
0
|
my $bytes = CDS->readBytesFromFile($file) // return $o->{ui}->error('Unable to read "', $file, '".'); |
3793
|
0
|
|
0
|
|
|
0
|
my $object = CDS::Object->fromBytes($bytes) // return $o->{ui}->error('"', $file, '" is not a Condensation object.'); |
3794
|
0
|
|
|
|
|
0
|
my $hash = $object->calculateHash; |
3795
|
0
|
0
|
|
|
|
0
|
$o->{ui}->warning('"', $file, '" is not a valid envelope. The server may reject it.') if ! $o->{actor}->isEnvelope($object); |
3796
|
0
|
|
|
|
|
0
|
$modifications->add($batch->{actorHash}, $batch->{boxLabel}, $hash, $object); |
3797
|
|
|
|
|
|
|
} |
3798
|
|
|
|
|
|
|
|
3799
|
0
|
|
|
|
|
0
|
for my $hash (@{$batch->{removeHashes}}) { |
|
0
|
|
|
|
|
0
|
|
3800
|
0
|
|
|
|
|
0
|
$modifications->remove($batch->{actorHash}, $batch->{boxLabel}, $hash); |
3801
|
|
|
|
|
|
|
} |
3802
|
|
|
|
|
|
|
} |
3803
|
|
|
|
|
|
|
|
3804
|
0
|
0
|
|
|
|
0
|
$o->{ui}->warning('You didn\'t specify any changes. The server should accept, but ignore this.') if $modifications->isEmpty; |
3805
|
|
|
|
|
|
|
|
3806
|
|
|
|
|
|
|
# Write a new file |
3807
|
0
|
|
|
|
|
0
|
my $modificationsObject = $modifications->toRecord->toObject; |
3808
|
0
|
|
|
|
|
0
|
my $modificationsHash = $modificationsObject->calculateHash; |
3809
|
0
|
|
|
|
|
0
|
my $file = '.cds-curl-modifications-'.substr($modificationsHash->hex, 0, 8); |
3810
|
0
|
|
0
|
|
|
0
|
CDS->writeBytesToFile($file, $modificationsObject->header, $modificationsObject->data) // return $o->{ui}->error('Unable to write modifications to "', $file, '".'); |
3811
|
0
|
|
|
|
|
0
|
$o->{ui}->line(scalar @{$modifications->additions}, ' addition(s) and ', scalar @{$modifications->removals}, ' removal(s) written to "', $file, '".'); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
3812
|
|
|
|
|
|
|
|
3813
|
|
|
|
|
|
|
# Submit |
3814
|
0
|
|
|
|
|
0
|
$o->curlRequest('POST', $o->{store}->url.'/accounts', ['--data-binary', '@'.$file, '-H', 'Content-Type: application/condensation-modifications'], $modificationsObject); |
3815
|
|
|
|
|
|
|
} |
3816
|
|
|
|
|
|
|
|
3817
|
|
|
|
|
|
|
sub curlRequest { |
3818
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3819
|
0
|
|
|
|
|
0
|
my $method = shift; |
3820
|
0
|
|
|
|
|
0
|
my $url = shift; |
3821
|
0
|
|
|
|
|
0
|
my $curlArgs = shift; |
3822
|
0
|
|
|
|
|
0
|
my $contentObjectToSign = shift; |
3823
|
|
|
|
|
|
|
|
3824
|
|
|
|
|
|
|
# Parse the URL |
3825
|
0
|
0
|
|
|
|
0
|
$url =~ /^(https?):\/\/([^\/]+)(\/.*|)$/i || return $o->{ui}->error('"', $url, '" does not look like a valid and complete http://… or https://… URL.'); |
3826
|
0
|
|
|
|
|
0
|
my $protocol = lc($1); |
3827
|
0
|
|
|
|
|
0
|
my $host = $2; |
3828
|
0
|
|
|
|
|
0
|
my $path = $3; |
3829
|
|
|
|
|
|
|
|
3830
|
|
|
|
|
|
|
# Strip off user and password, if any |
3831
|
0
|
|
|
|
|
0
|
my $credentials; |
3832
|
0
|
0
|
|
|
|
0
|
if ($host =~ /^(.*)\@([^\@]*)$/) { |
3833
|
0
|
|
|
|
|
0
|
$credentials = $1; |
3834
|
0
|
|
|
|
|
0
|
$host = lc($2); |
3835
|
|
|
|
|
|
|
} else { |
3836
|
0
|
|
|
|
|
0
|
$host = lc($host); |
3837
|
|
|
|
|
|
|
} |
3838
|
|
|
|
|
|
|
|
3839
|
|
|
|
|
|
|
# Remove default port |
3840
|
0
|
0
|
|
|
|
0
|
if ($host =~ /^(.*):(\d+)$/) { |
3841
|
0
|
0
|
0
|
|
|
0
|
$host = $1 if $protocol eq 'http' && $2 == 80; |
3842
|
0
|
0
|
0
|
|
|
0
|
$host = $1 if $protocol eq 'https' && $2 == 443; |
3843
|
|
|
|
|
|
|
} |
3844
|
|
|
|
|
|
|
|
3845
|
|
|
|
|
|
|
# Checks the path and warn the user if obvious things are likely to go wrong |
3846
|
0
|
0
|
|
|
|
0
|
$o->{ui}->warning('Warning: "//" in URL may not work') if $path =~ /\/\//; |
3847
|
0
|
0
|
|
|
|
0
|
$o->{ui}->warning('Warning: /./ or /../ in URL may not work') if $path =~ /\/\.+\//; |
3848
|
0
|
0
|
|
|
|
0
|
$o->{ui}->warning('Warning: /. or /.. at the end of the URL may not work') if $path =~ /\/\.+$/; |
3849
|
|
|
|
|
|
|
|
3850
|
|
|
|
|
|
|
# Signature |
3851
|
|
|
|
|
|
|
|
3852
|
|
|
|
|
|
|
# Date |
3853
|
0
|
|
|
|
|
0
|
my $dateString = CDS::ISODate->millisecondString(CDS->now); |
3854
|
|
|
|
|
|
|
|
3855
|
|
|
|
|
|
|
# Text to sign |
3856
|
0
|
|
|
|
|
0
|
my $bytesToSign = $dateString."\0".uc($method)."\0".$host.$path; |
3857
|
0
|
0
|
|
|
|
0
|
$bytesToSign .= "\0".$contentObjectToSign->header.$contentObjectToSign->data if defined $contentObjectToSign; |
3858
|
|
|
|
|
|
|
|
3859
|
|
|
|
|
|
|
# Signature |
3860
|
0
|
|
|
|
|
0
|
my $keyPair = $o->{keyPairToken}->keyPair; |
3861
|
0
|
|
|
|
|
0
|
my $hashToSign = CDS::Hash->calculateFor($bytesToSign); |
3862
|
0
|
|
|
|
|
0
|
my $signature = $keyPair->signHash($hashToSign); |
3863
|
0
|
|
|
|
|
0
|
push @$curlArgs, '-H', 'Condensation-Date: '.$dateString; |
3864
|
0
|
|
|
|
|
0
|
push @$curlArgs, '-H', 'Condensation-Actor: '.$keyPair->publicKey->hash->hex; |
3865
|
0
|
|
|
|
|
0
|
push @$curlArgs, '-H', 'Condensation-Signature: '.unpack('H*', $signature); |
3866
|
|
|
|
|
|
|
|
3867
|
|
|
|
|
|
|
# Write signature information to files |
3868
|
0
|
0
|
|
|
|
0
|
CDS->writeBytesToFile('.cds-curl-bytesToSign', $bytesToSign) || $o->{ui}->warning('Unable to write the bytes to sign to ".cds-curl-bytesToSign".'); |
3869
|
0
|
0
|
|
|
|
0
|
CDS->writeBytesToFile('.cds-curl-hashToSign', $hashToSign->bytes) || $o->{ui}->warning('Unable to write the hash to sign to ".cds-curl-hashToSign".'); |
3870
|
0
|
0
|
|
|
|
0
|
CDS->writeBytesToFile('.cds-curl-signature', $signature) || $o->{ui}->warning('Unable to write signature to ".cds-curl-signature".'); |
3871
|
|
|
|
|
|
|
|
3872
|
|
|
|
|
|
|
# Method |
3873
|
0
|
0
|
|
|
|
0
|
unshift @$curlArgs, '-X', $method if $method ne 'GET'; |
3874
|
0
|
|
|
|
|
0
|
unshift @$curlArgs, '-#', '--dump-header', '-'; |
3875
|
|
|
|
|
|
|
|
3876
|
|
|
|
|
|
|
# Print |
3877
|
0
|
0
|
0
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('curl', join('', map { ($_ ne '-X' && $_ ne '-' && $_ ne '--dump-header' && $_ ne '-#' && substr($_, 0, 1) eq '-' ? " \\\n " : ' ').&withQuotesIfNecessary($_) } @$curlArgs), scalar @$curlArgs ? " \\\n " : ' ', &withQuotesIfNecessary($url))); |
|
0
|
0
|
|
|
|
0
|
|
3878
|
|
|
|
|
|
|
|
3879
|
|
|
|
|
|
|
# Execute |
3880
|
0
|
|
|
|
|
0
|
system('curl', @$curlArgs, $url); |
3881
|
|
|
|
|
|
|
} |
3882
|
|
|
|
|
|
|
|
3883
|
|
|
|
|
|
|
sub withQuotesIfNecessary { |
3884
|
0
|
|
|
0
|
|
0
|
my $text = shift; |
3885
|
|
|
|
|
|
|
|
3886
|
0
|
0
|
|
|
|
0
|
return $text =~ /[^a-zA-Z0-9\.\/\@:,_-]/ ? '\''.$text.'\'' : $text; |
3887
|
|
|
|
|
|
|
} |
3888
|
|
|
|
|
|
|
|
3889
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
3890
|
|
|
|
|
|
|
package CDS::Commands::DiscoverActorGroup; |
3891
|
|
|
|
|
|
|
|
3892
|
|
|
|
|
|
|
sub register { |
3893
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
3894
|
0
|
|
|
|
|
0
|
my $cds = shift; |
3895
|
0
|
|
|
|
|
0
|
my $help = shift; |
3896
|
|
|
|
|
|
|
|
3897
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
3898
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
3899
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(1); |
3900
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
3901
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
3902
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
3903
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showActorGroupCmd}); |
3904
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
3905
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
3906
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&discover}); |
3907
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
3908
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
3909
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
3910
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&discover}); |
3911
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 1, 0, 'show'); |
3912
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'discover'); |
3913
|
0
|
|
|
|
|
0
|
$help->addArrow($node001, 1, 0, 'discover'); |
3914
|
0
|
|
|
|
|
0
|
$help->addArrow($node001, 1, 0, 'rediscover'); |
3915
|
0
|
|
|
|
|
0
|
$node000->addArrow($node006, 1, 0, 'ACTORGROUP', \&collectActorgroup); |
3916
|
0
|
|
|
|
|
0
|
$node002->addDefault($node003); |
3917
|
0
|
|
|
|
|
0
|
$node002->addDefault($node004); |
3918
|
0
|
|
|
|
|
0
|
$node002->addDefault($node005); |
3919
|
0
|
|
|
|
|
0
|
$node002->addArrow($node009, 1, 0, 'me', \&collectMe); |
3920
|
0
|
|
|
|
|
0
|
$node002->addArrow($node013, 1, 0, 'ACTORGROUP', \&collectActorgroup1); |
3921
|
0
|
|
|
|
|
0
|
$node003->addArrow($node003, 1, 0, 'ACCOUNT', \&collectAccount); |
3922
|
0
|
|
|
|
|
0
|
$node003->addArrow($node009, 1, 1, 'ACCOUNT', \&collectAccount); |
3923
|
0
|
|
|
|
|
0
|
$node004->addArrow($node004, 1, 0, 'KEYPAIR', \&collectKeypair); |
3924
|
0
|
|
|
|
|
0
|
$node004->addArrow($node007, 1, 0, 'KEYPAIR', \&collectKeypair); |
3925
|
0
|
|
|
|
|
0
|
$node005->addArrow($node005, 1, 0, 'ACTOR', \&collectActor); |
3926
|
0
|
|
|
|
|
0
|
$node005->addArrow($node007, 1, 0, 'ACTOR', \&collectActor); |
3927
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'on'); |
3928
|
0
|
|
|
|
|
0
|
$node007->addDefault($node009); |
3929
|
0
|
|
|
|
|
0
|
$node008->addArrow($node009, 1, 0, 'STORE', \&collectStore); |
3930
|
0
|
|
|
|
|
0
|
$node009->addArrow($node010, 1, 0, 'and'); |
3931
|
0
|
|
|
|
|
0
|
$node010->addArrow($node011, 1, 0, 'remember'); |
3932
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'as'); |
3933
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'TEXT', \&collectText); |
3934
|
|
|
|
|
|
|
} |
3935
|
|
|
|
|
|
|
|
3936
|
|
|
|
|
|
|
sub collectAccount { |
3937
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3938
|
0
|
|
|
|
|
0
|
my $label = shift; |
3939
|
0
|
|
|
|
|
0
|
my $value = shift; |
3940
|
|
|
|
|
|
|
|
3941
|
0
|
|
|
|
|
0
|
push @{$o->{accounts}}, $value; |
|
0
|
|
|
|
|
0
|
|
3942
|
|
|
|
|
|
|
} |
3943
|
|
|
|
|
|
|
|
3944
|
|
|
|
|
|
|
sub collectActor { |
3945
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3946
|
0
|
|
|
|
|
0
|
my $label = shift; |
3947
|
0
|
|
|
|
|
0
|
my $value = shift; |
3948
|
|
|
|
|
|
|
|
3949
|
0
|
|
|
|
|
0
|
push @{$o->{actorHashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
3950
|
|
|
|
|
|
|
} |
3951
|
|
|
|
|
|
|
|
3952
|
|
|
|
|
|
|
sub collectActorgroup { |
3953
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3954
|
0
|
|
|
|
|
0
|
my $label = shift; |
3955
|
0
|
|
|
|
|
0
|
my $value = shift; |
3956
|
|
|
|
|
|
|
|
3957
|
0
|
|
|
|
|
0
|
$o->{actorGroupToken} = $value; |
3958
|
|
|
|
|
|
|
} |
3959
|
|
|
|
|
|
|
|
3960
|
|
|
|
|
|
|
sub collectActorgroup1 { |
3961
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3962
|
0
|
|
|
|
|
0
|
my $label = shift; |
3963
|
0
|
|
|
|
|
0
|
my $value = shift; |
3964
|
|
|
|
|
|
|
|
3965
|
0
|
|
|
|
|
0
|
$o->{actorGroupToken} = $value; |
3966
|
0
|
|
|
|
|
0
|
$o->{label} = $value->label; |
3967
|
|
|
|
|
|
|
} |
3968
|
|
|
|
|
|
|
|
3969
|
|
|
|
|
|
|
sub collectKeypair { |
3970
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3971
|
0
|
|
|
|
|
0
|
my $label = shift; |
3972
|
0
|
|
|
|
|
0
|
my $value = shift; |
3973
|
|
|
|
|
|
|
|
3974
|
0
|
|
|
|
|
0
|
push @{$o->{actorHashes}}, $value->keyPair->publicKey->hash; |
|
0
|
|
|
|
|
0
|
|
3975
|
|
|
|
|
|
|
} |
3976
|
|
|
|
|
|
|
|
3977
|
|
|
|
|
|
|
sub collectMe { |
3978
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3979
|
0
|
|
|
|
|
0
|
my $label = shift; |
3980
|
0
|
|
|
|
|
0
|
my $value = shift; |
3981
|
|
|
|
|
|
|
|
3982
|
0
|
|
|
|
|
0
|
$o->{me} = 1; |
3983
|
|
|
|
|
|
|
} |
3984
|
|
|
|
|
|
|
|
3985
|
|
|
|
|
|
|
sub collectStore { |
3986
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3987
|
0
|
|
|
|
|
0
|
my $label = shift; |
3988
|
0
|
|
|
|
|
0
|
my $value = shift; |
3989
|
|
|
|
|
|
|
|
3990
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
3991
|
|
|
|
|
|
|
} |
3992
|
|
|
|
|
|
|
|
3993
|
|
|
|
|
|
|
sub collectText { |
3994
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
3995
|
0
|
|
|
|
|
0
|
my $label = shift; |
3996
|
0
|
|
|
|
|
0
|
my $value = shift; |
3997
|
|
|
|
|
|
|
|
3998
|
0
|
|
|
|
|
0
|
$o->{label} = $value; |
3999
|
|
|
|
|
|
|
} |
4000
|
|
|
|
|
|
|
|
4001
|
|
|
|
|
|
|
sub new { |
4002
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
4003
|
0
|
|
|
|
|
0
|
my $actor = shift; |
4004
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
4005
|
|
|
|
|
|
|
|
4006
|
|
|
|
|
|
|
# END AUTOGENERATED |
4007
|
|
|
|
|
|
|
|
4008
|
|
|
|
|
|
|
# HTML FOLDER NAME discover |
4009
|
|
|
|
|
|
|
# HTML TITLE Discover actor groups |
4010
|
|
|
|
|
|
|
sub help { |
4011
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4012
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4013
|
|
|
|
|
|
|
|
4014
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
4015
|
0
|
|
|
|
|
0
|
$ui->space; |
4016
|
0
|
|
|
|
|
0
|
$ui->command('cds discover ACCOUNT'); |
4017
|
0
|
|
|
|
|
0
|
$ui->command('cds discover ACTOR [on STORE]'); |
4018
|
0
|
|
|
|
|
0
|
$ui->p('Discovers the actor group the given account belongs to. Only active group members are discovered.'); |
4019
|
0
|
|
|
|
|
0
|
$ui->space; |
4020
|
0
|
|
|
|
|
0
|
$ui->command('cds discover ACCOUNT*'); |
4021
|
0
|
|
|
|
|
0
|
$ui->command('cds discover ACTOR* on STORE'); |
4022
|
0
|
|
|
|
|
0
|
$ui->p('Same as above, but starts discovery with multiple accounts. All accounts must belong to the same actor group.'); |
4023
|
0
|
|
|
|
|
0
|
$ui->p('Note that this rarely makes sense. The actor group discovery algorithm reliably discovers an actor group from a single account.'); |
4024
|
0
|
|
|
|
|
0
|
$ui->space; |
4025
|
0
|
|
|
|
|
0
|
$ui->command('cds discover me'); |
4026
|
0
|
|
|
|
|
0
|
$ui->p('Discovers your own actor group.'); |
4027
|
0
|
|
|
|
|
0
|
$ui->space; |
4028
|
0
|
|
|
|
|
0
|
$ui->command('… and remember as TEXT'); |
4029
|
0
|
|
|
|
|
0
|
$ui->p('The discovered actor group is remembered as TEXT. See "cds help remember" for details.'); |
4030
|
0
|
|
|
|
|
0
|
$ui->space; |
4031
|
0
|
|
|
|
|
0
|
$ui->command('cds discover ACTORGROUP'); |
4032
|
0
|
|
|
|
|
0
|
$ui->p('Updates a previously remembered actor group.'); |
4033
|
0
|
|
|
|
|
0
|
$ui->space; |
4034
|
0
|
|
|
|
|
0
|
$ui->command('cds show ACTORGROUP'); |
4035
|
0
|
|
|
|
|
0
|
$ui->p('Shows a previously discovered and remembered actor group.'); |
4036
|
0
|
|
|
|
|
0
|
$ui->space; |
4037
|
|
|
|
|
|
|
} |
4038
|
|
|
|
|
|
|
|
4039
|
|
|
|
|
|
|
sub discover { |
4040
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4041
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4042
|
|
|
|
|
|
|
|
4043
|
0
|
|
|
|
|
0
|
$o->{accounts} = []; |
4044
|
0
|
|
|
|
|
0
|
$o->{actorHashes} = []; |
4045
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4046
|
|
|
|
|
|
|
|
4047
|
|
|
|
|
|
|
# Discover |
4048
|
0
|
|
|
|
|
0
|
my $builder = $o->prepareBuilder; |
4049
|
0
|
|
|
|
|
0
|
my ($actorGroup, $cards, $nodes) = $builder->discover($o->{actor}->keyPair, $o); |
4050
|
|
|
|
|
|
|
|
4051
|
|
|
|
|
|
|
# Show the graph |
4052
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4053
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Graph'); |
4054
|
0
|
|
|
|
|
0
|
for my $node (@$nodes) { |
4055
|
0
|
0
|
|
|
|
0
|
my $status = $node->status eq 'active' ? $o->{ui}->green('active ') : $o->{ui}->gray('idle '); |
4056
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->blue($node->actorHash->hex), ' on ', $node->storeUrl, ' ', $status, $o->{ui}->gray($o->{ui}->niceDateTime($node->revision))); |
4057
|
0
|
|
|
|
|
0
|
$o->{ui}->pushIndent; |
4058
|
0
|
|
|
|
|
0
|
for my $link ($node->links) { |
4059
|
0
|
|
|
|
|
0
|
my $isMostRecentInformation = $link->revision == $link->node->revision; |
4060
|
0
|
0
|
|
|
|
0
|
my $color = $isMostRecentInformation ? 246 : 250; |
4061
|
0
|
|
|
|
|
0
|
$o->{ui}->line($link->node->actorHash->shortHex, ' on ', $link->node->storeUrl, ' ', $o->{ui}->foreground($color, $o->{ui}->left(8, $link->status), $o->{ui}->niceDateTime($link->revision))); |
4062
|
|
|
|
|
|
|
} |
4063
|
0
|
|
|
|
|
0
|
$o->{ui}->popIndent; |
4064
|
|
|
|
|
|
|
} |
4065
|
|
|
|
|
|
|
|
4066
|
|
|
|
|
|
|
# Show all accounts |
4067
|
0
|
|
|
|
|
0
|
$o->showActorGroup($actorGroup); |
4068
|
|
|
|
|
|
|
|
4069
|
|
|
|
|
|
|
# Show all cards |
4070
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4071
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Cards'); |
4072
|
0
|
|
|
|
|
0
|
for my $card (@$cards) { |
4073
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds show record ', $card->cardHash->hex, ' on ', $card->storeUrl)); |
4074
|
|
|
|
|
|
|
} |
4075
|
|
|
|
|
|
|
|
4076
|
|
|
|
|
|
|
# Remember the actor group if desired |
4077
|
0
|
0
|
|
|
|
0
|
if ($o->{label}) { |
4078
|
0
|
|
|
|
|
0
|
my $selector = $o->{actor}->labelSelector($o->{label}); |
4079
|
|
|
|
|
|
|
|
4080
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->new; |
4081
|
0
|
|
|
|
|
0
|
my $actorGroupRecord = $record->add('actor group'); |
4082
|
0
|
|
|
|
|
0
|
$actorGroupRecord->add('discovered')->addInteger(CDS->now); |
4083
|
0
|
|
|
|
|
0
|
$actorGroupRecord->addRecord($actorGroup->toBuilder->toRecord(1)->children); |
4084
|
0
|
|
|
|
|
0
|
$selector->set($record); |
4085
|
|
|
|
|
|
|
|
4086
|
0
|
|
|
|
|
0
|
for my $publicKey ($actorGroup->publicKeys) { |
4087
|
0
|
|
|
|
|
0
|
$selector->addObject($publicKey->hash, $publicKey->object); |
4088
|
|
|
|
|
|
|
} |
4089
|
|
|
|
|
|
|
|
4090
|
0
|
|
0
|
|
|
0
|
$o->{actor}->saveOrShowError // return; |
4091
|
|
|
|
|
|
|
} |
4092
|
|
|
|
|
|
|
|
4093
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4094
|
|
|
|
|
|
|
} |
4095
|
|
|
|
|
|
|
|
4096
|
|
|
|
|
|
|
sub prepareBuilder { |
4097
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4098
|
|
|
|
|
|
|
|
4099
|
|
|
|
|
|
|
# Actor group |
4100
|
0
|
0
|
|
|
|
0
|
return $o->{actorGroupToken}->actorGroup->toBuilder if $o->{actorGroupToken}; |
4101
|
|
|
|
|
|
|
|
4102
|
|
|
|
|
|
|
# Other than actor group |
4103
|
0
|
|
|
|
|
0
|
my $builder = CDS::ActorGroupBuilder->new; |
4104
|
0
|
|
|
|
|
0
|
$builder->addKnownPublicKey($o->{actor}->keyPair->publicKey); |
4105
|
|
|
|
|
|
|
|
4106
|
|
|
|
|
|
|
# Me |
4107
|
0
|
0
|
|
|
|
0
|
$builder->addMember($o->{actor}->messagingStoreUrl, $o->{actor}->keyPair->publicKey->hash) if $o->{me}; |
4108
|
|
|
|
|
|
|
|
4109
|
|
|
|
|
|
|
# Accounts |
4110
|
0
|
|
|
|
|
0
|
for my $account (@{$o->{accounts}}) { |
|
0
|
|
|
|
|
0
|
|
4111
|
0
|
|
|
|
|
0
|
$builder->addMember($account->cliStore->url, $account->actorHash); |
4112
|
|
|
|
|
|
|
} |
4113
|
|
|
|
|
|
|
|
4114
|
|
|
|
|
|
|
# Actors on store |
4115
|
0
|
0
|
|
|
|
0
|
if (scalar @{$o->{actorHashes}}) { |
|
0
|
|
|
|
|
0
|
|
4116
|
0
|
|
0
|
|
|
0
|
my $store = $o->{store} // $o->{actor}->preferredStore; |
4117
|
0
|
|
|
|
|
0
|
for my $actorHash (@{$o->{actorHashes}}) { |
|
0
|
|
|
|
|
0
|
|
4118
|
0
|
|
|
|
|
0
|
$builder->addMember($actorHash, $store->url); |
4119
|
|
|
|
|
|
|
} |
4120
|
|
|
|
|
|
|
} |
4121
|
|
|
|
|
|
|
|
4122
|
0
|
|
|
|
|
0
|
return $builder; |
4123
|
|
|
|
|
|
|
} |
4124
|
|
|
|
|
|
|
|
4125
|
|
|
|
|
|
|
sub showActorGroupCmd { |
4126
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4127
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4128
|
|
|
|
|
|
|
|
4129
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4130
|
0
|
|
|
|
|
0
|
$o->showActorGroup($o->{actorGroupToken}->actorGroup); |
4131
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4132
|
|
|
|
|
|
|
} |
4133
|
|
|
|
|
|
|
|
4134
|
|
|
|
|
|
|
sub showActorGroup { |
4135
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4136
|
0
|
0
|
0
|
|
|
0
|
my $actorGroup = shift; die 'wrong type '.ref($actorGroup).' for $actorGroup' if defined $actorGroup && ref $actorGroup ne 'CDS::ActorGroup'; |
|
0
|
|
|
|
|
0
|
|
4137
|
|
|
|
|
|
|
|
4138
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4139
|
0
|
0
|
|
|
|
0
|
$o->{ui}->title(length $o->{label} ? 'Actors of '.$o->{label} : 'Actor group'); |
4140
|
0
|
|
|
|
|
0
|
for my $member ($actorGroup->members) { |
4141
|
0
|
0
|
|
|
|
0
|
my $date = $member->revision ? $o->{ui}->niceDateTimeLocal($member->revision) : ' '; |
4142
|
0
|
0
|
|
|
|
0
|
my $status = $member->isActive ? $o->{ui}->green('active ') : $o->{ui}->gray('idle '); |
4143
|
0
|
|
|
|
|
0
|
my $storeReference = $o->{actor}->blueStoreUrlReference($member->storeUrl); |
4144
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray($date), ' ', $status, ' ', $member->actorOnStore->publicKey->hash->hex, ' on ', $storeReference); |
4145
|
|
|
|
|
|
|
} |
4146
|
|
|
|
|
|
|
|
4147
|
0
|
0
|
|
|
|
0
|
if ($actorGroup->entrustedActorsRevision) { |
4148
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4149
|
0
|
0
|
|
|
|
0
|
$o->{ui}->title(length $o->{label} ? 'Actors entrusted by '.$o->{label} : 'Entrusted actors'); |
4150
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray($o->{ui}->niceDateTimeLocal($actorGroup->entrustedActorsRevision))); |
4151
|
0
|
|
|
|
|
0
|
for my $actor ($actorGroup->entrustedActors) { |
4152
|
0
|
|
|
|
|
0
|
my $storeReference = $o->{actor}->storeUrlReference($actor->storeUrl); |
4153
|
0
|
|
|
|
|
0
|
$o->{ui}->line($actor->actorOnStore->publicKey->hash->hex, $o->{ui}->gray(' on ', $storeReference)); |
4154
|
|
|
|
|
|
|
} |
4155
|
|
|
|
|
|
|
|
4156
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray('(none)')) if ! scalar $actorGroup->entrustedActors; |
4157
|
|
|
|
|
|
|
} |
4158
|
|
|
|
|
|
|
} |
4159
|
|
|
|
|
|
|
|
4160
|
|
|
|
|
|
|
sub onDiscoverActorGroupVerifyStore { |
4161
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4162
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
4163
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
4164
|
|
|
|
|
|
|
|
4165
|
0
|
|
|
|
|
0
|
return $o->{actor}->storeForUrl($storeUrl); |
4166
|
|
|
|
|
|
|
} |
4167
|
|
|
|
|
|
|
|
4168
|
|
|
|
|
|
|
sub onDiscoverActorGroupInvalidPublicKey { |
4169
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4170
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
4171
|
0
|
|
|
|
|
0
|
my $store = shift; |
4172
|
0
|
|
|
|
|
0
|
my $reason = shift; |
4173
|
|
|
|
|
|
|
|
4174
|
0
|
|
|
|
|
0
|
$o->{ui}->warning('Public key ', $actorHash->hex, ' on ', $store->url, ' is invalid: ', $reason); |
4175
|
|
|
|
|
|
|
} |
4176
|
|
|
|
|
|
|
|
4177
|
|
|
|
|
|
|
sub onDiscoverActorGroupInvalidCard { |
4178
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4179
|
0
|
0
|
0
|
|
|
0
|
my $actorOnStore = shift; die 'wrong type '.ref($actorOnStore).' for $actorOnStore' if defined $actorOnStore && ref $actorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
0
|
|
4180
|
0
|
0
|
0
|
|
|
0
|
my $envelopeHash = shift; die 'wrong type '.ref($envelopeHash).' for $envelopeHash' if defined $envelopeHash && ref $envelopeHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
4181
|
0
|
|
|
|
|
0
|
my $reason = shift; |
4182
|
|
|
|
|
|
|
|
4183
|
0
|
|
|
|
|
0
|
$o->{ui}->warning('Card ', $envelopeHash->hex, ' on ', $actorOnStore->store->url, ' is invalid: ', $reason); |
4184
|
|
|
|
|
|
|
} |
4185
|
|
|
|
|
|
|
|
4186
|
|
|
|
|
|
|
sub onDiscoverActorGroupStoreError { |
4187
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4188
|
0
|
|
|
|
|
0
|
my $store = shift; |
4189
|
0
|
|
|
|
|
0
|
my $error = shift; |
4190
|
|
|
|
|
|
|
|
4191
|
|
|
|
|
|
|
} |
4192
|
|
|
|
|
|
|
|
4193
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
4194
|
|
|
|
|
|
|
package CDS::Commands::EntrustedActors; |
4195
|
|
|
|
|
|
|
|
4196
|
|
|
|
|
|
|
sub register { |
4197
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
4198
|
0
|
|
|
|
|
0
|
my $cds = shift; |
4199
|
0
|
|
|
|
|
0
|
my $help = shift; |
4200
|
|
|
|
|
|
|
|
4201
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
4202
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
4203
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
4204
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
4205
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
4206
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
4207
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
4208
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
4209
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
4210
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
4211
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
4212
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&show}); |
4213
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
4214
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
4215
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&doNotEntrust}); |
4216
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&entrust}); |
4217
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(0); |
4218
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'show'); |
4219
|
0
|
|
|
|
|
0
|
$cds->addArrow($node003, 1, 0, 'do'); |
4220
|
0
|
|
|
|
|
0
|
$cds->addArrow($node005, 1, 0, 'entrust'); |
4221
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'entrusted'); |
4222
|
0
|
|
|
|
|
0
|
$node000->addArrow($node010, 1, 0, 'actors'); |
4223
|
0
|
|
|
|
|
0
|
$node001->addArrow($node002, 1, 0, 'entrusted'); |
4224
|
0
|
|
|
|
|
0
|
$node002->addArrow($node011, 1, 0, 'actors'); |
4225
|
0
|
|
|
|
|
0
|
$node003->addArrow($node004, 1, 0, 'not'); |
4226
|
0
|
|
|
|
|
0
|
$node004->addArrow($node008, 1, 0, 'entrust'); |
4227
|
0
|
|
|
|
|
0
|
$node005->addDefault($node006); |
4228
|
0
|
|
|
|
|
0
|
$node005->addDefault($node007); |
4229
|
0
|
|
|
|
|
0
|
$node005->addArrow($node012, 1, 0, 'ACTOR', \&collectActor); |
4230
|
0
|
|
|
|
|
0
|
$node006->addArrow($node006, 1, 0, 'ACCOUNT', \&collectAccount); |
4231
|
0
|
|
|
|
|
0
|
$node006->addArrow($node015, 1, 1, 'ACCOUNT', \&collectAccount); |
4232
|
0
|
|
|
|
|
0
|
$node007->addArrow($node007, 1, 0, 'ACTOR', \&collectActor1); |
4233
|
0
|
|
|
|
|
0
|
$node007->addArrow($node015, 1, 0, 'ACTOR', \&collectActor1); |
4234
|
0
|
|
|
|
|
0
|
$node008->addDefault($node009); |
4235
|
0
|
|
|
|
|
0
|
$node009->addArrow($node009, 1, 0, 'ACTOR', \&collectActor2); |
4236
|
0
|
|
|
|
|
0
|
$node009->addArrow($node014, 1, 0, 'ACTOR', \&collectActor2); |
4237
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'on'); |
4238
|
0
|
|
|
|
|
0
|
$node013->addArrow($node015, 1, 0, 'STORE', \&collectStore); |
4239
|
0
|
|
|
|
|
0
|
$node015->addArrow($node016, 1, 0, 'and'); |
4240
|
0
|
|
|
|
|
0
|
$node016->addDefault($node005); |
4241
|
|
|
|
|
|
|
} |
4242
|
|
|
|
|
|
|
|
4243
|
|
|
|
|
|
|
sub collectAccount { |
4244
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4245
|
0
|
|
|
|
|
0
|
my $label = shift; |
4246
|
0
|
|
|
|
|
0
|
my $value = shift; |
4247
|
|
|
|
|
|
|
|
4248
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, $value; |
|
0
|
|
|
|
|
0
|
|
4249
|
|
|
|
|
|
|
} |
4250
|
|
|
|
|
|
|
|
4251
|
|
|
|
|
|
|
sub collectActor { |
4252
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4253
|
0
|
|
|
|
|
0
|
my $label = shift; |
4254
|
0
|
|
|
|
|
0
|
my $value = shift; |
4255
|
|
|
|
|
|
|
|
4256
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value; |
4257
|
|
|
|
|
|
|
} |
4258
|
|
|
|
|
|
|
|
4259
|
|
|
|
|
|
|
sub collectActor1 { |
4260
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4261
|
0
|
|
|
|
|
0
|
my $label = shift; |
4262
|
0
|
|
|
|
|
0
|
my $value = shift; |
4263
|
|
|
|
|
|
|
|
4264
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, CDS::AccountToken->new($o->{actor}->preferredStore, $value); |
|
0
|
|
|
|
|
0
|
|
4265
|
|
|
|
|
|
|
} |
4266
|
|
|
|
|
|
|
|
4267
|
|
|
|
|
|
|
sub collectActor2 { |
4268
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4269
|
0
|
|
|
|
|
0
|
my $label = shift; |
4270
|
0
|
|
|
|
|
0
|
my $value = shift; |
4271
|
|
|
|
|
|
|
|
4272
|
0
|
|
|
|
|
0
|
push @{$o->{actorHashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
4273
|
|
|
|
|
|
|
} |
4274
|
|
|
|
|
|
|
|
4275
|
|
|
|
|
|
|
sub collectStore { |
4276
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4277
|
0
|
|
|
|
|
0
|
my $label = shift; |
4278
|
0
|
|
|
|
|
0
|
my $value = shift; |
4279
|
|
|
|
|
|
|
|
4280
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, CDS::AccountToken->new($value, $o->{actorHash}); |
|
0
|
|
|
|
|
0
|
|
4281
|
0
|
|
|
|
|
0
|
delete $o->{actorHash}; |
4282
|
|
|
|
|
|
|
} |
4283
|
|
|
|
|
|
|
|
4284
|
|
|
|
|
|
|
sub new { |
4285
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
4286
|
0
|
|
|
|
|
0
|
my $actor = shift; |
4287
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
4288
|
|
|
|
|
|
|
|
4289
|
|
|
|
|
|
|
# END AUTOGENERATED |
4290
|
|
|
|
|
|
|
|
4291
|
|
|
|
|
|
|
# HTML FOLDER NAME entrusted-actors |
4292
|
|
|
|
|
|
|
# HTML TITLE Entrusted actors |
4293
|
|
|
|
|
|
|
sub help { |
4294
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4295
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4296
|
|
|
|
|
|
|
|
4297
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
4298
|
0
|
|
|
|
|
0
|
$ui->space; |
4299
|
0
|
|
|
|
|
0
|
$ui->command('cds show entrusted actors'); |
4300
|
0
|
|
|
|
|
0
|
$ui->p('Shows all entrusted actors.'); |
4301
|
0
|
|
|
|
|
0
|
$ui->space; |
4302
|
0
|
|
|
|
|
0
|
$ui->command('cds entrust ACCOUNT*'); |
4303
|
0
|
|
|
|
|
0
|
$ui->command('cds entrust ACTOR on STORE'); |
4304
|
0
|
|
|
|
|
0
|
$ui->p('Adds the indicated entrusted actors. Entrusted actors can read our private data and messages. The public key of the entrusted actor must be available on the store.'); |
4305
|
0
|
|
|
|
|
0
|
$ui->space; |
4306
|
0
|
|
|
|
|
0
|
$ui->command('cds do not entrust ACTOR*'); |
4307
|
0
|
|
|
|
|
0
|
$ui->p('Removes the indicated entrusted actors.'); |
4308
|
0
|
|
|
|
|
0
|
$ui->space; |
4309
|
0
|
|
|
|
|
0
|
$ui->p('After modifying the entrusted actors, you should "cds announce" yourself to publish the changes.'); |
4310
|
0
|
|
|
|
|
0
|
$ui->space; |
4311
|
|
|
|
|
|
|
} |
4312
|
|
|
|
|
|
|
|
4313
|
|
|
|
|
|
|
sub show { |
4314
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4315
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4316
|
|
|
|
|
|
|
|
4317
|
0
|
|
|
|
|
0
|
my $builder = CDS::ActorGroupBuilder->new; |
4318
|
0
|
|
|
|
|
0
|
$builder->parseEntrustedActorList($o->{actor}->entrustedActorsSelector->record, 1); |
4319
|
|
|
|
|
|
|
|
4320
|
0
|
|
|
|
|
0
|
my @actors = $builder->entrustedActors; |
4321
|
0
|
|
|
|
|
0
|
for my $actor (@actors) { |
4322
|
0
|
|
|
|
|
0
|
my $storeReference = $o->{actor}->storeUrlReference($actor->storeUrl); |
4323
|
0
|
|
|
|
|
0
|
$o->{ui}->line($actor->hash->hex, $o->{ui}->gray(' on ', $storeReference)); |
4324
|
|
|
|
|
|
|
} |
4325
|
|
|
|
|
|
|
|
4326
|
0
|
0
|
|
|
|
0
|
return if scalar @actors; |
4327
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray('none')); |
4328
|
|
|
|
|
|
|
} |
4329
|
|
|
|
|
|
|
|
4330
|
|
|
|
|
|
|
sub entrust { |
4331
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4332
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4333
|
|
|
|
|
|
|
|
4334
|
0
|
|
|
|
|
0
|
$o->{accountTokens} = []; |
4335
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4336
|
|
|
|
|
|
|
|
4337
|
|
|
|
|
|
|
# Get the list of currently entrusted actors |
4338
|
0
|
|
|
|
|
0
|
my $entrusted = $o->createEntrustedActorsIndex; |
4339
|
|
|
|
|
|
|
|
4340
|
|
|
|
|
|
|
# Add new actors |
4341
|
0
|
|
|
|
|
0
|
for my $accountToken (@{$o->{accountTokens}}) { |
|
0
|
|
|
|
|
0
|
|
4342
|
0
|
|
|
|
|
0
|
my $actorHash = $accountToken->actorHash; |
4343
|
|
|
|
|
|
|
|
4344
|
|
|
|
|
|
|
# Check if the key is already entrusted |
4345
|
0
|
0
|
|
|
|
0
|
if ($entrusted->{$accountToken->url}) { |
4346
|
0
|
|
|
|
|
0
|
$o->{ui}->pOrange($accountToken->url, ' is already entrusted.'); |
4347
|
0
|
|
|
|
|
0
|
next; |
4348
|
|
|
|
|
|
|
} |
4349
|
|
|
|
|
|
|
|
4350
|
|
|
|
|
|
|
# Get the public key |
4351
|
0
|
|
|
|
|
0
|
my ($publicKey, $invalidReason, $storeError) = $o->{actor}->keyPair->getPublicKey($actorHash, $accountToken->cliStore); |
4352
|
0
|
0
|
|
|
|
0
|
if (defined $storeError) { |
4353
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('Unable to get the public key ', $actorHash->hex, ' from ', $accountToken->cliStore->url, ': ', $storeError); |
4354
|
0
|
|
|
|
|
0
|
next; |
4355
|
|
|
|
|
|
|
} |
4356
|
|
|
|
|
|
|
|
4357
|
0
|
0
|
|
|
|
0
|
if (defined $invalidReason) { |
4358
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('Unable to get the public key ', $actorHash->hex, ' from ', $accountToken->cliStore->url, ': ', $invalidReason); |
4359
|
0
|
|
|
|
|
0
|
next; |
4360
|
|
|
|
|
|
|
} |
4361
|
|
|
|
|
|
|
|
4362
|
|
|
|
|
|
|
# Add it |
4363
|
0
|
|
|
|
|
0
|
$o->{actor}->entrust($accountToken->cliStore->url, $publicKey); |
4364
|
0
|
0
|
|
|
|
0
|
$o->{ui}->pGreen($entrusted->{$actorHash->hex} ? 'Updated ' : 'Added ', $actorHash->hex, ' as entrusted actor.'); |
4365
|
|
|
|
|
|
|
} |
4366
|
|
|
|
|
|
|
|
4367
|
|
|
|
|
|
|
# Save |
4368
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
4369
|
|
|
|
|
|
|
} |
4370
|
|
|
|
|
|
|
|
4371
|
|
|
|
|
|
|
sub doNotEntrust { |
4372
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4373
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4374
|
|
|
|
|
|
|
|
4375
|
0
|
|
|
|
|
0
|
$o->{actorHashes} = []; |
4376
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4377
|
|
|
|
|
|
|
|
4378
|
|
|
|
|
|
|
# Get the list of currently entrusted actors |
4379
|
0
|
|
|
|
|
0
|
my $entrusted = $o->createEntrustedActorsIndex; |
4380
|
|
|
|
|
|
|
|
4381
|
|
|
|
|
|
|
# Remove entrusted actors |
4382
|
0
|
|
|
|
|
0
|
for my $actorHash (@{$o->{actorHashes}}) { |
|
0
|
|
|
|
|
0
|
|
4383
|
0
|
0
|
|
|
|
0
|
if ($entrusted->{$actorHash->hex}) { |
4384
|
0
|
|
|
|
|
0
|
$o->{actor}->doNotEntrust($actorHash); |
4385
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Removed ', $actorHash->hex, ' from the list of entrusted actors.'); |
4386
|
|
|
|
|
|
|
} else { |
4387
|
0
|
|
|
|
|
0
|
$o->{ui}->pOrange($actorHash->hex, ' is not entrusted.'); |
4388
|
|
|
|
|
|
|
} |
4389
|
|
|
|
|
|
|
} |
4390
|
|
|
|
|
|
|
|
4391
|
|
|
|
|
|
|
# Save |
4392
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
4393
|
|
|
|
|
|
|
} |
4394
|
|
|
|
|
|
|
|
4395
|
|
|
|
|
|
|
sub createEntrustedActorsIndex { |
4396
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4397
|
|
|
|
|
|
|
|
4398
|
0
|
|
|
|
|
0
|
my $builder = CDS::ActorGroupBuilder->new; |
4399
|
0
|
|
|
|
|
0
|
$builder->parseEntrustedActorList($o->{actor}->entrustedActorsSelector->record, 1); |
4400
|
|
|
|
|
|
|
|
4401
|
0
|
|
|
|
|
0
|
my $index = {}; |
4402
|
0
|
|
|
|
|
0
|
for my $actor ($builder->entrustedActors) { |
4403
|
0
|
|
|
|
|
0
|
my $url = $actor->storeUrl.'/accounts/'.$actor->hash->hex; |
4404
|
0
|
|
|
|
|
0
|
$index->{$actor->hash->hex} = 1; |
4405
|
0
|
|
|
|
|
0
|
$index->{$url} = 1; |
4406
|
|
|
|
|
|
|
} |
4407
|
|
|
|
|
|
|
|
4408
|
0
|
|
|
|
|
0
|
return $index; |
4409
|
|
|
|
|
|
|
} |
4410
|
|
|
|
|
|
|
|
4411
|
|
|
|
|
|
|
package CDS::Commands::FolderStore; |
4412
|
|
|
|
|
|
|
|
4413
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
4414
|
|
|
|
|
|
|
|
4415
|
|
|
|
|
|
|
sub register { |
4416
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
4417
|
0
|
|
|
|
|
0
|
my $cds = shift; |
4418
|
0
|
|
|
|
|
0
|
my $help = shift; |
4419
|
|
|
|
|
|
|
|
4420
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
4421
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
4422
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
4423
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
4424
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
4425
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
4426
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
4427
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
4428
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
4429
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
4430
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
4431
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
4432
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
4433
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
4434
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(0); |
4435
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(0); |
4436
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(0); |
4437
|
0
|
|
|
|
|
0
|
my $node017 = CDS::Parser::Node->new(0); |
4438
|
0
|
|
|
|
|
0
|
my $node018 = CDS::Parser::Node->new(0); |
4439
|
0
|
|
|
|
|
0
|
my $node019 = CDS::Parser::Node->new(0); |
4440
|
0
|
|
|
|
|
0
|
my $node020 = CDS::Parser::Node->new(0); |
4441
|
0
|
|
|
|
|
0
|
my $node021 = CDS::Parser::Node->new(0); |
4442
|
0
|
|
|
|
|
0
|
my $node022 = CDS::Parser::Node->new(0); |
4443
|
0
|
|
|
|
|
0
|
my $node023 = CDS::Parser::Node->new(0); |
4444
|
0
|
|
|
|
|
0
|
my $node024 = CDS::Parser::Node->new(0); |
4445
|
0
|
|
|
|
|
0
|
my $node025 = CDS::Parser::Node->new(1); |
4446
|
0
|
|
|
|
|
0
|
my $node026 = CDS::Parser::Node->new(0); |
4447
|
0
|
|
|
|
|
0
|
my $node027 = CDS::Parser::Node->new(0); |
4448
|
0
|
|
|
|
|
0
|
my $node028 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
4449
|
0
|
|
|
|
|
0
|
my $node029 = CDS::Parser::Node->new(1); |
4450
|
0
|
|
|
|
|
0
|
my $node030 = CDS::Parser::Node->new(0); |
4451
|
0
|
|
|
|
|
0
|
my $node031 = CDS::Parser::Node->new(0); |
4452
|
0
|
|
|
|
|
0
|
my $node032 = CDS::Parser::Node->new(0); |
4453
|
0
|
|
|
|
|
0
|
my $node033 = CDS::Parser::Node->new(0); |
4454
|
0
|
|
|
|
|
0
|
my $node034 = CDS::Parser::Node->new(0); |
4455
|
0
|
|
|
|
|
0
|
my $node035 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&checkPermissions}); |
4456
|
0
|
|
|
|
|
0
|
my $node036 = CDS::Parser::Node->new(0); |
4457
|
0
|
|
|
|
|
0
|
my $node037 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&fixPermissions}); |
4458
|
0
|
|
|
|
|
0
|
my $node038 = CDS::Parser::Node->new(0); |
4459
|
0
|
|
|
|
|
0
|
my $node039 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showPermissions}); |
4460
|
0
|
|
|
|
|
0
|
my $node040 = CDS::Parser::Node->new(0); |
4461
|
0
|
|
|
|
|
0
|
my $node041 = CDS::Parser::Node->new(1); |
4462
|
0
|
|
|
|
|
0
|
my $node042 = CDS::Parser::Node->new(0); |
4463
|
0
|
|
|
|
|
0
|
my $node043 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&addAccount}); |
4464
|
0
|
|
|
|
|
0
|
my $node044 = CDS::Parser::Node->new(0); |
4465
|
0
|
|
|
|
|
0
|
my $node045 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&removeAccount}); |
4466
|
0
|
|
|
|
|
0
|
my $node046 = CDS::Parser::Node->new(0); |
4467
|
0
|
|
|
|
|
0
|
my $node047 = CDS::Parser::Node->new(1); |
4468
|
0
|
|
|
|
|
0
|
my $node048 = CDS::Parser::Node->new(0); |
4469
|
0
|
|
|
|
|
0
|
my $node049 = CDS::Parser::Node->new(0); |
4470
|
0
|
|
|
|
|
0
|
my $node050 = CDS::Parser::Node->new(0); |
4471
|
0
|
|
|
|
|
0
|
my $node051 = CDS::Parser::Node->new(0); |
4472
|
0
|
|
|
|
|
0
|
my $node052 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&checkPermissions}); |
4473
|
0
|
|
|
|
|
0
|
my $node053 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&fixPermissions}); |
4474
|
0
|
|
|
|
|
0
|
my $node054 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showPermissions}); |
4475
|
0
|
|
|
|
|
0
|
my $node055 = CDS::Parser::Node->new(1); |
4476
|
0
|
|
|
|
|
0
|
my $node056 = CDS::Parser::Node->new(0); |
4477
|
0
|
|
|
|
|
0
|
my $node057 = CDS::Parser::Node->new(0); |
4478
|
0
|
|
|
|
|
0
|
my $node058 = CDS::Parser::Node->new(0); |
4479
|
0
|
|
|
|
|
0
|
my $node059 = CDS::Parser::Node->new(0); |
4480
|
0
|
|
|
|
|
0
|
my $node060 = CDS::Parser::Node->new(0); |
4481
|
0
|
|
|
|
|
0
|
my $node061 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&addAccount}); |
4482
|
0
|
|
|
|
|
0
|
my $node062 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&removeAccount}); |
4483
|
0
|
|
|
|
|
0
|
my $node063 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&setPermissions}); |
4484
|
0
|
|
|
|
|
0
|
my $node064 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&createStore}); |
4485
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'create'); |
4486
|
0
|
|
|
|
|
0
|
$cds->addArrow($node003, 1, 0, 'check'); |
4487
|
0
|
|
|
|
|
0
|
$cds->addArrow($node004, 1, 0, 'fix'); |
4488
|
0
|
|
|
|
|
0
|
$cds->addArrow($node005, 1, 0, 'show'); |
4489
|
0
|
|
|
|
|
0
|
$cds->addArrow($node007, 1, 0, 'set'); |
4490
|
0
|
|
|
|
|
0
|
$cds->addArrow($node009, 1, 0, 'add'); |
4491
|
0
|
|
|
|
|
0
|
$cds->addArrow($node010, 1, 0, 'add'); |
4492
|
0
|
|
|
|
|
0
|
$cds->addArrow($node011, 1, 0, 'add'); |
4493
|
0
|
|
|
|
|
0
|
$cds->addArrow($node012, 1, 0, 'add'); |
4494
|
0
|
|
|
|
|
0
|
$cds->addArrow($node013, 1, 0, 'add'); |
4495
|
0
|
|
|
|
|
0
|
$cds->addArrow($node023, 1, 0, 'remove'); |
4496
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'create'); |
4497
|
0
|
|
|
|
|
0
|
$node000->addArrow($node028, 1, 0, 'store'); |
4498
|
0
|
|
|
|
|
0
|
$node001->addArrow($node002, 1, 0, 'store'); |
4499
|
0
|
|
|
|
|
0
|
$node002->addArrow($node029, 1, 0, 'FOLDERNAME', \&collectFoldername); |
4500
|
0
|
|
|
|
|
0
|
$node003->addArrow($node035, 1, 0, 'permissions'); |
4501
|
0
|
|
|
|
|
0
|
$node004->addArrow($node037, 1, 0, 'permissions'); |
4502
|
0
|
|
|
|
|
0
|
$node005->addArrow($node006, 1, 0, 'permission'); |
4503
|
0
|
|
|
|
|
0
|
$node006->addArrow($node039, 1, 0, 'scheme'); |
4504
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'permission'); |
4505
|
0
|
|
|
|
|
0
|
$node008->addArrow($node041, 1, 0, 'scheme'); |
4506
|
0
|
|
|
|
|
0
|
$node009->addArrow($node014, 1, 0, 'account'); |
4507
|
0
|
|
|
|
|
0
|
$node010->addArrow($node015, 1, 0, 'account'); |
4508
|
0
|
|
|
|
|
0
|
$node011->addArrow($node016, 1, 0, 'account'); |
4509
|
0
|
|
|
|
|
0
|
$node012->addArrow($node017, 1, 0, 'account'); |
4510
|
0
|
|
|
|
|
0
|
$node013->addArrow($node018, 1, 0, 'account'); |
4511
|
0
|
|
|
|
|
0
|
$node014->addArrow($node019, 1, 0, 'for'); |
4512
|
0
|
|
|
|
|
0
|
$node015->addArrow($node020, 1, 0, 'for'); |
4513
|
0
|
|
|
|
|
0
|
$node016->addArrow($node021, 1, 0, 'for'); |
4514
|
0
|
|
|
|
|
0
|
$node017->addArrow($node043, 1, 1, 'ACCOUNT', \&collectAccount); |
4515
|
0
|
|
|
|
|
0
|
$node018->addArrow($node022, 1, 0, 'for'); |
4516
|
0
|
|
|
|
|
0
|
$node019->addArrow($node043, 1, 0, 'OBJECTFILE', \&collectObjectfile); |
4517
|
0
|
|
|
|
|
0
|
$node020->addArrow($node043, 1, 0, 'KEYPAIR', \&collectKeypair); |
4518
|
0
|
|
|
|
|
0
|
$node021->addArrow($node025, 1, 0, 'ACTOR', \&collectActor); |
4519
|
0
|
|
|
|
|
0
|
$node022->addArrow($node043, 1, 0, 'OBJECT', \&collectObject); |
4520
|
0
|
|
|
|
|
0
|
$node023->addArrow($node024, 1, 0, 'account'); |
4521
|
0
|
|
|
|
|
0
|
$node024->addArrow($node045, 1, 0, 'HASH', \&collectHash); |
4522
|
0
|
|
|
|
|
0
|
$node025->addArrow($node026, 1, 0, 'on'); |
4523
|
0
|
|
|
|
|
0
|
$node025->addArrow($node027, 0, 0, 'from'); |
4524
|
0
|
|
|
|
|
0
|
$node026->addArrow($node043, 1, 0, 'STORE', \&collectStore); |
4525
|
0
|
|
|
|
|
0
|
$node027->addArrow($node043, 0, 0, 'STORE', \&collectStore); |
4526
|
0
|
|
|
|
|
0
|
$node029->addArrow($node030, 1, 0, 'for'); |
4527
|
0
|
|
|
|
|
0
|
$node029->addArrow($node031, 1, 0, 'for'); |
4528
|
0
|
|
|
|
|
0
|
$node029->addArrow($node032, 1, 0, 'for'); |
4529
|
0
|
|
|
|
|
0
|
$node029->addDefault($node047); |
4530
|
0
|
|
|
|
|
0
|
$node030->addArrow($node033, 1, 0, 'user'); |
4531
|
0
|
|
|
|
|
0
|
$node031->addArrow($node034, 1, 0, 'group'); |
4532
|
0
|
|
|
|
|
0
|
$node032->addArrow($node047, 1, 0, 'everybody', \&collectEverybody); |
4533
|
0
|
|
|
|
|
0
|
$node033->addArrow($node047, 1, 0, 'USER', \&collectUser); |
4534
|
0
|
|
|
|
|
0
|
$node034->addArrow($node047, 1, 0, 'GROUP', \&collectGroup); |
4535
|
0
|
|
|
|
|
0
|
$node035->addArrow($node036, 1, 0, 'of'); |
4536
|
0
|
|
|
|
|
0
|
$node036->addArrow($node052, 1, 0, 'STORE', \&collectStore1); |
4537
|
0
|
|
|
|
|
0
|
$node037->addArrow($node038, 1, 0, 'of'); |
4538
|
0
|
|
|
|
|
0
|
$node038->addArrow($node053, 1, 0, 'STORE', \&collectStore1); |
4539
|
0
|
|
|
|
|
0
|
$node039->addArrow($node040, 1, 0, 'of'); |
4540
|
0
|
|
|
|
|
0
|
$node040->addArrow($node054, 1, 0, 'STORE', \&collectStore1); |
4541
|
0
|
|
|
|
|
0
|
$node041->addArrow($node042, 1, 0, 'of'); |
4542
|
0
|
|
|
|
|
0
|
$node041->addDefault($node055); |
4543
|
0
|
|
|
|
|
0
|
$node042->addArrow($node055, 1, 0, 'STORE', \&collectStore1); |
4544
|
0
|
|
|
|
|
0
|
$node043->addArrow($node044, 1, 0, 'to'); |
4545
|
0
|
|
|
|
|
0
|
$node044->addArrow($node061, 1, 0, 'STORE', \&collectStore1); |
4546
|
0
|
|
|
|
|
0
|
$node045->addArrow($node046, 1, 0, 'from'); |
4547
|
0
|
|
|
|
|
0
|
$node046->addArrow($node062, 1, 0, 'STORE', \&collectStore1); |
4548
|
0
|
|
|
|
|
0
|
$node047->addArrow($node048, 1, 0, 'and'); |
4549
|
0
|
|
|
|
|
0
|
$node047->addDefault($node064); |
4550
|
0
|
|
|
|
|
0
|
$node048->addArrow($node049, 1, 0, 'remember'); |
4551
|
0
|
|
|
|
|
0
|
$node049->addArrow($node050, 1, 0, 'it'); |
4552
|
0
|
|
|
|
|
0
|
$node050->addArrow($node051, 1, 0, 'as'); |
4553
|
0
|
|
|
|
|
0
|
$node051->addArrow($node064, 1, 0, 'TEXT', \&collectText); |
4554
|
0
|
|
|
|
|
0
|
$node055->addArrow($node056, 1, 0, 'to'); |
4555
|
0
|
|
|
|
|
0
|
$node055->addArrow($node057, 1, 0, 'to'); |
4556
|
0
|
|
|
|
|
0
|
$node055->addArrow($node058, 1, 0, 'to'); |
4557
|
0
|
|
|
|
|
0
|
$node056->addArrow($node059, 1, 0, 'user'); |
4558
|
0
|
|
|
|
|
0
|
$node057->addArrow($node060, 1, 0, 'group'); |
4559
|
0
|
|
|
|
|
0
|
$node058->addArrow($node063, 1, 0, 'everybody', \&collectEverybody); |
4560
|
0
|
|
|
|
|
0
|
$node059->addArrow($node063, 1, 0, 'USER', \&collectUser); |
4561
|
0
|
|
|
|
|
0
|
$node060->addArrow($node063, 1, 0, 'GROUP', \&collectGroup); |
4562
|
|
|
|
|
|
|
} |
4563
|
|
|
|
|
|
|
|
4564
|
|
|
|
|
|
|
sub collectAccount { |
4565
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4566
|
0
|
|
|
|
|
0
|
my $label = shift; |
4567
|
0
|
|
|
|
|
0
|
my $value = shift; |
4568
|
|
|
|
|
|
|
|
4569
|
0
|
|
|
|
|
0
|
$o->{accountToken} = $value; |
4570
|
|
|
|
|
|
|
} |
4571
|
|
|
|
|
|
|
|
4572
|
|
|
|
|
|
|
sub collectActor { |
4573
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4574
|
0
|
|
|
|
|
0
|
my $label = shift; |
4575
|
0
|
|
|
|
|
0
|
my $value = shift; |
4576
|
|
|
|
|
|
|
|
4577
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value; |
4578
|
|
|
|
|
|
|
} |
4579
|
|
|
|
|
|
|
|
4580
|
|
|
|
|
|
|
sub collectEverybody { |
4581
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4582
|
0
|
|
|
|
|
0
|
my $label = shift; |
4583
|
0
|
|
|
|
|
0
|
my $value = shift; |
4584
|
|
|
|
|
|
|
|
4585
|
0
|
|
|
|
|
0
|
$o->{permissions} = CDS::FolderStore::PosixPermissions::World->new; |
4586
|
|
|
|
|
|
|
} |
4587
|
|
|
|
|
|
|
|
4588
|
|
|
|
|
|
|
sub collectFoldername { |
4589
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4590
|
0
|
|
|
|
|
0
|
my $label = shift; |
4591
|
0
|
|
|
|
|
0
|
my $value = shift; |
4592
|
|
|
|
|
|
|
|
4593
|
0
|
|
|
|
|
0
|
$o->{foldername} = $value; |
4594
|
|
|
|
|
|
|
} |
4595
|
|
|
|
|
|
|
|
4596
|
|
|
|
|
|
|
sub collectGroup { |
4597
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4598
|
0
|
|
|
|
|
0
|
my $label = shift; |
4599
|
0
|
|
|
|
|
0
|
my $value = shift; |
4600
|
|
|
|
|
|
|
|
4601
|
0
|
|
|
|
|
0
|
$o->{permissions} = CDS::FolderStore::PosixPermissions::Group->new($o->{group}); |
4602
|
|
|
|
|
|
|
} |
4603
|
|
|
|
|
|
|
|
4604
|
|
|
|
|
|
|
sub collectHash { |
4605
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4606
|
0
|
|
|
|
|
0
|
my $label = shift; |
4607
|
0
|
|
|
|
|
0
|
my $value = shift; |
4608
|
|
|
|
|
|
|
|
4609
|
0
|
|
|
|
|
0
|
$o->{hash} = $value; |
4610
|
|
|
|
|
|
|
} |
4611
|
|
|
|
|
|
|
|
4612
|
|
|
|
|
|
|
sub collectKeypair { |
4613
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4614
|
0
|
|
|
|
|
0
|
my $label = shift; |
4615
|
0
|
|
|
|
|
0
|
my $value = shift; |
4616
|
|
|
|
|
|
|
|
4617
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
4618
|
|
|
|
|
|
|
} |
4619
|
|
|
|
|
|
|
|
4620
|
|
|
|
|
|
|
sub collectObject { |
4621
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4622
|
0
|
|
|
|
|
0
|
my $label = shift; |
4623
|
0
|
|
|
|
|
0
|
my $value = shift; |
4624
|
|
|
|
|
|
|
|
4625
|
0
|
|
|
|
|
0
|
$o->{accountToken} = CDS::AccountToken->new($value->cliStore, $value->hash); |
4626
|
|
|
|
|
|
|
} |
4627
|
|
|
|
|
|
|
|
4628
|
|
|
|
|
|
|
sub collectObjectfile { |
4629
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4630
|
0
|
|
|
|
|
0
|
my $label = shift; |
4631
|
0
|
|
|
|
|
0
|
my $value = shift; |
4632
|
|
|
|
|
|
|
|
4633
|
0
|
|
|
|
|
0
|
$o->{file} = $value; |
4634
|
|
|
|
|
|
|
} |
4635
|
|
|
|
|
|
|
|
4636
|
|
|
|
|
|
|
sub collectStore { |
4637
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4638
|
0
|
|
|
|
|
0
|
my $label = shift; |
4639
|
0
|
|
|
|
|
0
|
my $value = shift; |
4640
|
|
|
|
|
|
|
|
4641
|
0
|
|
|
|
|
0
|
$o->{accountToken} = CDS::AccountToken->new($value, $o->{actorHash}); |
4642
|
|
|
|
|
|
|
} |
4643
|
|
|
|
|
|
|
|
4644
|
|
|
|
|
|
|
sub collectStore1 { |
4645
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4646
|
0
|
|
|
|
|
0
|
my $label = shift; |
4647
|
0
|
|
|
|
|
0
|
my $value = shift; |
4648
|
|
|
|
|
|
|
|
4649
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
4650
|
|
|
|
|
|
|
} |
4651
|
|
|
|
|
|
|
|
4652
|
|
|
|
|
|
|
sub collectText { |
4653
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4654
|
0
|
|
|
|
|
0
|
my $label = shift; |
4655
|
0
|
|
|
|
|
0
|
my $value = shift; |
4656
|
|
|
|
|
|
|
|
4657
|
0
|
|
|
|
|
0
|
$o->{label} = $value; |
4658
|
|
|
|
|
|
|
} |
4659
|
|
|
|
|
|
|
|
4660
|
|
|
|
|
|
|
sub collectUser { |
4661
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4662
|
0
|
|
|
|
|
0
|
my $label = shift; |
4663
|
0
|
|
|
|
|
0
|
my $value = shift; |
4664
|
|
|
|
|
|
|
|
4665
|
0
|
|
|
|
|
0
|
$o->{permissions} = CDS::FolderStore::PosixPermissions::User->new($value); |
4666
|
|
|
|
|
|
|
} |
4667
|
|
|
|
|
|
|
|
4668
|
|
|
|
|
|
|
sub new { |
4669
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
4670
|
0
|
|
|
|
|
0
|
my $actor = shift; |
4671
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
4672
|
|
|
|
|
|
|
|
4673
|
|
|
|
|
|
|
# END AUTOGENERATED |
4674
|
|
|
|
|
|
|
|
4675
|
|
|
|
|
|
|
# HTML FOLDER NAME folder-store |
4676
|
|
|
|
|
|
|
# HTML TITLE Folder store management |
4677
|
|
|
|
|
|
|
sub help { |
4678
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4679
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4680
|
|
|
|
|
|
|
|
4681
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
4682
|
0
|
|
|
|
|
0
|
$ui->space; |
4683
|
0
|
|
|
|
|
0
|
$ui->command('cds create store FOLDERNAME'); |
4684
|
0
|
|
|
|
|
0
|
$ui->p('Creates a new store in FOLDERNAME, and adds it to the list of known stores. If the folder does not exist, it is created. If it does exist, it must be empty.'); |
4685
|
0
|
|
|
|
|
0
|
$ui->space; |
4686
|
0
|
|
|
|
|
0
|
$ui->p('By default, the filesystem permissions of the store are set such that only the current user can post objects and modify boxes. Other users on the system can post to the message box, list boxes, and read objects.'); |
4687
|
0
|
|
|
|
|
0
|
$ui->space; |
4688
|
0
|
|
|
|
|
0
|
$ui->command('… for user USER'); |
4689
|
0
|
|
|
|
|
0
|
$ui->p('Makes the store accessible to the user USER.'); |
4690
|
0
|
|
|
|
|
0
|
$ui->space; |
4691
|
0
|
|
|
|
|
0
|
$ui->command('… for group GROUP'); |
4692
|
0
|
|
|
|
|
0
|
$ui->p('Makes the store accessible to the group GROUP.'); |
4693
|
0
|
|
|
|
|
0
|
$ui->space; |
4694
|
0
|
|
|
|
|
0
|
$ui->command('… for everybody'); |
4695
|
0
|
|
|
|
|
0
|
$ui->p('Makes the store accessible to everybody.'); |
4696
|
0
|
|
|
|
|
0
|
$ui->space; |
4697
|
0
|
|
|
|
|
0
|
$ui->p('Note that the permissions only affect direct filesystem access. If your store is exposed by a server (e.g. a web server), it may be accessible to others.'); |
4698
|
0
|
|
|
|
|
0
|
$ui->space; |
4699
|
0
|
|
|
|
|
0
|
$ui->command('… and remember it as TEXT'); |
4700
|
0
|
|
|
|
|
0
|
$ui->p('Remembers the store under the label TEXT. See "cds help remember" for details.'); |
4701
|
0
|
|
|
|
|
0
|
$ui->space; |
4702
|
0
|
|
|
|
|
0
|
$ui->command('cds check permissions [of STORE]'); |
4703
|
0
|
|
|
|
|
0
|
$ui->p('Checks the permissions (owner, mode) of all accounts, boxes, box entries, and objects of the store, and reports any error. The permission scheme (user, group, or everybody) is derived from the "accounts" and "objects" folders.'); |
4704
|
0
|
|
|
|
|
0
|
$ui->p('If the store is omitted, the selected store is used.'); |
4705
|
0
|
|
|
|
|
0
|
$ui->space; |
4706
|
0
|
|
|
|
|
0
|
$ui->command('cds fix permissions [of STORE]'); |
4707
|
0
|
|
|
|
|
0
|
$ui->p('Same as above, but tries to fix the permissions (chown, chmod) instead of just reporting them.'); |
4708
|
0
|
|
|
|
|
0
|
$ui->space; |
4709
|
0
|
|
|
|
|
0
|
$ui->command('cds show permission scheme [of STORE]'); |
4710
|
0
|
|
|
|
|
0
|
$ui->p('Reports the permission scheme of the store.'); |
4711
|
0
|
|
|
|
|
0
|
$ui->space; |
4712
|
0
|
|
|
|
|
0
|
$ui->command('cds set permission scheme [of STORE] to (user USER|group GROUP|everybody)'); |
4713
|
0
|
|
|
|
|
0
|
$ui->p('Sets the permission scheme of the stores, and changes all permissions accordingly.'); |
4714
|
0
|
|
|
|
|
0
|
$ui->space; |
4715
|
0
|
|
|
|
|
0
|
$ui->command('cds add account ACCOUNT [to STORE]'); |
4716
|
0
|
|
|
|
|
0
|
$ui->command('cds add account for FILE [to STORE]'); |
4717
|
0
|
|
|
|
|
0
|
$ui->command('cds add account for KEYPAIR [to STORE]'); |
4718
|
0
|
|
|
|
|
0
|
$ui->command('cds add account for OBJECT [to STORE]'); |
4719
|
0
|
|
|
|
|
0
|
$ui->command('cds add account for ACTOR on STORE [to STORE]'); |
4720
|
0
|
|
|
|
|
0
|
$ui->p('Uploads the public key (FILE, KEYPAIR, OBJECT, ACCOUNT, or ACTOR on STORE) onto the store, and adds the corresponding account. This grants the user the right to access this account.'); |
4721
|
0
|
|
|
|
|
0
|
$ui->space; |
4722
|
0
|
|
|
|
|
0
|
$ui->command('cds remove account HASH [from STORE]'); |
4723
|
0
|
|
|
|
|
0
|
$ui->p('Removes the indicated account from the store. This immediately destroys the user\'s data.'); |
4724
|
0
|
|
|
|
|
0
|
$ui->space; |
4725
|
|
|
|
|
|
|
} |
4726
|
|
|
|
|
|
|
|
4727
|
|
|
|
|
|
|
sub createStore { |
4728
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4729
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4730
|
|
|
|
|
|
|
|
4731
|
0
|
|
|
|
|
0
|
$o->{permissions} = CDS::FolderStore::PosixPermissions::User->new; |
4732
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4733
|
|
|
|
|
|
|
|
4734
|
|
|
|
|
|
|
# Give up if the folder is non-empty (but we accept hidden files) |
4735
|
0
|
|
|
|
|
0
|
for my $file (CDS->listFolder($o->{foldername})) { |
4736
|
0
|
0
|
|
|
|
0
|
next if $file =~ /^\./; |
4737
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('The folder ', $o->{foldername}, ' is not empty. Giving up …'); |
4738
|
0
|
|
|
|
|
0
|
return; |
4739
|
|
|
|
|
|
|
} |
4740
|
|
|
|
|
|
|
|
4741
|
|
|
|
|
|
|
# Create the object store |
4742
|
0
|
|
0
|
|
|
0
|
$o->create($o->{foldername}.'/objects') // return; |
4743
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Object store created for ', $o->{permissions}->target, '.'); |
4744
|
|
|
|
|
|
|
|
4745
|
|
|
|
|
|
|
# Create the account store |
4746
|
0
|
|
0
|
|
|
0
|
$o->create($o->{foldername}.'/accounts') // return; |
4747
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Account store created for ', $o->{permissions}->target, '.'); |
4748
|
|
|
|
|
|
|
|
4749
|
|
|
|
|
|
|
# Return if the user does not want us to add the store |
4750
|
0
|
0
|
|
|
|
0
|
return if ! defined $o->{label}; |
4751
|
|
|
|
|
|
|
|
4752
|
|
|
|
|
|
|
# Remember the store |
4753
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->new; |
4754
|
0
|
|
|
|
|
0
|
$record->addText('store')->addText('file://'.$o->{foldername}); |
4755
|
0
|
|
|
|
|
0
|
$o->{actor}->remember($o->{label}, $record); |
4756
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
4757
|
|
|
|
|
|
|
} |
4758
|
|
|
|
|
|
|
|
4759
|
|
|
|
|
|
|
# Creates a folder with the selected permissions. |
4760
|
|
|
|
|
|
|
sub create { |
4761
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4762
|
0
|
|
|
|
|
0
|
my $folder = shift; |
4763
|
|
|
|
|
|
|
|
4764
|
|
|
|
|
|
|
# Create the folders to here if necessary |
4765
|
0
|
|
|
|
|
0
|
for my $intermediateFolder (CDS->intermediateFolders($folder)) { |
4766
|
0
|
|
|
|
|
0
|
mkdir $intermediateFolder, 0755; |
4767
|
|
|
|
|
|
|
} |
4768
|
|
|
|
|
|
|
|
4769
|
|
|
|
|
|
|
# mkdir (if it does not exist yet) and chmod (if it does exist already) |
4770
|
0
|
|
|
|
|
0
|
mkdir $folder, $o->{permissions}->baseFolderMode; |
4771
|
0
|
|
|
|
|
0
|
chmod $o->{permissions}->baseFolderMode, $folder; |
4772
|
0
|
|
0
|
|
|
0
|
chown $o->{permissions}->uid // -1, $o->{permissions}->gid // -1, $folder; |
|
|
|
0
|
|
|
|
|
4773
|
|
|
|
|
|
|
|
4774
|
|
|
|
|
|
|
# Check if the result is correct |
4775
|
0
|
|
|
|
|
0
|
my @s = stat $folder; |
4776
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('Unable to create ', $o->{foldername}, '.') if ! scalar @s; |
4777
|
0
|
|
|
|
|
0
|
my $mode = $s[2]; |
4778
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error($folder, ' exists, but is not a folder') if ! Fcntl::S_ISDIR($mode); |
4779
|
0
|
0
|
0
|
|
|
0
|
return $o->{ui}->error('Unable to set the owning user ', $o->{permissions}->user, ' for ', $folder, '.') if defined $o->{permissions}->uid && $s[4] != $o->{permissions}->uid; |
4780
|
0
|
0
|
0
|
|
|
0
|
return $o->{ui}->error('Unable to set the owning group ', $o->{permissions}->group, ' for ', $folder, '.') if defined $o->{permissions}->gid && $s[5] != $o->{permissions}->gid; |
4781
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('Unable to set the mode on ', $folder, '.') if ($mode & 0777) != $o->{permissions}->baseFolderMode; |
4782
|
0
|
|
|
|
|
0
|
return 1; |
4783
|
|
|
|
|
|
|
} |
4784
|
|
|
|
|
|
|
|
4785
|
|
|
|
|
|
|
sub existingFolderStoreOrShowError { |
4786
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4787
|
|
|
|
|
|
|
|
4788
|
0
|
|
0
|
|
|
0
|
my $store = $o->{store} // $o->{actor}->preferredStore; |
4789
|
|
|
|
|
|
|
|
4790
|
0
|
|
|
|
|
0
|
my $folderStore = CDS::FolderStore->forUrl($store->url); |
4791
|
0
|
0
|
|
|
|
0
|
if (! $folderStore) { |
4792
|
0
|
|
|
|
|
0
|
$o->{ui}->error('"', $store->url, '" is not a folder store.'); |
4793
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4794
|
0
|
|
|
|
|
0
|
$o->{ui}->p('Account management and file system permission checks only apply to stores on the local file system. Such stores are referred to by file://… URLs, or file system paths.'); |
4795
|
0
|
|
|
|
|
0
|
$o->{ui}->p('To fix the permissions on a remote store, log onto that server and fix the permissions there. Note that permissions are not part of the Condensation protocol, but a property of some underlying storage systems, such as file systems.'); |
4796
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4797
|
0
|
|
|
|
|
0
|
return; |
4798
|
|
|
|
|
|
|
} |
4799
|
|
|
|
|
|
|
|
4800
|
0
|
0
|
|
|
|
0
|
if (! $folderStore->exists) { |
4801
|
0
|
|
|
|
|
0
|
$o->{ui}->error('"', $folderStore->folder, '" does not exist.'); |
4802
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4803
|
0
|
|
|
|
|
0
|
$o->{ui}->p('The folder either does not exist, or is not a folder store. You can create this store using:'); |
4804
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold(' cds create store ', $folderStore->folder)); |
4805
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4806
|
0
|
|
|
|
|
0
|
return; |
4807
|
|
|
|
|
|
|
} |
4808
|
|
|
|
|
|
|
|
4809
|
0
|
|
|
|
|
0
|
return $folderStore; |
4810
|
|
|
|
|
|
|
} |
4811
|
|
|
|
|
|
|
|
4812
|
|
|
|
|
|
|
sub showPermissions { |
4813
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4814
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4815
|
|
|
|
|
|
|
|
4816
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4817
|
0
|
|
0
|
|
|
0
|
my $folderStore = $o->existingFolderStoreOrShowError // return; |
4818
|
0
|
|
|
|
|
0
|
$o->showStore($folderStore); |
4819
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4820
|
|
|
|
|
|
|
} |
4821
|
|
|
|
|
|
|
|
4822
|
|
|
|
|
|
|
sub showStore { |
4823
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4824
|
0
|
|
|
|
|
0
|
my $folderStore = shift; |
4825
|
|
|
|
|
|
|
|
4826
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4827
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Store'); |
4828
|
0
|
|
|
|
|
0
|
$o->{ui}->line($folderStore->folder); |
4829
|
0
|
|
|
|
|
0
|
$o->{ui}->line('Accessible to ', $folderStore->permissions->target, '.'); |
4830
|
|
|
|
|
|
|
} |
4831
|
|
|
|
|
|
|
|
4832
|
|
|
|
|
|
|
sub setPermissions { |
4833
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4834
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4835
|
|
|
|
|
|
|
|
4836
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4837
|
|
|
|
|
|
|
|
4838
|
0
|
|
0
|
|
|
0
|
my $folderStore = $o->existingFolderStoreOrShowError // return; |
4839
|
0
|
|
|
|
|
0
|
$o->showStore($folderStore); |
4840
|
|
|
|
|
|
|
|
4841
|
0
|
|
|
|
|
0
|
$folderStore->setPermissions($o->{permissions}); |
4842
|
0
|
|
|
|
|
0
|
$o->{ui}->line('Changing permissions …'); |
4843
|
0
|
|
|
|
|
0
|
my $logger = CDS::Commands::FolderStore::SetLogger->new($o, $folderStore->folder); |
4844
|
0
|
0
|
|
|
|
0
|
$folderStore->checkPermissions($logger) || $o->traversalFailed($folderStore); |
4845
|
0
|
|
|
|
|
0
|
$logger->summary; |
4846
|
|
|
|
|
|
|
|
4847
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4848
|
|
|
|
|
|
|
} |
4849
|
|
|
|
|
|
|
|
4850
|
|
|
|
|
|
|
sub checkPermissions { |
4851
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4852
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4853
|
|
|
|
|
|
|
|
4854
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4855
|
|
|
|
|
|
|
|
4856
|
0
|
|
0
|
|
|
0
|
my $folderStore = $o->existingFolderStoreOrShowError // return; |
4857
|
0
|
|
|
|
|
0
|
$o->showStore($folderStore); |
4858
|
|
|
|
|
|
|
|
4859
|
0
|
|
|
|
|
0
|
$o->{ui}->line('Checking permissions …'); |
4860
|
0
|
|
|
|
|
0
|
my $logger = CDS::Commands::FolderStore::CheckLogger->new($o, $folderStore->folder); |
4861
|
0
|
0
|
|
|
|
0
|
$folderStore->checkPermissions($logger) || $o->traversalFailed($folderStore); |
4862
|
0
|
|
|
|
|
0
|
$logger->summary; |
4863
|
|
|
|
|
|
|
|
4864
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4865
|
|
|
|
|
|
|
} |
4866
|
|
|
|
|
|
|
|
4867
|
|
|
|
|
|
|
sub fixPermissions { |
4868
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4869
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4870
|
|
|
|
|
|
|
|
4871
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4872
|
|
|
|
|
|
|
|
4873
|
0
|
|
0
|
|
|
0
|
my $folderStore = $o->existingFolderStoreOrShowError // return; |
4874
|
0
|
|
|
|
|
0
|
$o->showStore($folderStore); |
4875
|
|
|
|
|
|
|
|
4876
|
0
|
|
|
|
|
0
|
$o->{ui}->line('Fixing permissions …'); |
4877
|
0
|
|
|
|
|
0
|
my $logger = CDS::Commands::FolderStore::FixLogger->new($o, $folderStore->folder); |
4878
|
0
|
0
|
|
|
|
0
|
$folderStore->checkPermissions($logger) || $o->traversalFailed($folderStore); |
4879
|
0
|
|
|
|
|
0
|
$logger->summary; |
4880
|
|
|
|
|
|
|
|
4881
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4882
|
|
|
|
|
|
|
} |
4883
|
|
|
|
|
|
|
|
4884
|
|
|
|
|
|
|
sub traversalFailed { |
4885
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4886
|
0
|
|
|
|
|
0
|
my $folderStore = shift; |
4887
|
|
|
|
|
|
|
|
4888
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4889
|
0
|
|
|
|
|
0
|
$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.'); |
4890
|
0
|
|
|
|
|
0
|
$o->{ui}->p('If you have root privileges, you can take over this store using:'); |
4891
|
0
|
|
|
|
|
0
|
my $userName = getpwuid($<); |
4892
|
0
|
|
|
|
|
0
|
my $groupName = getgrgid($(); |
4893
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold(' sudo chown -R ', $userName, ':', $groupName, ' ', $folderStore->folder)); |
4894
|
0
|
|
|
|
|
0
|
$o->{ui}->p('and then set the desired permission scheme:'); |
4895
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold(' cds set permissions of ', $folderStore->folder, ' to …')); |
4896
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
4897
|
0
|
|
|
|
|
0
|
exit(1); |
4898
|
|
|
|
|
|
|
} |
4899
|
|
|
|
|
|
|
|
4900
|
|
|
|
|
|
|
sub addAccount { |
4901
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4902
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4903
|
|
|
|
|
|
|
|
4904
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4905
|
|
|
|
|
|
|
|
4906
|
|
|
|
|
|
|
# Prepare |
4907
|
0
|
|
0
|
|
|
0
|
my $folderStore = $o->existingFolderStoreOrShowError // return; |
4908
|
0
|
|
0
|
|
|
0
|
my $publicKey = $o->publicKey // return; |
4909
|
|
|
|
|
|
|
|
4910
|
|
|
|
|
|
|
# Upload the public key onto the store |
4911
|
0
|
|
|
|
|
0
|
my $error = $folderStore->put($publicKey->hash, $publicKey->object); |
4912
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('Unable to upload the public key: ', $error) if $error; |
4913
|
|
|
|
|
|
|
|
4914
|
|
|
|
|
|
|
# Create the account folder |
4915
|
0
|
|
|
|
|
0
|
my $folder = $folderStore->folder.'/accounts/'.$publicKey->hash->hex; |
4916
|
0
|
|
|
|
|
0
|
my $permissions = $folderStore->permissions; |
4917
|
0
|
|
|
|
|
0
|
$permissions->mkdir($folder, $permissions->accountFolderMode); |
4918
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->error('Unable to create folder "', $folder, '".') if ! -d $folder; |
4919
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Account ', $publicKey->hash->hex, ' added.'); |
4920
|
0
|
|
|
|
|
0
|
return 1; |
4921
|
|
|
|
|
|
|
} |
4922
|
|
|
|
|
|
|
|
4923
|
|
|
|
|
|
|
sub publicKey { |
4924
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4925
|
|
|
|
|
|
|
|
4926
|
0
|
0
|
|
|
|
0
|
return $o->{keyPairToken}->keyPair->publicKey if $o->{keyPairToken}; |
4927
|
|
|
|
|
|
|
|
4928
|
0
|
0
|
|
|
|
0
|
if ($o->{file}) { |
4929
|
0
|
|
0
|
|
|
0
|
my $bytes = CDS->readBytesFromFile($o->{file}) // return $o->{ui}->error('Cannot read "', $o->{file}, '".'); |
4930
|
0
|
|
0
|
|
|
0
|
my $object = CDS::Object->fromBytes($bytes) // return $o->{ui}->error('"', $o->{file}, '" is not a public key.'); |
4931
|
0
|
|
0
|
|
|
0
|
return CDS::PublicKey->fromObject($object) // return $o->{ui}->error('"', $o->{file}, '" is not a public key.'); |
4932
|
|
|
|
|
|
|
} |
4933
|
|
|
|
|
|
|
|
4934
|
0
|
|
|
|
|
0
|
return $o->{actor}->uiGetPublicKey($o->{accountToken}->actorHash, $o->{accountToken}->cliStore, $o->{actor}->preferredKeyPairToken); |
4935
|
|
|
|
|
|
|
} |
4936
|
|
|
|
|
|
|
|
4937
|
|
|
|
|
|
|
sub removeAccount { |
4938
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4939
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
4940
|
|
|
|
|
|
|
|
4941
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
4942
|
|
|
|
|
|
|
|
4943
|
|
|
|
|
|
|
# Prepare the folder |
4944
|
0
|
|
0
|
|
|
0
|
my $folderStore = $o->existingFolderStoreOrShowError // return; |
4945
|
0
|
|
|
|
|
0
|
my $folder = $folderStore->folder.'/accounts/'.$o->{hash}->hex; |
4946
|
0
|
|
|
|
|
0
|
my $deletedFolder = $folderStore->folder.'/accounts/deleted-'.$o->{hash}->hex; |
4947
|
|
|
|
|
|
|
|
4948
|
|
|
|
|
|
|
# Rename, so that it is not visible any more |
4949
|
0
|
0
|
|
|
|
0
|
$o->recursivelyDelete($deletedFolder) if -e $deletedFolder; |
4950
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->line('The account ', $o->{hash}->hex, ' does not exist.') if ! -e $folder; |
4951
|
0
|
0
|
|
|
|
0
|
rename($folder, $deletedFolder) || return $o->{ui}->error('Unable to rename the folder "', $folder, '".'); |
4952
|
|
|
|
|
|
|
|
4953
|
|
|
|
|
|
|
# Try to delete it entirely |
4954
|
0
|
|
|
|
|
0
|
$o->recursivelyDelete($deletedFolder); |
4955
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Account ', $o->{hash}->hex, ' removed.'); |
4956
|
0
|
|
|
|
|
0
|
return 1; |
4957
|
|
|
|
|
|
|
} |
4958
|
|
|
|
|
|
|
|
4959
|
|
|
|
|
|
|
sub recursivelyDelete { |
4960
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4961
|
0
|
|
|
|
|
0
|
my $folder = shift; |
4962
|
|
|
|
|
|
|
|
4963
|
0
|
|
|
|
|
0
|
for my $filename (CDS->listFolder($folder)) { |
4964
|
0
|
0
|
|
|
|
0
|
next if $filename =~ /^\./; |
4965
|
0
|
|
|
|
|
0
|
my $file = $folder.'/'.$filename; |
4966
|
0
|
0
|
|
|
|
0
|
if (-f $file) { |
|
|
0
|
|
|
|
|
|
4967
|
0
|
|
0
|
|
|
0
|
unlink $file || $o->{ui}->pOrange('Unable to remove the file "', $file, '".'); |
4968
|
|
|
|
|
|
|
} elsif (-d $file) { |
4969
|
0
|
|
|
|
|
0
|
$o->recursivelyDelete($file); |
4970
|
|
|
|
|
|
|
} |
4971
|
|
|
|
|
|
|
} |
4972
|
|
|
|
|
|
|
|
4973
|
0
|
0
|
|
|
|
0
|
rmdir($folder) || $o->{ui}->pOrange('Unable to remove the folder "', $folder, '".'); |
4974
|
|
|
|
|
|
|
} |
4975
|
|
|
|
|
|
|
|
4976
|
|
|
|
|
|
|
package CDS::Commands::FolderStore::CheckLogger; |
4977
|
|
|
|
|
|
|
|
4978
|
1
|
|
|
1
|
|
25224
|
use parent -norequire, 'CDS::Commands::FolderStore::Logger'; |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
5
|
|
4979
|
|
|
|
|
|
|
|
4980
|
|
|
|
|
|
|
sub finalizeWrong { |
4981
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4982
|
|
|
|
|
|
|
|
4983
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed(@_); |
4984
|
0
|
|
|
|
|
0
|
return 0; |
4985
|
|
|
|
|
|
|
} |
4986
|
|
|
|
|
|
|
|
4987
|
|
|
|
|
|
|
sub summary { |
4988
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
4989
|
|
|
|
|
|
|
|
4990
|
0
|
|
|
|
|
0
|
$o->{ui}->p(($o->{correct} + $o->{wrong}).' files and folders traversed.'); |
4991
|
0
|
0
|
|
|
|
0
|
if ($o->{wrong} > 0) { |
4992
|
0
|
|
|
|
|
0
|
$o->{ui}->p($o->{wrong}, ' files and folders have wrong permissions. To fix them, run'); |
4993
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold(' cds fix permissions of ', $o->{store}->url)); |
4994
|
|
|
|
|
|
|
} else { |
4995
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('All permissions are OK.'); |
4996
|
|
|
|
|
|
|
} |
4997
|
|
|
|
|
|
|
} |
4998
|
|
|
|
|
|
|
|
4999
|
|
|
|
|
|
|
package CDS::Commands::FolderStore::FixLogger; |
5000
|
|
|
|
|
|
|
|
5001
|
1
|
|
|
1
|
|
181
|
use parent -norequire, 'CDS::Commands::FolderStore::Logger'; |
|
1
|
|
|
|
|
13
|
|
|
1
|
|
|
|
|
5
|
|
5002
|
|
|
|
|
|
|
|
5003
|
|
|
|
|
|
|
sub finalizeWrong { |
5004
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5005
|
|
|
|
|
|
|
|
5006
|
0
|
|
|
|
|
0
|
$o->{ui}->line(@_); |
5007
|
0
|
|
|
|
|
0
|
return 1; |
5008
|
|
|
|
|
|
|
} |
5009
|
|
|
|
|
|
|
|
5010
|
|
|
|
|
|
|
sub summary { |
5011
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5012
|
|
|
|
|
|
|
|
5013
|
0
|
|
|
|
|
0
|
$o->{ui}->p(($o->{correct} + $o->{wrong}).' files and folders traversed.'); |
5014
|
0
|
0
|
|
|
|
0
|
$o->{ui}->p('The permissions of ', $o->{wrong}, ' files and folders have been fixed.') if $o->{wrong} > 0; |
5015
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('All permissions are OK.'); |
5016
|
|
|
|
|
|
|
} |
5017
|
|
|
|
|
|
|
|
5018
|
|
|
|
|
|
|
package CDS::Commands::FolderStore::Logger; |
5019
|
|
|
|
|
|
|
|
5020
|
|
|
|
|
|
|
sub new { |
5021
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
5022
|
0
|
|
|
|
|
0
|
my $parent = shift; |
5023
|
0
|
|
|
|
|
0
|
my $baseFolder = shift; |
5024
|
|
|
|
|
|
|
|
5025
|
|
|
|
|
|
|
return bless { |
5026
|
|
|
|
|
|
|
ui => $parent->{ui}, |
5027
|
|
|
|
|
|
|
store => $parent->{store}, |
5028
|
0
|
|
|
|
|
0
|
baseFolder => $baseFolder, |
5029
|
|
|
|
|
|
|
correct => 0, |
5030
|
|
|
|
|
|
|
wrong => 0, |
5031
|
|
|
|
|
|
|
}, $class; |
5032
|
|
|
|
|
|
|
} |
5033
|
|
|
|
|
|
|
|
5034
|
|
|
|
|
|
|
sub correct { |
5035
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5036
|
|
|
|
|
|
|
|
5037
|
0
|
|
|
|
|
0
|
$o->{correct} += 1; |
5038
|
|
|
|
|
|
|
} |
5039
|
|
|
|
|
|
|
|
5040
|
|
|
|
|
|
|
sub wrong { |
5041
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5042
|
0
|
|
|
|
|
0
|
my $item = shift; |
5043
|
0
|
|
|
|
|
0
|
my $uid = shift; |
5044
|
0
|
|
|
|
|
0
|
my $gid = shift; |
5045
|
0
|
|
|
|
|
0
|
my $mode = shift; |
5046
|
0
|
|
|
|
|
0
|
my $expectedUid = shift; |
5047
|
0
|
|
|
|
|
0
|
my $expectedGid = shift; |
5048
|
0
|
|
|
|
|
0
|
my $expectedMode = shift; |
5049
|
|
|
|
|
|
|
|
5050
|
0
|
|
|
|
|
0
|
my $len = length $o->{baseFolder}; |
5051
|
0
|
|
|
|
|
0
|
$o->{wrong} += 1; |
5052
|
0
|
0
|
0
|
|
|
0
|
$item = '…'.substr($item, $len) if length $item > $len && substr($item, 0, $len) eq $o->{baseFolder}; |
5053
|
0
|
|
|
|
|
0
|
my @changes; |
5054
|
0
|
0
|
0
|
|
|
0
|
push @changes, 'user '.&username($uid).' -> '.&username($expectedUid) if defined $expectedUid && $uid != $expectedUid; |
5055
|
0
|
0
|
0
|
|
|
0
|
push @changes, 'group '.&groupname($gid).' -> '.&groupname($expectedGid) if defined $expectedGid && $gid != $expectedGid; |
5056
|
0
|
0
|
|
|
|
0
|
push @changes, 'mode '.sprintf('%04o -> %04o', $mode, $expectedMode) if $mode != $expectedMode; |
5057
|
0
|
|
|
|
|
0
|
return $o->finalizeWrong(join(', ', @changes), "\t", $item); |
5058
|
|
|
|
|
|
|
} |
5059
|
|
|
|
|
|
|
|
5060
|
|
|
|
|
|
|
sub username { |
5061
|
0
|
|
|
0
|
|
0
|
my $uid = shift; |
5062
|
|
|
|
|
|
|
|
5063
|
0
|
|
0
|
|
|
0
|
return getpwuid($uid) // $uid; |
5064
|
|
|
|
|
|
|
} |
5065
|
|
|
|
|
|
|
|
5066
|
|
|
|
|
|
|
sub groupname { |
5067
|
0
|
|
|
0
|
|
0
|
my $gid = shift; |
5068
|
|
|
|
|
|
|
|
5069
|
0
|
|
0
|
|
|
0
|
return getgrgid($gid) // $gid; |
5070
|
|
|
|
|
|
|
} |
5071
|
|
|
|
|
|
|
|
5072
|
|
|
|
|
|
|
sub accessError { |
5073
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5074
|
0
|
|
|
|
|
0
|
my $item = shift; |
5075
|
|
|
|
|
|
|
|
5076
|
0
|
|
|
|
|
0
|
$o->{ui}->error('Error accessing ', $item, '.'); |
5077
|
0
|
|
|
|
|
0
|
return 0; |
5078
|
|
|
|
|
|
|
} |
5079
|
|
|
|
|
|
|
|
5080
|
|
|
|
|
|
|
sub setError { |
5081
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5082
|
0
|
|
|
|
|
0
|
my $item = shift; |
5083
|
|
|
|
|
|
|
|
5084
|
0
|
|
|
|
|
0
|
$o->{ui}->error('Error setting permissions of ', $item, '.'); |
5085
|
0
|
|
|
|
|
0
|
return 0; |
5086
|
|
|
|
|
|
|
} |
5087
|
|
|
|
|
|
|
|
5088
|
|
|
|
|
|
|
package CDS::Commands::FolderStore::SetLogger; |
5089
|
|
|
|
|
|
|
|
5090
|
1
|
|
|
1
|
|
588
|
use parent -norequire, 'CDS::Commands::FolderStore::Logger'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
5091
|
|
|
|
|
|
|
|
5092
|
|
|
|
|
|
|
sub finalizeWrong { |
5093
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5094
|
|
|
|
|
|
|
|
5095
|
0
|
|
|
|
|
0
|
return 1; |
5096
|
|
|
|
|
|
|
} |
5097
|
|
|
|
|
|
|
|
5098
|
|
|
|
|
|
|
sub summary { |
5099
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5100
|
|
|
|
|
|
|
|
5101
|
0
|
|
|
|
|
0
|
$o->{ui}->p(($o->{correct} + $o->{wrong}).' files and folders traversed.'); |
5102
|
0
|
0
|
|
|
|
0
|
$o->{ui}->p('The permissions of ', $o->{wrong}, ' files and folders have been adjusted.') if $o->{wrong} > 0; |
5103
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('All permissions are OK.'); |
5104
|
|
|
|
|
|
|
} |
5105
|
|
|
|
|
|
|
|
5106
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
5107
|
|
|
|
|
|
|
package CDS::Commands::Get; |
5108
|
|
|
|
|
|
|
|
5109
|
|
|
|
|
|
|
sub register { |
5110
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
5111
|
0
|
|
|
|
|
0
|
my $cds = shift; |
5112
|
0
|
|
|
|
|
0
|
my $help = shift; |
5113
|
|
|
|
|
|
|
|
5114
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
5115
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
5116
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
5117
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
5118
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
5119
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
5120
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
5121
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
5122
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
5123
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
5124
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(1); |
5125
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
5126
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
5127
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
5128
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(0); |
5129
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(0); |
5130
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(1); |
5131
|
0
|
|
|
|
|
0
|
my $node017 = CDS::Parser::Node->new(0); |
5132
|
0
|
|
|
|
|
0
|
my $node018 = CDS::Parser::Node->new(0); |
5133
|
0
|
|
|
|
|
0
|
my $node019 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&get}); |
5134
|
0
|
|
|
|
|
0
|
my $node020 = CDS::Parser::Node->new(1); |
5135
|
0
|
|
|
|
|
0
|
my $node021 = CDS::Parser::Node->new(0); |
5136
|
0
|
|
|
|
|
0
|
my $node022 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&get}); |
5137
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 1, 0, 'get'); |
5138
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'save'); |
5139
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'get'); |
5140
|
0
|
|
|
|
|
0
|
$cds->addArrow($node003, 1, 0, 'get'); |
5141
|
0
|
|
|
|
|
0
|
$cds->addArrow($node009, 1, 0, 'save', \&collectSave); |
5142
|
0
|
|
|
|
|
0
|
$help->addArrow($node005, 1, 0, 'get'); |
5143
|
0
|
|
|
|
|
0
|
$help->addArrow($node005, 1, 0, 'save'); |
5144
|
0
|
|
|
|
|
0
|
$node000->addArrow($node010, 1, 0, 'HASH', \&collectHash); |
5145
|
0
|
|
|
|
|
0
|
$node001->addArrow($node004, 1, 0, 'data'); |
5146
|
0
|
|
|
|
|
0
|
$node002->addArrow($node006, 1, 0, 'HASH', \&collectHash1); |
5147
|
0
|
|
|
|
|
0
|
$node003->addArrow($node010, 1, 0, 'OBJECT', \&collectObject); |
5148
|
0
|
|
|
|
|
0
|
$node004->addArrow($node009, 1, 0, 'of', \&collectOf); |
5149
|
0
|
|
|
|
|
0
|
$node006->addArrow($node007, 1, 0, 'on'); |
5150
|
0
|
|
|
|
|
0
|
$node006->addArrow($node008, 0, 0, 'from'); |
5151
|
0
|
|
|
|
|
0
|
$node007->addArrow($node010, 1, 0, 'STORE', \&collectStore); |
5152
|
0
|
|
|
|
|
0
|
$node008->addArrow($node010, 0, 0, 'STORE', \&collectStore); |
5153
|
0
|
|
|
|
|
0
|
$node009->addArrow($node013, 1, 0, 'HASH', \&collectHash1); |
5154
|
0
|
|
|
|
|
0
|
$node009->addArrow($node016, 1, 0, 'HASH', \&collectHash); |
5155
|
0
|
|
|
|
|
0
|
$node009->addArrow($node016, 1, 0, 'OBJECT', \&collectObject1); |
5156
|
0
|
|
|
|
|
0
|
$node010->addArrow($node011, 1, 0, 'decrypted'); |
5157
|
0
|
|
|
|
|
0
|
$node010->addDefault($node019); |
5158
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'with'); |
5159
|
0
|
|
|
|
|
0
|
$node012->addArrow($node019, 1, 0, 'AESKEY', \&collectAeskey); |
5160
|
0
|
|
|
|
|
0
|
$node013->addArrow($node014, 1, 0, 'on'); |
5161
|
0
|
|
|
|
|
0
|
$node013->addArrow($node015, 0, 0, 'from'); |
5162
|
0
|
|
|
|
|
0
|
$node014->addArrow($node016, 1, 0, 'STORE', \&collectStore); |
5163
|
0
|
|
|
|
|
0
|
$node015->addArrow($node016, 0, 0, 'STORE', \&collectStore); |
5164
|
0
|
|
|
|
|
0
|
$node016->addArrow($node017, 1, 0, 'decrypted'); |
5165
|
0
|
|
|
|
|
0
|
$node016->addDefault($node020); |
5166
|
0
|
|
|
|
|
0
|
$node017->addArrow($node018, 1, 0, 'with'); |
5167
|
0
|
|
|
|
|
0
|
$node018->addArrow($node020, 1, 0, 'AESKEY', \&collectAeskey); |
5168
|
0
|
|
|
|
|
0
|
$node020->addArrow($node021, 1, 0, 'as'); |
5169
|
0
|
|
|
|
|
0
|
$node021->addArrow($node022, 1, 0, 'FILENAME', \&collectFilename); |
5170
|
|
|
|
|
|
|
} |
5171
|
|
|
|
|
|
|
|
5172
|
|
|
|
|
|
|
sub collectAeskey { |
5173
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5174
|
0
|
|
|
|
|
0
|
my $label = shift; |
5175
|
0
|
|
|
|
|
0
|
my $value = shift; |
5176
|
|
|
|
|
|
|
|
5177
|
0
|
|
|
|
|
0
|
$o->{aesKey} = $value; |
5178
|
|
|
|
|
|
|
} |
5179
|
|
|
|
|
|
|
|
5180
|
|
|
|
|
|
|
sub collectFilename { |
5181
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5182
|
0
|
|
|
|
|
0
|
my $label = shift; |
5183
|
0
|
|
|
|
|
0
|
my $value = shift; |
5184
|
|
|
|
|
|
|
|
5185
|
0
|
|
|
|
|
0
|
$o->{filename} = $value; |
5186
|
|
|
|
|
|
|
} |
5187
|
|
|
|
|
|
|
|
5188
|
|
|
|
|
|
|
sub collectHash { |
5189
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5190
|
0
|
|
|
|
|
0
|
my $label = shift; |
5191
|
0
|
|
|
|
|
0
|
my $value = shift; |
5192
|
|
|
|
|
|
|
|
5193
|
0
|
|
|
|
|
0
|
$o->{hash} = $value; |
5194
|
0
|
|
|
|
|
0
|
$o->{store} = $o->{actor}->preferredStore; |
5195
|
|
|
|
|
|
|
} |
5196
|
|
|
|
|
|
|
|
5197
|
|
|
|
|
|
|
sub collectHash1 { |
5198
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5199
|
0
|
|
|
|
|
0
|
my $label = shift; |
5200
|
0
|
|
|
|
|
0
|
my $value = shift; |
5201
|
|
|
|
|
|
|
|
5202
|
0
|
|
|
|
|
0
|
$o->{hash} = $value; |
5203
|
|
|
|
|
|
|
} |
5204
|
|
|
|
|
|
|
|
5205
|
|
|
|
|
|
|
sub collectObject { |
5206
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5207
|
0
|
|
|
|
|
0
|
my $label = shift; |
5208
|
0
|
|
|
|
|
0
|
my $value = shift; |
5209
|
|
|
|
|
|
|
|
5210
|
0
|
|
|
|
|
0
|
$o->{hash} = $value->hash; |
5211
|
0
|
|
|
|
|
0
|
$o->{store} = $value->cliStore; |
5212
|
|
|
|
|
|
|
} |
5213
|
|
|
|
|
|
|
|
5214
|
|
|
|
|
|
|
sub collectObject1 { |
5215
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5216
|
0
|
|
|
|
|
0
|
my $label = shift; |
5217
|
0
|
|
|
|
|
0
|
my $value = shift; |
5218
|
|
|
|
|
|
|
|
5219
|
0
|
|
|
|
|
0
|
$o->{hash} = $value->hash; |
5220
|
0
|
|
|
|
|
0
|
push @{$o->{stores}}, $value->store; |
|
0
|
|
|
|
|
0
|
|
5221
|
|
|
|
|
|
|
} |
5222
|
|
|
|
|
|
|
|
5223
|
|
|
|
|
|
|
sub collectOf { |
5224
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5225
|
0
|
|
|
|
|
0
|
my $label = shift; |
5226
|
0
|
|
|
|
|
0
|
my $value = shift; |
5227
|
|
|
|
|
|
|
|
5228
|
0
|
|
|
|
|
0
|
$o->{saveData} = 1; |
5229
|
|
|
|
|
|
|
} |
5230
|
|
|
|
|
|
|
|
5231
|
|
|
|
|
|
|
sub collectSave { |
5232
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5233
|
0
|
|
|
|
|
0
|
my $label = shift; |
5234
|
0
|
|
|
|
|
0
|
my $value = shift; |
5235
|
|
|
|
|
|
|
|
5236
|
0
|
|
|
|
|
0
|
$o->{saveObject} = 1; |
5237
|
|
|
|
|
|
|
} |
5238
|
|
|
|
|
|
|
|
5239
|
|
|
|
|
|
|
sub collectStore { |
5240
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5241
|
0
|
|
|
|
|
0
|
my $label = shift; |
5242
|
0
|
|
|
|
|
0
|
my $value = shift; |
5243
|
|
|
|
|
|
|
|
5244
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
5245
|
|
|
|
|
|
|
} |
5246
|
|
|
|
|
|
|
|
5247
|
|
|
|
|
|
|
sub new { |
5248
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
5249
|
0
|
|
|
|
|
0
|
my $actor = shift; |
5250
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
5251
|
|
|
|
|
|
|
|
5252
|
|
|
|
|
|
|
# END AUTOGENERATED |
5253
|
|
|
|
|
|
|
|
5254
|
|
|
|
|
|
|
# HTML FOLDER NAME store-get |
5255
|
|
|
|
|
|
|
# HTML TITLE Get |
5256
|
|
|
|
|
|
|
sub help { |
5257
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5258
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
5259
|
|
|
|
|
|
|
|
5260
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
5261
|
0
|
|
|
|
|
0
|
$ui->space; |
5262
|
0
|
|
|
|
|
0
|
$ui->command('cds get OBJECT'); |
5263
|
0
|
|
|
|
|
0
|
$ui->command('cds get HASH on STORE'); |
5264
|
0
|
|
|
|
|
0
|
$ui->p('Downloads an object and writes it to STDOUT. If the object is not found, the program quits with exit code 1.'); |
5265
|
0
|
|
|
|
|
0
|
$ui->space; |
5266
|
0
|
|
|
|
|
0
|
$ui->command('cds get HASH'); |
5267
|
0
|
|
|
|
|
0
|
$ui->p('As above, but uses the selected store.'); |
5268
|
0
|
|
|
|
|
0
|
$ui->space; |
5269
|
0
|
|
|
|
|
0
|
$ui->command('… decrypted with AESKEY'); |
5270
|
0
|
|
|
|
|
0
|
$ui->p('Decrypts the object after retrieval.'); |
5271
|
0
|
|
|
|
|
0
|
$ui->space; |
5272
|
0
|
|
|
|
|
0
|
$ui->command('cds save … as FILENAME'); |
5273
|
0
|
|
|
|
|
0
|
$ui->p('Saves the object to FILENAME instead of writing it to STDOUT.'); |
5274
|
0
|
|
|
|
|
0
|
$ui->space; |
5275
|
0
|
|
|
|
|
0
|
$ui->command('cds save data of … as FILENAME'); |
5276
|
0
|
|
|
|
|
0
|
$ui->p('Saves the object\'s data to FILENAME.'); |
5277
|
0
|
|
|
|
|
0
|
$ui->space; |
5278
|
0
|
|
|
|
|
0
|
$ui->title('Related commands'); |
5279
|
0
|
|
|
|
|
0
|
$ui->line('cds open envelope OBJECT'); |
5280
|
0
|
|
|
|
|
0
|
$ui->line('cds show record OBJECT [decrypted with AESKEY]'); |
5281
|
0
|
|
|
|
|
0
|
$ui->line('cds show hashes of OBJECT'); |
5282
|
0
|
|
|
|
|
0
|
$ui->space; |
5283
|
|
|
|
|
|
|
} |
5284
|
|
|
|
|
|
|
|
5285
|
|
|
|
|
|
|
sub get { |
5286
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5287
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
5288
|
|
|
|
|
|
|
|
5289
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
5290
|
|
|
|
|
|
|
|
5291
|
|
|
|
|
|
|
# Retrieve the object |
5292
|
0
|
|
0
|
|
|
0
|
my $object = $o->{actor}->uiGetObject($o->{hash}, $o->{store}, $o->{actor}->preferredKeyPairToken) // return; |
5293
|
|
|
|
|
|
|
|
5294
|
|
|
|
|
|
|
# Decrypt |
5295
|
0
|
0
|
|
|
|
0
|
$object = $object->crypt($o->{aesKey}) if defined $o->{aesKey}; |
5296
|
|
|
|
|
|
|
|
5297
|
|
|
|
|
|
|
# Output |
5298
|
0
|
0
|
|
|
|
0
|
if ($o->{saveData}) { |
|
|
0
|
|
|
|
|
|
5299
|
0
|
|
0
|
|
|
0
|
CDS->writeBytesToFile($o->{filename}, $object->data) // return $o->{ui}->error('Failed to write data to "', $o->{filename}, '".'); |
5300
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen(length $object->data, ' bytes written to ', $o->{filename}, '.'); |
5301
|
|
|
|
|
|
|
} elsif ($o->{saveObject}) { |
5302
|
0
|
|
0
|
|
|
0
|
CDS->writeBytesToFile($o->{filename}, $object->bytes) // return $o->{ui}->error('Failed to write object to "', $o->{filename}, '".'); |
5303
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen(length $object->bytes, ' bytes written to ', $o->{filename}, '.'); |
5304
|
|
|
|
|
|
|
} else { |
5305
|
0
|
|
|
|
|
0
|
$o->{ui}->raw($object->bytes); |
5306
|
|
|
|
|
|
|
} |
5307
|
|
|
|
|
|
|
} |
5308
|
|
|
|
|
|
|
|
5309
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
5310
|
|
|
|
|
|
|
package CDS::Commands::Help; |
5311
|
|
|
|
|
|
|
|
5312
|
|
|
|
|
|
|
sub register { |
5313
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
5314
|
0
|
|
|
|
|
0
|
my $cds = shift; |
5315
|
0
|
|
|
|
|
0
|
my $help = shift; |
5316
|
|
|
|
|
|
|
|
5317
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
5318
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&version}); |
5319
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 0, 0, '--h'); |
5320
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 0, 0, '--help'); |
5321
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 0, 0, '-?'); |
5322
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 0, 0, '-h'); |
5323
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 0, 0, '-help'); |
5324
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 0, 0, '/?'); |
5325
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 0, 0, '/h'); |
5326
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 0, 0, '/help'); |
5327
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 0, 0, '--version'); |
5328
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 0, 0, '-version'); |
5329
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'version'); |
5330
|
|
|
|
|
|
|
} |
5331
|
|
|
|
|
|
|
|
5332
|
|
|
|
|
|
|
sub new { |
5333
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
5334
|
0
|
|
|
|
|
0
|
my $actor = shift; |
5335
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
5336
|
|
|
|
|
|
|
|
5337
|
|
|
|
|
|
|
# END AUTOGENERATED |
5338
|
|
|
|
|
|
|
|
5339
|
|
|
|
|
|
|
# HTML IGNORE |
5340
|
|
|
|
|
|
|
sub help { |
5341
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5342
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
5343
|
|
|
|
|
|
|
|
5344
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
5345
|
0
|
|
|
|
|
0
|
$ui->space; |
5346
|
0
|
|
|
|
|
0
|
$ui->title('Condensation CLI'); |
5347
|
0
|
|
|
|
|
0
|
$ui->line('Version ', $CDS::VERSION, ', ', $CDS::releaseDate, ', implementing the Condensation 1 protocol'); |
5348
|
0
|
|
|
|
|
0
|
$ui->space; |
5349
|
0
|
|
|
|
|
0
|
$ui->p('Condensation is a distributed data system with conflict-free forward merging and end-to-end security. More information is available on ', $ui->a('https://condensation.io'), '.'); |
5350
|
0
|
|
|
|
|
0
|
$ui->space; |
5351
|
0
|
|
|
|
|
0
|
$ui->p('The command line interface (CLI) understands english-like queries like these:'); |
5352
|
0
|
|
|
|
|
0
|
$ui->pushIndent; |
5353
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue('cds show key pair')); |
5354
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue('cds create key pair thomas')); |
5355
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue('cds get 45db86549d6d2af3a45be834f2cb0e08cdbbd7699624e7bfd947a3505e6b03e5 \\')); |
5356
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue(' and decrypt with 8b8b091bbe577d5e8d38eae9cd327aa8123fe402a41ea9dd16d86f42fb70cf7e')); |
5357
|
0
|
|
|
|
|
0
|
$ui->popIndent; |
5358
|
0
|
|
|
|
|
0
|
$ui->space; |
5359
|
0
|
|
|
|
|
0
|
$ui->p('If you don\'t know how to continue a command, simply put a ? to see all valid options:'); |
5360
|
0
|
|
|
|
|
0
|
$ui->pushIndent; |
5361
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue('cds ?')); |
5362
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue('cds show ?')); |
5363
|
0
|
|
|
|
|
0
|
$ui->popIndent; |
5364
|
0
|
|
|
|
|
0
|
$ui->space; |
5365
|
0
|
|
|
|
|
0
|
$ui->p('To see a list of help topics, type'); |
5366
|
0
|
|
|
|
|
0
|
$ui->pushIndent; |
5367
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue('cds help ?')); |
5368
|
0
|
|
|
|
|
0
|
$ui->popIndent; |
5369
|
0
|
|
|
|
|
0
|
$ui->space; |
5370
|
|
|
|
|
|
|
} |
5371
|
|
|
|
|
|
|
|
5372
|
|
|
|
|
|
|
sub version { |
5373
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5374
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
5375
|
|
|
|
|
|
|
|
5376
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
5377
|
0
|
|
|
|
|
0
|
$ui->line('Condensation CLI ', $CDS::VERSION, ', ', $CDS::releaseDate); |
5378
|
0
|
|
|
|
|
0
|
$ui->line('implementing the Condensation 1 protocol'); |
5379
|
|
|
|
|
|
|
} |
5380
|
|
|
|
|
|
|
|
5381
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
5382
|
|
|
|
|
|
|
package CDS::Commands::List; |
5383
|
|
|
|
|
|
|
|
5384
|
|
|
|
|
|
|
sub register { |
5385
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
5386
|
0
|
|
|
|
|
0
|
my $cds = shift; |
5387
|
0
|
|
|
|
|
0
|
my $help = shift; |
5388
|
|
|
|
|
|
|
|
5389
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
5390
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&list}); |
5391
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
5392
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
5393
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
5394
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
5395
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
5396
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
5397
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
5398
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
5399
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
5400
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
5401
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
5402
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
5403
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&listBoxes}); |
5404
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&list}); |
5405
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'list'); |
5406
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'watch', \&collectWatch); |
5407
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'list'); |
5408
|
0
|
|
|
|
|
0
|
$node001->addDefault($node002); |
5409
|
0
|
|
|
|
|
0
|
$node001->addArrow($node003, 1, 0, 'message'); |
5410
|
0
|
|
|
|
|
0
|
$node001->addArrow($node004, 1, 0, 'private'); |
5411
|
0
|
|
|
|
|
0
|
$node001->addArrow($node005, 1, 0, 'public'); |
5412
|
0
|
|
|
|
|
0
|
$node001->addArrow($node006, 0, 0, 'messages', \&collectMessages); |
5413
|
0
|
|
|
|
|
0
|
$node001->addArrow($node006, 0, 0, 'private', \&collectPrivate); |
5414
|
0
|
|
|
|
|
0
|
$node001->addArrow($node006, 0, 0, 'public', \&collectPublic); |
5415
|
0
|
|
|
|
|
0
|
$node001->addArrow($node007, 1, 0, 'my', \&collectMy); |
5416
|
0
|
|
|
|
|
0
|
$node001->addDefault($node011); |
5417
|
0
|
|
|
|
|
0
|
$node002->addArrow($node002, 1, 0, 'BOX', \&collectBox); |
5418
|
0
|
|
|
|
|
0
|
$node002->addArrow($node014, 1, 0, 'BOX', \&collectBox); |
5419
|
0
|
|
|
|
|
0
|
$node003->addArrow($node006, 1, 0, 'box', \&collectMessages); |
5420
|
0
|
|
|
|
|
0
|
$node004->addArrow($node006, 1, 0, 'box', \&collectPrivate); |
5421
|
0
|
|
|
|
|
0
|
$node005->addArrow($node006, 1, 0, 'box', \&collectPublic); |
5422
|
0
|
|
|
|
|
0
|
$node006->addArrow($node011, 1, 0, 'of'); |
5423
|
0
|
|
|
|
|
0
|
$node006->addDefault($node012); |
5424
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'message'); |
5425
|
0
|
|
|
|
|
0
|
$node007->addArrow($node009, 1, 0, 'private'); |
5426
|
0
|
|
|
|
|
0
|
$node007->addArrow($node010, 1, 0, 'public'); |
5427
|
0
|
|
|
|
|
0
|
$node007->addArrow($node015, 1, 0, 'boxes'); |
5428
|
0
|
|
|
|
|
0
|
$node007->addArrow($node015, 0, 0, 'messages', \&collectMessages); |
5429
|
0
|
|
|
|
|
0
|
$node007->addArrow($node015, 0, 0, 'private', \&collectPrivate); |
5430
|
0
|
|
|
|
|
0
|
$node007->addArrow($node015, 0, 0, 'public', \&collectPublic); |
5431
|
0
|
|
|
|
|
0
|
$node008->addArrow($node015, 1, 0, 'box', \&collectMessages); |
5432
|
0
|
|
|
|
|
0
|
$node009->addArrow($node015, 1, 0, 'box', \&collectPrivate); |
5433
|
0
|
|
|
|
|
0
|
$node010->addArrow($node015, 1, 0, 'box', \&collectPublic); |
5434
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'ACTOR', \&collectActor); |
5435
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'KEYPAIR', \&collectKeypair); |
5436
|
0
|
|
|
|
|
0
|
$node011->addArrow($node015, 1, 1, 'ACCOUNT', \&collectAccount); |
5437
|
0
|
|
|
|
|
0
|
$node011->addArrow($node015, 1, 0, 'ACTORGROUP', \&collectActorgroup); |
5438
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'on'); |
5439
|
0
|
|
|
|
|
0
|
$node012->addDefault($node015); |
5440
|
0
|
|
|
|
|
0
|
$node013->addArrow($node015, 1, 0, 'STORE', \&collectStore); |
5441
|
|
|
|
|
|
|
} |
5442
|
|
|
|
|
|
|
|
5443
|
|
|
|
|
|
|
sub collectAccount { |
5444
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5445
|
0
|
|
|
|
|
0
|
my $label = shift; |
5446
|
0
|
|
|
|
|
0
|
my $value = shift; |
5447
|
|
|
|
|
|
|
|
5448
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value->actorHash; |
5449
|
0
|
|
|
|
|
0
|
$o->{store} = $value->cliStore; |
5450
|
|
|
|
|
|
|
} |
5451
|
|
|
|
|
|
|
|
5452
|
|
|
|
|
|
|
sub collectActor { |
5453
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5454
|
0
|
|
|
|
|
0
|
my $label = shift; |
5455
|
0
|
|
|
|
|
0
|
my $value = shift; |
5456
|
|
|
|
|
|
|
|
5457
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value; |
5458
|
|
|
|
|
|
|
} |
5459
|
|
|
|
|
|
|
|
5460
|
|
|
|
|
|
|
sub collectActorgroup { |
5461
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5462
|
0
|
|
|
|
|
0
|
my $label = shift; |
5463
|
0
|
|
|
|
|
0
|
my $value = shift; |
5464
|
|
|
|
|
|
|
|
5465
|
0
|
|
|
|
|
0
|
$o->{actorGroup} = $value; |
5466
|
|
|
|
|
|
|
} |
5467
|
|
|
|
|
|
|
|
5468
|
|
|
|
|
|
|
sub collectBox { |
5469
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5470
|
0
|
|
|
|
|
0
|
my $label = shift; |
5471
|
0
|
|
|
|
|
0
|
my $value = shift; |
5472
|
|
|
|
|
|
|
|
5473
|
0
|
|
|
|
|
0
|
push @{$o->{boxTokens}}, $value; |
|
0
|
|
|
|
|
0
|
|
5474
|
|
|
|
|
|
|
} |
5475
|
|
|
|
|
|
|
|
5476
|
|
|
|
|
|
|
sub collectKeypair { |
5477
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5478
|
0
|
|
|
|
|
0
|
my $label = shift; |
5479
|
0
|
|
|
|
|
0
|
my $value = shift; |
5480
|
|
|
|
|
|
|
|
5481
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value->keyPair->publicKey->hash; |
5482
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
5483
|
|
|
|
|
|
|
} |
5484
|
|
|
|
|
|
|
|
5485
|
|
|
|
|
|
|
sub collectMessages { |
5486
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5487
|
0
|
|
|
|
|
0
|
my $label = shift; |
5488
|
0
|
|
|
|
|
0
|
my $value = shift; |
5489
|
|
|
|
|
|
|
|
5490
|
0
|
|
|
|
|
0
|
$o->{boxLabels} = ['messages']; |
5491
|
|
|
|
|
|
|
} |
5492
|
|
|
|
|
|
|
|
5493
|
|
|
|
|
|
|
sub collectMy { |
5494
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5495
|
0
|
|
|
|
|
0
|
my $label = shift; |
5496
|
0
|
|
|
|
|
0
|
my $value = shift; |
5497
|
|
|
|
|
|
|
|
5498
|
0
|
|
|
|
|
0
|
$o->{my} = 1; |
5499
|
|
|
|
|
|
|
} |
5500
|
|
|
|
|
|
|
|
5501
|
|
|
|
|
|
|
sub collectPrivate { |
5502
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5503
|
0
|
|
|
|
|
0
|
my $label = shift; |
5504
|
0
|
|
|
|
|
0
|
my $value = shift; |
5505
|
|
|
|
|
|
|
|
5506
|
0
|
|
|
|
|
0
|
$o->{boxLabels} = ['private']; |
5507
|
|
|
|
|
|
|
} |
5508
|
|
|
|
|
|
|
|
5509
|
|
|
|
|
|
|
sub collectPublic { |
5510
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5511
|
0
|
|
|
|
|
0
|
my $label = shift; |
5512
|
0
|
|
|
|
|
0
|
my $value = shift; |
5513
|
|
|
|
|
|
|
|
5514
|
0
|
|
|
|
|
0
|
$o->{boxLabels} = ['public']; |
5515
|
|
|
|
|
|
|
} |
5516
|
|
|
|
|
|
|
|
5517
|
|
|
|
|
|
|
sub collectStore { |
5518
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5519
|
0
|
|
|
|
|
0
|
my $label = shift; |
5520
|
0
|
|
|
|
|
0
|
my $value = shift; |
5521
|
|
|
|
|
|
|
|
5522
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
5523
|
|
|
|
|
|
|
} |
5524
|
|
|
|
|
|
|
|
5525
|
|
|
|
|
|
|
sub collectWatch { |
5526
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5527
|
0
|
|
|
|
|
0
|
my $label = shift; |
5528
|
0
|
|
|
|
|
0
|
my $value = shift; |
5529
|
|
|
|
|
|
|
|
5530
|
0
|
|
|
|
|
0
|
$o->{watchTimeout} = 60000; |
5531
|
|
|
|
|
|
|
} |
5532
|
|
|
|
|
|
|
|
5533
|
|
|
|
|
|
|
sub new { |
5534
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
5535
|
0
|
|
|
|
|
0
|
my $actor = shift; |
5536
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
5537
|
|
|
|
|
|
|
|
5538
|
|
|
|
|
|
|
# END AUTOGENERATED |
5539
|
|
|
|
|
|
|
|
5540
|
|
|
|
|
|
|
# HTML FOLDER NAME store-list |
5541
|
|
|
|
|
|
|
# HTML TITLE List |
5542
|
|
|
|
|
|
|
sub help { |
5543
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5544
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
5545
|
|
|
|
|
|
|
|
5546
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
5547
|
0
|
|
|
|
|
0
|
$ui->space; |
5548
|
0
|
|
|
|
|
0
|
$ui->command('cds list BOX'); |
5549
|
0
|
|
|
|
|
0
|
$ui->p('Lists the indicated box. The object references are shown as "cds open envelope …" command, which can be executed to display the corresponding envelope. Change the command to "cds get …" to download the raw object, or "cds show record …" to show it as record.'); |
5550
|
0
|
|
|
|
|
0
|
$ui->space; |
5551
|
0
|
|
|
|
|
0
|
$ui->command('cds list'); |
5552
|
0
|
|
|
|
|
0
|
$ui->p('Lists all boxes of the selected key pair.'); |
5553
|
0
|
|
|
|
|
0
|
$ui->space; |
5554
|
0
|
|
|
|
|
0
|
$ui->command('cds list BOXLABEL'); |
5555
|
0
|
|
|
|
|
0
|
$ui->p('Lists only the indicated box of the selected key pair. BOXLABEL may be:'); |
5556
|
0
|
|
|
|
|
0
|
$ui->line(' message box'); |
5557
|
0
|
|
|
|
|
0
|
$ui->line(' public box'); |
5558
|
0
|
|
|
|
|
0
|
$ui->line(' private box'); |
5559
|
0
|
|
|
|
|
0
|
$ui->space; |
5560
|
0
|
|
|
|
|
0
|
$ui->command('cds list my boxes'); |
5561
|
0
|
|
|
|
|
0
|
$ui->command('cds list my BOXLABEL'); |
5562
|
0
|
|
|
|
|
0
|
$ui->p('Lists your own boxes.'); |
5563
|
0
|
|
|
|
|
0
|
$ui->space; |
5564
|
0
|
|
|
|
|
0
|
$ui->command('cds list [BOXLABEL of] ACTORGROUP|ACCOUNT'); |
5565
|
0
|
|
|
|
|
0
|
$ui->p('Lists boxes of an actor group, or account.'); |
5566
|
0
|
|
|
|
|
0
|
$ui->space; |
5567
|
0
|
|
|
|
|
0
|
$ui->command('cds list [BOXLABEL of] KEYPAIR|ACTOR [on STORE]'); |
5568
|
0
|
|
|
|
|
0
|
$ui->p('Lists boxes of an actor on the specified or selected store.'); |
5569
|
0
|
|
|
|
|
0
|
$ui->space; |
5570
|
|
|
|
|
|
|
} |
5571
|
|
|
|
|
|
|
|
5572
|
|
|
|
|
|
|
sub listBoxes { |
5573
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5574
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
5575
|
|
|
|
|
|
|
|
5576
|
0
|
|
|
|
|
0
|
$o->{boxTokens} = []; |
5577
|
0
|
|
|
|
|
0
|
$o->{boxLabels} = ['messages', 'private', 'public']; |
5578
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
5579
|
|
|
|
|
|
|
|
5580
|
|
|
|
|
|
|
# Use the selected key pair to sign requests |
5581
|
0
|
0
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken if ! $o->{keyPairToken}; |
5582
|
|
|
|
|
|
|
|
5583
|
0
|
|
|
|
|
0
|
for my $boxToken (@{$o->{boxTokens}}) { |
|
0
|
|
|
|
|
0
|
|
5584
|
0
|
|
|
|
|
0
|
$o->listBox($boxToken); |
5585
|
|
|
|
|
|
|
} |
5586
|
|
|
|
|
|
|
|
5587
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
5588
|
|
|
|
|
|
|
} |
5589
|
|
|
|
|
|
|
|
5590
|
|
|
|
|
|
|
sub list { |
5591
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5592
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
5593
|
|
|
|
|
|
|
|
5594
|
0
|
|
|
|
|
0
|
$o->{boxLabels} = ['messages', 'private', 'public']; |
5595
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
5596
|
|
|
|
|
|
|
|
5597
|
|
|
|
|
|
|
# Actor hashes |
5598
|
0
|
|
|
|
|
0
|
my @actorHashes; |
5599
|
|
|
|
|
|
|
my @stores; |
5600
|
0
|
0
|
|
|
|
0
|
if ($o->{my}) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
5601
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->keyPairToken; |
5602
|
0
|
|
|
|
|
0
|
push @actorHashes, $o->{keyPairToken}->keyPair->publicKey->hash; |
5603
|
0
|
|
|
|
|
0
|
push @stores, $o->{actor}->storageStore, $o->{actor}->messagingStore; |
5604
|
|
|
|
|
|
|
} elsif ($o->{actorHash}) { |
5605
|
0
|
|
|
|
|
0
|
push @actorHashes, $o->{actorHash}; |
5606
|
|
|
|
|
|
|
} elsif ($o->{actorGroup}) { |
5607
|
|
|
|
|
|
|
# TODO |
5608
|
|
|
|
|
|
|
} else { |
5609
|
0
|
|
|
|
|
0
|
push @actorHashes, $o->{actor}->preferredActorHash; |
5610
|
|
|
|
|
|
|
} |
5611
|
|
|
|
|
|
|
|
5612
|
|
|
|
|
|
|
# Stores |
5613
|
0
|
0
|
|
|
|
0
|
push @stores, $o->{store} if $o->{store}; |
5614
|
0
|
0
|
|
|
|
0
|
push @stores, $o->{actor}->preferredStore if ! scalar @stores; |
5615
|
|
|
|
|
|
|
|
5616
|
|
|
|
|
|
|
# Use the selected key pair to sign requests |
5617
|
0
|
|
|
|
|
0
|
my $preferredKeyPairToken = $o->{actor}->preferredKeyPairToken; |
5618
|
0
|
0
|
|
|
|
0
|
$o->{keyPairToken} = $preferredKeyPairToken if ! $o->{keyPairToken}; |
5619
|
0
|
0
|
|
|
|
0
|
$o->{keyPairContext} = $preferredKeyPairToken->keyPair->equals($o->{keyPairToken}->keyPair) ? '' : $o->{ui}->gray(' using ', $o->{actor}->keyPairReference($o->{keyPairToken})); |
5620
|
|
|
|
|
|
|
|
5621
|
|
|
|
|
|
|
# List boxes |
5622
|
0
|
|
|
|
|
0
|
for my $store (@stores) { |
5623
|
0
|
|
|
|
|
0
|
for my $actorHash (@actorHashes) { |
5624
|
0
|
|
|
|
|
0
|
for my $boxLabel (@{$o->{boxLabels}}) { |
|
0
|
|
|
|
|
0
|
|
5625
|
0
|
|
|
|
|
0
|
$o->listBox(CDS::BoxToken->new(CDS::AccountToken->new($store, $actorHash), $boxLabel)); |
5626
|
|
|
|
|
|
|
} |
5627
|
|
|
|
|
|
|
} |
5628
|
|
|
|
|
|
|
} |
5629
|
|
|
|
|
|
|
|
5630
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
5631
|
|
|
|
|
|
|
} |
5632
|
|
|
|
|
|
|
|
5633
|
|
|
|
|
|
|
sub listBox { |
5634
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5635
|
0
|
|
|
|
|
0
|
my $boxToken = shift; |
5636
|
|
|
|
|
|
|
|
5637
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
5638
|
0
|
|
|
|
|
0
|
$o->{ui}->title($o->{actor}->blueBoxReference($boxToken)); |
5639
|
|
|
|
|
|
|
|
5640
|
|
|
|
|
|
|
# Query the store |
5641
|
0
|
|
|
|
|
0
|
my $store = $boxToken->accountToken->cliStore; |
5642
|
0
|
|
0
|
|
|
0
|
my ($hashes, $storeError) = $store->list($boxToken->accountToken->actorHash, $boxToken->boxLabel, $o->{watchTimeout} // 0, $o->{keyPairToken}->keyPair); |
5643
|
0
|
0
|
|
|
|
0
|
return if defined $storeError; |
5644
|
|
|
|
|
|
|
|
5645
|
|
|
|
|
|
|
# Print the result |
5646
|
0
|
|
|
|
|
0
|
my $count = scalar @$hashes; |
5647
|
0
|
0
|
|
|
|
0
|
return if ! $count; |
5648
|
|
|
|
|
|
|
|
5649
|
0
|
0
|
|
|
|
0
|
my $context = $boxToken->boxLabel eq 'messages' ? $o->{ui}->gray(' on ', $o->{actor}->storeReference($store)) : $o->{ui}->gray(' from ', $o->{actor}->accountReference($boxToken->accountToken)); |
5650
|
0
|
0
|
0
|
|
|
0
|
my $keyPairContext = $boxToken->boxLabel eq 'public' ? '' : $o->{keyPairContext} // ''; |
5651
|
0
|
|
|
|
|
0
|
foreach my $hash (sort { $a->bytes cmp $b->bytes } @$hashes) { |
|
0
|
|
|
|
|
0
|
|
5652
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds open envelope ', $hash->hex), $context, $keyPairContext); |
5653
|
|
|
|
|
|
|
} |
5654
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($count.' entries') if $count > 5; |
5655
|
|
|
|
|
|
|
} |
5656
|
|
|
|
|
|
|
|
5657
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
5658
|
|
|
|
|
|
|
package CDS::Commands::Modify; |
5659
|
|
|
|
|
|
|
|
5660
|
|
|
|
|
|
|
sub register { |
5661
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
5662
|
0
|
|
|
|
|
0
|
my $cds = shift; |
5663
|
0
|
|
|
|
|
0
|
my $help = shift; |
5664
|
|
|
|
|
|
|
|
5665
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
5666
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
5667
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
5668
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
5669
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
5670
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
5671
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
5672
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
5673
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(1); |
5674
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
5675
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
5676
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
5677
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
5678
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
5679
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(0); |
5680
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(0); |
5681
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&modify}); |
5682
|
0
|
|
|
|
|
0
|
$cds->addDefault($node000); |
5683
|
0
|
|
|
|
|
0
|
$help->addArrow($node007, 1, 0, 'add'); |
5684
|
0
|
|
|
|
|
0
|
$help->addArrow($node007, 1, 0, 'purge'); |
5685
|
0
|
|
|
|
|
0
|
$help->addArrow($node007, 1, 0, 'remove'); |
5686
|
0
|
|
|
|
|
0
|
$node000->addArrow($node001, 1, 0, 'add'); |
5687
|
0
|
|
|
|
|
0
|
$node000->addArrow($node002, 1, 0, 'remove'); |
5688
|
0
|
|
|
|
|
0
|
$node000->addArrow($node003, 1, 0, 'add'); |
5689
|
0
|
|
|
|
|
0
|
$node000->addArrow($node008, 1, 0, 'purge', \&collectPurge); |
5690
|
0
|
|
|
|
|
0
|
$node001->addArrow($node001, 1, 0, 'HASH', \&collectHash); |
5691
|
0
|
|
|
|
|
0
|
$node001->addArrow($node004, 1, 0, 'HASH', \&collectHash); |
5692
|
0
|
|
|
|
|
0
|
$node002->addArrow($node002, 1, 0, 'HASH', \&collectHash1); |
5693
|
0
|
|
|
|
|
0
|
$node002->addArrow($node005, 1, 0, 'HASH', \&collectHash1); |
5694
|
0
|
|
|
|
|
0
|
$node003->addArrow($node003, 1, 0, 'FILE', \&collectFile); |
5695
|
0
|
|
|
|
|
0
|
$node003->addArrow($node006, 1, 0, 'FILE', \&collectFile); |
5696
|
0
|
|
|
|
|
0
|
$node004->addArrow($node008, 1, 0, 'to'); |
5697
|
0
|
|
|
|
|
0
|
$node005->addArrow($node008, 1, 0, 'from'); |
5698
|
0
|
|
|
|
|
0
|
$node006->addArrow($node008, 1, 0, 'to'); |
5699
|
0
|
|
|
|
|
0
|
$node008->addArrow($node000, 1, 0, 'and'); |
5700
|
0
|
|
|
|
|
0
|
$node008->addArrow($node009, 1, 0, 'message'); |
5701
|
0
|
|
|
|
|
0
|
$node008->addArrow($node010, 1, 0, 'private'); |
5702
|
0
|
|
|
|
|
0
|
$node008->addArrow($node011, 1, 0, 'public'); |
5703
|
0
|
|
|
|
|
0
|
$node008->addArrow($node012, 0, 0, 'messages', \&collectMessages); |
5704
|
0
|
|
|
|
|
0
|
$node008->addArrow($node012, 0, 0, 'private', \&collectPrivate); |
5705
|
0
|
|
|
|
|
0
|
$node008->addArrow($node012, 0, 0, 'public', \&collectPublic); |
5706
|
0
|
|
|
|
|
0
|
$node008->addArrow($node016, 1, 0, 'BOX', \&collectBox); |
5707
|
0
|
|
|
|
|
0
|
$node009->addArrow($node012, 1, 0, 'box', \&collectMessages); |
5708
|
0
|
|
|
|
|
0
|
$node010->addArrow($node012, 1, 0, 'box', \&collectPrivate); |
5709
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'box', \&collectPublic); |
5710
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'of'); |
5711
|
0
|
|
|
|
|
0
|
$node013->addArrow($node014, 1, 0, 'ACTOR', \&collectActor); |
5712
|
0
|
|
|
|
|
0
|
$node013->addArrow($node014, 1, 0, 'KEYPAIR', \&collectKeypair); |
5713
|
0
|
|
|
|
|
0
|
$node013->addArrow($node016, 1, 1, 'ACCOUNT', \&collectAccount); |
5714
|
0
|
|
|
|
|
0
|
$node014->addArrow($node015, 1, 0, 'on'); |
5715
|
0
|
|
|
|
|
0
|
$node014->addDefault($node016); |
5716
|
0
|
|
|
|
|
0
|
$node015->addArrow($node016, 1, 0, 'STORE', \&collectStore); |
5717
|
|
|
|
|
|
|
} |
5718
|
|
|
|
|
|
|
|
5719
|
|
|
|
|
|
|
sub collectAccount { |
5720
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5721
|
0
|
|
|
|
|
0
|
my $label = shift; |
5722
|
0
|
|
|
|
|
0
|
my $value = shift; |
5723
|
|
|
|
|
|
|
|
5724
|
0
|
|
|
|
|
0
|
$o->{boxToken} = CDS::BoxToken->new($value, $o->{boxLabel}); |
5725
|
0
|
|
|
|
|
0
|
delete $o->{boxLabel}; |
5726
|
|
|
|
|
|
|
} |
5727
|
|
|
|
|
|
|
|
5728
|
|
|
|
|
|
|
sub collectActor { |
5729
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5730
|
0
|
|
|
|
|
0
|
my $label = shift; |
5731
|
0
|
|
|
|
|
0
|
my $value = shift; |
5732
|
|
|
|
|
|
|
|
5733
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value; |
5734
|
|
|
|
|
|
|
} |
5735
|
|
|
|
|
|
|
|
5736
|
|
|
|
|
|
|
sub collectBox { |
5737
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5738
|
0
|
|
|
|
|
0
|
my $label = shift; |
5739
|
0
|
|
|
|
|
0
|
my $value = shift; |
5740
|
|
|
|
|
|
|
|
5741
|
0
|
|
|
|
|
0
|
$o->{boxToken} = $value; |
5742
|
|
|
|
|
|
|
} |
5743
|
|
|
|
|
|
|
|
5744
|
|
|
|
|
|
|
sub collectFile { |
5745
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5746
|
0
|
|
|
|
|
0
|
my $label = shift; |
5747
|
0
|
|
|
|
|
0
|
my $value = shift; |
5748
|
|
|
|
|
|
|
|
5749
|
0
|
|
|
|
|
0
|
push @{$o->{fileAdditions}}, $value; |
|
0
|
|
|
|
|
0
|
|
5750
|
|
|
|
|
|
|
} |
5751
|
|
|
|
|
|
|
|
5752
|
|
|
|
|
|
|
sub collectHash { |
5753
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5754
|
0
|
|
|
|
|
0
|
my $label = shift; |
5755
|
0
|
|
|
|
|
0
|
my $value = shift; |
5756
|
|
|
|
|
|
|
|
5757
|
0
|
|
|
|
|
0
|
push @{$o->{additions}}, $value; |
|
0
|
|
|
|
|
0
|
|
5758
|
|
|
|
|
|
|
} |
5759
|
|
|
|
|
|
|
|
5760
|
|
|
|
|
|
|
sub collectHash1 { |
5761
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5762
|
0
|
|
|
|
|
0
|
my $label = shift; |
5763
|
0
|
|
|
|
|
0
|
my $value = shift; |
5764
|
|
|
|
|
|
|
|
5765
|
0
|
|
|
|
|
0
|
push @{$o->{removals}}, $value; |
|
0
|
|
|
|
|
0
|
|
5766
|
|
|
|
|
|
|
} |
5767
|
|
|
|
|
|
|
|
5768
|
|
|
|
|
|
|
sub collectKeypair { |
5769
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5770
|
0
|
|
|
|
|
0
|
my $label = shift; |
5771
|
0
|
|
|
|
|
0
|
my $value = shift; |
5772
|
|
|
|
|
|
|
|
5773
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value->publicKey->hash; |
5774
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
5775
|
|
|
|
|
|
|
} |
5776
|
|
|
|
|
|
|
|
5777
|
|
|
|
|
|
|
sub collectMessages { |
5778
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5779
|
0
|
|
|
|
|
0
|
my $label = shift; |
5780
|
0
|
|
|
|
|
0
|
my $value = shift; |
5781
|
|
|
|
|
|
|
|
5782
|
0
|
|
|
|
|
0
|
$o->{boxLabel} = 'messages'; |
5783
|
|
|
|
|
|
|
} |
5784
|
|
|
|
|
|
|
|
5785
|
|
|
|
|
|
|
sub collectPrivate { |
5786
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5787
|
0
|
|
|
|
|
0
|
my $label = shift; |
5788
|
0
|
|
|
|
|
0
|
my $value = shift; |
5789
|
|
|
|
|
|
|
|
5790
|
0
|
|
|
|
|
0
|
$o->{boxLabel} = 'private'; |
5791
|
|
|
|
|
|
|
} |
5792
|
|
|
|
|
|
|
|
5793
|
|
|
|
|
|
|
sub collectPublic { |
5794
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5795
|
0
|
|
|
|
|
0
|
my $label = shift; |
5796
|
0
|
|
|
|
|
0
|
my $value = shift; |
5797
|
|
|
|
|
|
|
|
5798
|
0
|
|
|
|
|
0
|
$o->{boxLabel} = 'public'; |
5799
|
|
|
|
|
|
|
} |
5800
|
|
|
|
|
|
|
|
5801
|
|
|
|
|
|
|
sub collectPurge { |
5802
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5803
|
0
|
|
|
|
|
0
|
my $label = shift; |
5804
|
0
|
|
|
|
|
0
|
my $value = shift; |
5805
|
|
|
|
|
|
|
|
5806
|
0
|
|
|
|
|
0
|
$o->{purge} = 1; |
5807
|
|
|
|
|
|
|
} |
5808
|
|
|
|
|
|
|
|
5809
|
|
|
|
|
|
|
sub collectStore { |
5810
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5811
|
0
|
|
|
|
|
0
|
my $label = shift; |
5812
|
0
|
|
|
|
|
0
|
my $value = shift; |
5813
|
|
|
|
|
|
|
|
5814
|
0
|
|
|
|
|
0
|
$o->{boxToken} = CDS::BoxToken->new(CDS::AccountToken->new($value, $o->{actorHash}), $o->{boxLabel}); |
5815
|
0
|
|
|
|
|
0
|
delete $o->{boxLabel}; |
5816
|
0
|
|
|
|
|
0
|
delete $o->{actorHash}; |
5817
|
|
|
|
|
|
|
} |
5818
|
|
|
|
|
|
|
|
5819
|
|
|
|
|
|
|
sub new { |
5820
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
5821
|
0
|
|
|
|
|
0
|
my $actor = shift; |
5822
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
5823
|
|
|
|
|
|
|
|
5824
|
|
|
|
|
|
|
# END AUTOGENERATED |
5825
|
|
|
|
|
|
|
|
5826
|
|
|
|
|
|
|
# HTML FOLDER NAME store-modify |
5827
|
|
|
|
|
|
|
# HTML TITLE Modify |
5828
|
|
|
|
|
|
|
sub help { |
5829
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5830
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
5831
|
|
|
|
|
|
|
|
5832
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
5833
|
0
|
|
|
|
|
0
|
$ui->space; |
5834
|
0
|
|
|
|
|
0
|
$ui->command('cds add HASH* to BOX'); |
5835
|
0
|
|
|
|
|
0
|
$ui->p('Adds HASH to BOX.'); |
5836
|
0
|
|
|
|
|
0
|
$ui->space; |
5837
|
0
|
|
|
|
|
0
|
$ui->command('cds add FILE* to BOX'); |
5838
|
0
|
|
|
|
|
0
|
$ui->p('Adds the envelope FILE to BOX.'); |
5839
|
0
|
|
|
|
|
0
|
$ui->space; |
5840
|
0
|
|
|
|
|
0
|
$ui->command('cds remove HASH* from BOX'); |
5841
|
0
|
|
|
|
|
0
|
$ui->p('Removes HASH from BOX.'); |
5842
|
0
|
|
|
|
|
0
|
$ui->p('Note that the store may just mark the hash for removal, and defer its actual removal, or even cancel it. Such removals will still be reported as success.'); |
5843
|
0
|
|
|
|
|
0
|
$ui->space; |
5844
|
0
|
|
|
|
|
0
|
$ui->command('cds purge BOX'); |
5845
|
0
|
|
|
|
|
0
|
$ui->p('Empties BOX, i.e., removes all its hashes.'); |
5846
|
0
|
|
|
|
|
0
|
$ui->space; |
5847
|
0
|
|
|
|
|
0
|
$ui->command('… BOXLABEL of ACCOUNT'); |
5848
|
0
|
|
|
|
|
0
|
$ui->p('Modifies a box of an actor group, or account.'); |
5849
|
0
|
|
|
|
|
0
|
$ui->space; |
5850
|
0
|
|
|
|
|
0
|
$ui->command('… BOXLABEL of KEYPAIR on STORE'); |
5851
|
0
|
|
|
|
|
0
|
$ui->command('… BOXLABEL of ACTOR on STORE'); |
5852
|
0
|
|
|
|
|
0
|
$ui->p('Modifies a box of a key pair or an actor on a specific store.'); |
5853
|
0
|
|
|
|
|
0
|
$ui->space; |
5854
|
|
|
|
|
|
|
} |
5855
|
|
|
|
|
|
|
|
5856
|
|
|
|
|
|
|
sub modify { |
5857
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5858
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
5859
|
|
|
|
|
|
|
|
5860
|
0
|
|
|
|
|
0
|
$o->{additions} = []; |
5861
|
0
|
|
|
|
|
0
|
$o->{removals} = []; |
5862
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
5863
|
|
|
|
|
|
|
|
5864
|
|
|
|
|
|
|
# Add a box using the selected store |
5865
|
0
|
0
|
0
|
|
|
0
|
if ($o->{actorHash} && $o->{boxLabel}) { |
5866
|
0
|
|
|
|
|
0
|
$o->{boxToken} = CDS::BoxToken->new(CDS::AccountToken->new($o->{actor}->preferredStore, $o->{actorHash}), $o->{boxLabel}); |
5867
|
0
|
|
|
|
|
0
|
delete $o->{actorHash}; |
5868
|
0
|
|
|
|
|
0
|
delete $o->{boxLabel}; |
5869
|
|
|
|
|
|
|
} |
5870
|
|
|
|
|
|
|
|
5871
|
0
|
|
|
|
|
0
|
my $store = $o->{boxToken}->accountToken->cliStore; |
5872
|
|
|
|
|
|
|
|
5873
|
|
|
|
|
|
|
# Prepare additions |
5874
|
0
|
|
|
|
|
0
|
my $modifications = CDS::StoreModifications->new; |
5875
|
0
|
|
|
|
|
0
|
for my $hash (@{$o->{additions}}) { |
|
0
|
|
|
|
|
0
|
|
5876
|
0
|
|
|
|
|
0
|
$modifications->add($o->{boxToken}->accountToken->actorHash, $o->{boxToken}->boxLabel, $hash); |
5877
|
|
|
|
|
|
|
} |
5878
|
|
|
|
|
|
|
|
5879
|
0
|
|
|
|
|
0
|
for my $file (@{$o->{fileAdditions}}) { |
|
0
|
|
|
|
|
0
|
|
5880
|
0
|
|
0
|
|
|
0
|
my $bytes = CDS->readBytesFromFile($file) // return $o->{ui}->error('Unable to read "', $file, '".'); |
5881
|
0
|
|
0
|
|
|
0
|
my $object = CDS::Object->fromBytes($bytes) // return $o->{ui}->error('"', $file, '" is not a Condensation object.'); |
5882
|
0
|
|
|
|
|
0
|
my $hash = $object->calculateHash; |
5883
|
0
|
0
|
|
|
|
0
|
$o->{ui}->warning('"', $file, '" is not a valid envelope. The server may reject it.') if ! $o->{actor}->isEnvelope($object); |
5884
|
0
|
|
|
|
|
0
|
$modifications->add($o->{boxToken}->accountToken->actorHash, $o->{boxToken}->boxLabel, $hash, $object); |
5885
|
|
|
|
|
|
|
} |
5886
|
|
|
|
|
|
|
|
5887
|
|
|
|
|
|
|
# Prepare removals |
5888
|
0
|
|
|
|
|
0
|
my $boxRemovals = []; |
5889
|
0
|
|
|
|
|
0
|
for my $hash (@{$o->{removals}}) { |
|
0
|
|
|
|
|
0
|
|
5890
|
0
|
|
|
|
|
0
|
$modifications->remove($o->{boxToken}->accountToken->actorHash, $o->{boxToken}->boxLabel, $hash); |
5891
|
|
|
|
|
|
|
} |
5892
|
|
|
|
|
|
|
|
5893
|
|
|
|
|
|
|
# If purging is requested, list the box |
5894
|
0
|
0
|
|
|
|
0
|
if ($o->{purge}) { |
5895
|
0
|
|
|
|
|
0
|
my ($hashes, $error) = $store->list($o->{boxToken}->accountToken->actorHash, $o->{boxToken}->boxLabel, 0); |
5896
|
0
|
0
|
|
|
|
0
|
return if defined $error; |
5897
|
0
|
0
|
|
|
|
0
|
$o->{ui}->warning('The box is empty.') if ! scalar @$hashes; |
5898
|
|
|
|
|
|
|
|
5899
|
0
|
|
|
|
|
0
|
for my $hash (@$hashes) { |
5900
|
0
|
|
|
|
|
0
|
$modifications->remove($o->{boxToken}->accountToken->actorHash, $o->{boxToken}->boxLabel, $hash); |
5901
|
|
|
|
|
|
|
} |
5902
|
|
|
|
|
|
|
} |
5903
|
|
|
|
|
|
|
|
5904
|
|
|
|
|
|
|
# Cancel if there is nothing to do |
5905
|
0
|
0
|
|
|
|
0
|
return if $modifications->isEmpty; |
5906
|
|
|
|
|
|
|
|
5907
|
|
|
|
|
|
|
# Modify the box |
5908
|
0
|
|
0
|
|
|
0
|
my $keyPairToken = $o->{keyPairToken} // $o->{actor}->preferredKeyPairToken; |
5909
|
0
|
|
|
|
|
0
|
my $error = $store->modify($modifications, $keyPairToken->keyPair); |
5910
|
0
|
0
|
|
|
|
0
|
$o->{ui}->pGreen('Box modified.') if ! defined $error; |
5911
|
|
|
|
|
|
|
|
5912
|
|
|
|
|
|
|
# Print undo information |
5913
|
0
|
0
|
0
|
|
|
0
|
if ($o->{purge} && scalar @$boxRemovals) { |
5914
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
5915
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray('To undo purging, type:')); |
5916
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray(' cds add ', join(" \\\n ", map { $_->{hash}->hex } @$boxRemovals), " \\\n to ", $o->{actor}->boxReference($o->{boxToken}))); |
|
0
|
|
|
|
|
0
|
|
5917
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
5918
|
|
|
|
|
|
|
} |
5919
|
|
|
|
|
|
|
} |
5920
|
|
|
|
|
|
|
|
5921
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
5922
|
|
|
|
|
|
|
package CDS::Commands::OpenEnvelope; |
5923
|
|
|
|
|
|
|
|
5924
|
|
|
|
|
|
|
sub register { |
5925
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
5926
|
0
|
|
|
|
|
0
|
my $cds = shift; |
5927
|
0
|
|
|
|
|
0
|
my $help = shift; |
5928
|
|
|
|
|
|
|
|
5929
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
5930
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
5931
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
5932
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(1); |
5933
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(1); |
5934
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
5935
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
5936
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(1); |
5937
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
5938
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
5939
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
5940
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(1); |
5941
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
5942
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&openEnvelope}); |
5943
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'open'); |
5944
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'open'); |
5945
|
0
|
|
|
|
|
0
|
$node000->addArrow($node002, 1, 0, 'envelope'); |
5946
|
0
|
|
|
|
|
0
|
$node001->addArrow($node003, 1, 0, 'envelope'); |
5947
|
0
|
|
|
|
|
0
|
$node003->addArrow($node004, 1, 0, 'HASH', \&collectHash); |
5948
|
0
|
|
|
|
|
0
|
$node003->addArrow($node007, 1, 0, 'OBJECT', \&collectObject); |
5949
|
0
|
|
|
|
|
0
|
$node004->addArrow($node005, 1, 0, 'from'); |
5950
|
0
|
|
|
|
|
0
|
$node004->addArrow($node006, 1, 0, 'from'); |
5951
|
0
|
|
|
|
|
0
|
$node004->addDefault($node009); |
5952
|
0
|
|
|
|
|
0
|
$node005->addArrow($node009, 1, 0, 'ACTOR', \&collectActor); |
5953
|
0
|
|
|
|
|
0
|
$node006->addArrow($node011, 1, 1, 'ACCOUNT', \&collectAccount); |
5954
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'from'); |
5955
|
0
|
|
|
|
|
0
|
$node007->addDefault($node011); |
5956
|
0
|
|
|
|
|
0
|
$node008->addArrow($node011, 1, 0, 'ACTOR', \&collectActor); |
5957
|
0
|
|
|
|
|
0
|
$node009->addArrow($node010, 1, 0, 'on'); |
5958
|
0
|
|
|
|
|
0
|
$node009->addDefault($node011); |
5959
|
0
|
|
|
|
|
0
|
$node010->addArrow($node011, 1, 0, 'STORE', \&collectStore); |
5960
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'using'); |
5961
|
0
|
|
|
|
|
0
|
$node011->addDefault($node013); |
5962
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'KEYPAIR', \&collectKeypair); |
5963
|
|
|
|
|
|
|
} |
5964
|
|
|
|
|
|
|
|
5965
|
|
|
|
|
|
|
sub collectAccount { |
5966
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5967
|
0
|
|
|
|
|
0
|
my $label = shift; |
5968
|
0
|
|
|
|
|
0
|
my $value = shift; |
5969
|
|
|
|
|
|
|
|
5970
|
0
|
|
|
|
|
0
|
$o->{senderHash} = $value->actorHash; |
5971
|
0
|
|
|
|
|
0
|
$o->{store} = $value->cliStore; |
5972
|
|
|
|
|
|
|
} |
5973
|
|
|
|
|
|
|
|
5974
|
|
|
|
|
|
|
sub collectActor { |
5975
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5976
|
0
|
|
|
|
|
0
|
my $label = shift; |
5977
|
0
|
|
|
|
|
0
|
my $value = shift; |
5978
|
|
|
|
|
|
|
|
5979
|
0
|
|
|
|
|
0
|
$o->{senderHash} = $value; |
5980
|
|
|
|
|
|
|
} |
5981
|
|
|
|
|
|
|
|
5982
|
|
|
|
|
|
|
sub collectHash { |
5983
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5984
|
0
|
|
|
|
|
0
|
my $label = shift; |
5985
|
0
|
|
|
|
|
0
|
my $value = shift; |
5986
|
|
|
|
|
|
|
|
5987
|
0
|
|
|
|
|
0
|
$o->{hash} = $value; |
5988
|
0
|
|
|
|
|
0
|
$o->{store} = $o->{actor}->preferredStore; |
5989
|
|
|
|
|
|
|
} |
5990
|
|
|
|
|
|
|
|
5991
|
|
|
|
|
|
|
sub collectKeypair { |
5992
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
5993
|
0
|
|
|
|
|
0
|
my $label = shift; |
5994
|
0
|
|
|
|
|
0
|
my $value = shift; |
5995
|
|
|
|
|
|
|
|
5996
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
5997
|
|
|
|
|
|
|
} |
5998
|
|
|
|
|
|
|
|
5999
|
|
|
|
|
|
|
sub collectObject { |
6000
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6001
|
0
|
|
|
|
|
0
|
my $label = shift; |
6002
|
0
|
|
|
|
|
0
|
my $value = shift; |
6003
|
|
|
|
|
|
|
|
6004
|
0
|
|
|
|
|
0
|
$o->{hash} = $value->hash; |
6005
|
0
|
|
|
|
|
0
|
$o->{store} = $value->cliStore; |
6006
|
|
|
|
|
|
|
} |
6007
|
|
|
|
|
|
|
|
6008
|
|
|
|
|
|
|
sub collectStore { |
6009
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6010
|
0
|
|
|
|
|
0
|
my $label = shift; |
6011
|
0
|
|
|
|
|
0
|
my $value = shift; |
6012
|
|
|
|
|
|
|
|
6013
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
6014
|
|
|
|
|
|
|
} |
6015
|
|
|
|
|
|
|
|
6016
|
|
|
|
|
|
|
sub new { |
6017
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
6018
|
0
|
|
|
|
|
0
|
my $actor = shift; |
6019
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
6020
|
|
|
|
|
|
|
|
6021
|
|
|
|
|
|
|
# END AUTOGENERATED |
6022
|
|
|
|
|
|
|
|
6023
|
|
|
|
|
|
|
# HTML FOLDER NAME open-envelope |
6024
|
|
|
|
|
|
|
# HTML TITLE Open envelope |
6025
|
|
|
|
|
|
|
sub help { |
6026
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6027
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
6028
|
|
|
|
|
|
|
|
6029
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
6030
|
0
|
|
|
|
|
0
|
$ui->space; |
6031
|
0
|
|
|
|
|
0
|
$ui->command('cds open envelope OBJECT'); |
6032
|
0
|
|
|
|
|
0
|
$ui->command('cds open envelope HASH on STORE'); |
6033
|
0
|
|
|
|
|
0
|
$ui->p('Downloads an envelope, verifies its signatures, and tries to decrypt the AES key using the selected key pair and your own key pair.'); |
6034
|
0
|
|
|
|
|
0
|
$ui->p('In addition to displaying the envelope details, this command also displays the necessary "cds show record …" command to retrieve the content.'); |
6035
|
0
|
|
|
|
|
0
|
$ui->space; |
6036
|
0
|
|
|
|
|
0
|
$ui->command('cds open envelope HASH'); |
6037
|
0
|
|
|
|
|
0
|
$ui->p('As above, but uses the selected store.'); |
6038
|
0
|
|
|
|
|
0
|
$ui->space; |
6039
|
0
|
|
|
|
|
0
|
$ui->command('… from ACTOR'); |
6040
|
0
|
|
|
|
|
0
|
$ui->p('Assumes that the envelope was signed by ACTOR, and downloads the corresponding public key. The sender store is assumed to be the envelope\'s store. This is useful to verify public and private envelopes.'); |
6041
|
0
|
|
|
|
|
0
|
$ui->space; |
6042
|
0
|
|
|
|
|
0
|
$ui->command('… using KEYPAIR'); |
6043
|
0
|
|
|
|
|
0
|
$ui->p('Tries to decrypt the AES key using this key pair, instead of the selected key pair.'); |
6044
|
0
|
|
|
|
|
0
|
$ui->space; |
6045
|
|
|
|
|
|
|
} |
6046
|
|
|
|
|
|
|
|
6047
|
|
|
|
|
|
|
sub openEnvelope { |
6048
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6049
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
6050
|
|
|
|
|
|
|
|
6051
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken; |
6052
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
6053
|
|
|
|
|
|
|
|
6054
|
|
|
|
|
|
|
# Get the envelope |
6055
|
0
|
|
0
|
|
|
0
|
my $envelope = $o->{actor}->uiGetRecord($o->{hash}, $o->{store}, $o->{keyPairToken}) // return; |
6056
|
|
|
|
|
|
|
|
6057
|
|
|
|
|
|
|
# Continue by envelope type |
6058
|
0
|
|
|
|
|
0
|
my $contentRecord = $envelope->child('content'); |
6059
|
0
|
0
|
|
|
|
0
|
if ($contentRecord->hashValue) { |
|
|
0
|
|
|
|
|
|
6060
|
0
|
0
|
|
|
|
0
|
if ($envelope->contains('encrypted for')) { |
6061
|
0
|
|
|
|
|
0
|
$o->processPrivateEnvelope($envelope); |
6062
|
|
|
|
|
|
|
} else { |
6063
|
0
|
|
|
|
|
0
|
$o->processPublicEnvelope($envelope); |
6064
|
|
|
|
|
|
|
} |
6065
|
|
|
|
|
|
|
} elsif (length $contentRecord->bytesValue) { |
6066
|
0
|
0
|
0
|
|
|
0
|
if ($envelope->contains('head') && $envelope->contains('mac')) { |
6067
|
0
|
|
|
|
|
0
|
$o->processStreamEnvelope($envelope); |
6068
|
|
|
|
|
|
|
} else { |
6069
|
0
|
|
|
|
|
0
|
$o->processMessageEnvelope($envelope); |
6070
|
|
|
|
|
|
|
} |
6071
|
|
|
|
|
|
|
} else { |
6072
|
0
|
|
|
|
|
0
|
$o->processOther($envelope); |
6073
|
|
|
|
|
|
|
} |
6074
|
|
|
|
|
|
|
} |
6075
|
|
|
|
|
|
|
|
6076
|
|
|
|
|
|
|
sub processOther { |
6077
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6078
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6079
|
|
|
|
|
|
|
|
6080
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6081
|
0
|
|
|
|
|
0
|
$o->{ui}->pOrange('This is not an envelope. Envelopes always have a "content" section. The raw record is shown below.'); |
6082
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6083
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Record'); |
6084
|
0
|
|
|
|
|
0
|
$o->{ui}->recordChildren($envelope, $o->{actor}->storeReference($o->{store})); |
6085
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6086
|
|
|
|
|
|
|
} |
6087
|
|
|
|
|
|
|
|
6088
|
|
|
|
|
|
|
sub processPublicEnvelope { |
6089
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6090
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6091
|
|
|
|
|
|
|
|
6092
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6093
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Public envelope'); |
6094
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds show record ', $o->{hash}->hex, ' on ', $o->{actor}->storeReference($o->{store}))); |
6095
|
|
|
|
|
|
|
|
6096
|
0
|
|
|
|
|
0
|
my $contentHash = $envelope->child('content')->hashValue; |
6097
|
0
|
|
|
|
|
0
|
$o->showPublicPrivateSignature($envelope, $contentHash); |
6098
|
|
|
|
|
|
|
|
6099
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6100
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Content'); |
6101
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds show record ', $contentHash->hex, ' on ', $o->{actor}->storeReference($o->{store}))); |
6102
|
|
|
|
|
|
|
|
6103
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6104
|
|
|
|
|
|
|
} |
6105
|
|
|
|
|
|
|
|
6106
|
|
|
|
|
|
|
sub processPrivateEnvelope { |
6107
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6108
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6109
|
|
|
|
|
|
|
|
6110
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6111
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Private envelope'); |
6112
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds show record ', $o->{hash}->hex, ' on ', $o->{actor}->storeReference($o->{store}))); |
6113
|
|
|
|
|
|
|
|
6114
|
0
|
|
|
|
|
0
|
my $aesKey = $o->decryptAesKey($envelope); |
6115
|
0
|
|
|
|
|
0
|
my $contentHash = $envelope->child('content')->hashValue; |
6116
|
0
|
|
|
|
|
0
|
$o->showPublicPrivateSignature($envelope, $contentHash); |
6117
|
0
|
|
|
|
|
0
|
$o->showEncryptedFor($envelope); |
6118
|
|
|
|
|
|
|
|
6119
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6120
|
0
|
0
|
|
|
|
0
|
if ($aesKey) { |
6121
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Content'); |
6122
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds show record ', $contentHash->hex, ' on ', $o->{actor}->storeReference($o->{store}), ' decrypted with ', unpack('H*', $aesKey))); |
6123
|
|
|
|
|
|
|
} else { |
6124
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Encrypted content'); |
6125
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds get ', $contentHash->hex, ' on ', $o->{actor}->storeReference($o->{store}))); |
6126
|
|
|
|
|
|
|
} |
6127
|
|
|
|
|
|
|
|
6128
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6129
|
|
|
|
|
|
|
} |
6130
|
|
|
|
|
|
|
|
6131
|
|
|
|
|
|
|
sub showPublicPrivateSignature { |
6132
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6133
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6134
|
0
|
0
|
0
|
|
|
0
|
my $contentHash = shift; die 'wrong type '.ref($contentHash).' for $contentHash' if defined $contentHash && ref $contentHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
6135
|
|
|
|
|
|
|
|
6136
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6137
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Signed by'); |
6138
|
0
|
0
|
|
|
|
0
|
if ($o->{senderHash}) { |
6139
|
0
|
|
|
|
|
0
|
my $accountToken = CDS::AccountToken->new($o->{store}, $o->{senderHash}); |
6140
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{actor}->blueAccountReference($accountToken)); |
6141
|
0
|
|
|
|
|
0
|
$o->showSignature($envelope, $o->{senderHash}, $o->{store}, $contentHash); |
6142
|
|
|
|
|
|
|
} else { |
6143
|
0
|
|
|
|
|
0
|
$o->{ui}->p('The signer is not known. To verify the signature of a public or private envelope, you need to indicate the account on which it was found:'); |
6144
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold(' cds show envelope ', $o->{hash}->hex, ' from ', $o->{ui}->underlined('ACTOR'), ' on ', $o->{actor}->storeReference($o->{store}))); |
6145
|
|
|
|
|
|
|
} |
6146
|
|
|
|
|
|
|
} |
6147
|
|
|
|
|
|
|
|
6148
|
|
|
|
|
|
|
sub processMessageEnvelope { |
6149
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6150
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6151
|
|
|
|
|
|
|
|
6152
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6153
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Message envelope'); |
6154
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds show record ', $o->{hash}->hex, ' on ', $o->{actor}->storeReference($o->{store}))); |
6155
|
|
|
|
|
|
|
|
6156
|
|
|
|
|
|
|
# Decrypt |
6157
|
0
|
|
|
|
|
0
|
my $encryptedContentBytes = $envelope->child('content')->bytesValue; |
6158
|
0
|
|
|
|
|
0
|
my $aesKey = $o->decryptAesKey($envelope); |
6159
|
0
|
0
|
|
|
|
0
|
if (! $aesKey) { |
6160
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6161
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Encrypted content'); |
6162
|
0
|
|
|
|
|
0
|
$o->{ui}->line(length $encryptedContentBytes, ' bytes'); |
6163
|
0
|
|
|
|
|
0
|
return $o->processMessageEnvelope2($envelope); |
6164
|
|
|
|
|
|
|
} |
6165
|
|
|
|
|
|
|
|
6166
|
0
|
|
|
|
|
0
|
my $contentObject = CDS::Object->fromBytes(CDS::C::aesCrypt($encryptedContentBytes, $aesKey, CDS->zeroCTR)); |
6167
|
0
|
0
|
|
|
|
0
|
if (! $contentObject) { |
6168
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('The embedded content object is invalid, or the AES key (', unpack('H*', $aesKey), ') is wrong.'); |
6169
|
0
|
|
|
|
|
0
|
return $o->processMessageEnvelope2($envelope); |
6170
|
|
|
|
|
|
|
} |
6171
|
|
|
|
|
|
|
|
6172
|
|
|
|
|
|
|
#my $signedHash = $contentObject->calculateHash; # before 2020-05-05 |
6173
|
0
|
|
|
|
|
0
|
my $signedHash = CDS::Hash->calculateFor($encryptedContentBytes); |
6174
|
0
|
|
|
|
|
0
|
my $content = CDS::Record->fromObject($contentObject); |
6175
|
0
|
0
|
|
|
|
0
|
if (! $content) { |
6176
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('The embedded content object does not contain a record, or the AES key (', unpack('H*', $aesKey), ') is wrong.'); |
6177
|
0
|
|
|
|
|
0
|
return $o->processMessageEnvelope2($envelope); |
6178
|
|
|
|
|
|
|
} |
6179
|
|
|
|
|
|
|
|
6180
|
|
|
|
|
|
|
# Sender hash |
6181
|
0
|
|
|
|
|
0
|
my $senderHash = $content->child('sender')->hashValue; |
6182
|
0
|
0
|
|
|
|
0
|
$o->{ui}->pRed('The content object is missing the sender.') if ! $senderHash; |
6183
|
|
|
|
|
|
|
|
6184
|
|
|
|
|
|
|
# Sender store |
6185
|
0
|
|
|
|
|
0
|
my $senderStoreRecord = $content->child('store'); |
6186
|
0
|
|
|
|
|
0
|
my $senderStoreBytes = $senderStoreRecord->bytesValue; |
6187
|
0
|
|
|
|
|
0
|
my $mentionsSenderStore = length $senderStoreBytes; |
6188
|
0
|
0
|
|
|
|
0
|
$o->{ui}->pRed('The content object is missing the sender\'s store.') if ! $mentionsSenderStore; |
6189
|
0
|
0
|
|
|
|
0
|
my $senderStore = scalar $mentionsSenderStore ? $o->{actor}->storeForUrl($senderStoreRecord->textValue) : undef; |
6190
|
|
|
|
|
|
|
|
6191
|
|
|
|
|
|
|
# Sender |
6192
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6193
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Signed by'); |
6194
|
0
|
0
|
0
|
|
|
0
|
if ($senderHash && $senderStore) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
6195
|
0
|
|
|
|
|
0
|
my $senderToken = CDS::AccountToken->new($senderStore, $senderHash); |
6196
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{actor}->blueAccountReference($senderToken)); |
6197
|
0
|
|
|
|
|
0
|
$o->showSignature($envelope, $senderHash, $senderStore, $signedHash); |
6198
|
|
|
|
|
|
|
} elsif ($senderHash) { |
6199
|
0
|
|
0
|
|
|
0
|
my $actorLabel = $o->{actor}->actorLabel($senderHash) // $senderHash->hex; |
6200
|
0
|
0
|
|
|
|
0
|
if ($mentionsSenderStore) { |
6201
|
0
|
|
|
|
|
0
|
$o->{ui}->line($actorLabel, ' on ', $o->{ui}->red($o->{ui}->niceBytes($senderStoreBytes, 64))); |
6202
|
|
|
|
|
|
|
} else { |
6203
|
0
|
|
|
|
|
0
|
$o->{ui}->line($actorLabel); |
6204
|
|
|
|
|
|
|
} |
6205
|
0
|
|
|
|
|
0
|
$o->{ui}->pOrange('The signature cannot be verified, because the signer\'s store is not known.'); |
6206
|
|
|
|
|
|
|
} elsif ($senderStore) { |
6207
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->red('?'), ' on ', $o->{actor}->storeReference($senderStore)); |
6208
|
0
|
|
|
|
|
0
|
$o->{ui}->pOrange('The signature cannot be verified, because the signer is not known.'); |
6209
|
|
|
|
|
|
|
} elsif ($mentionsSenderStore) { |
6210
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->red('?'), ' on ', $o->{ui}->red($o->{ui}->niceBytes($senderStoreBytes, 64))); |
6211
|
0
|
|
|
|
|
0
|
$o->{ui}->pOrange('The signature cannot be verified, because the signer is not known.'); |
6212
|
|
|
|
|
|
|
} else { |
6213
|
0
|
|
|
|
|
0
|
$o->{ui}->pOrange('The signature cannot be verified, because the signer is not known.'); |
6214
|
|
|
|
|
|
|
} |
6215
|
|
|
|
|
|
|
|
6216
|
|
|
|
|
|
|
# Content |
6217
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6218
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Content'); |
6219
|
0
|
0
|
|
|
|
0
|
$o->{ui}->recordChildren($content, $senderStore ? $o->{actor}->storeReference($senderStore) : undef); |
6220
|
|
|
|
|
|
|
|
6221
|
0
|
|
|
|
|
0
|
return $o->processMessageEnvelope2($envelope); |
6222
|
|
|
|
|
|
|
} |
6223
|
|
|
|
|
|
|
|
6224
|
|
|
|
|
|
|
sub processMessageEnvelope2 { |
6225
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6226
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6227
|
|
|
|
|
|
|
|
6228
|
|
|
|
|
|
|
# Encrypted for |
6229
|
0
|
|
|
|
|
0
|
$o->showEncryptedFor($envelope); |
6230
|
|
|
|
|
|
|
|
6231
|
|
|
|
|
|
|
# Updated by |
6232
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6233
|
0
|
|
|
|
|
0
|
$o->{ui}->title('May be removed or updated by'); |
6234
|
|
|
|
|
|
|
|
6235
|
0
|
|
|
|
|
0
|
for my $child ($envelope->child('updated by')->children) { |
6236
|
0
|
|
|
|
|
0
|
$o->showActorHash24($child->bytes); |
6237
|
|
|
|
|
|
|
} |
6238
|
|
|
|
|
|
|
|
6239
|
|
|
|
|
|
|
# Expires |
6240
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6241
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Expires'); |
6242
|
0
|
|
|
|
|
0
|
my $expires = $envelope->child('expires')->integerValue; |
6243
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($expires ? $o->{ui}->niceDateTime($expires) : $o->{ui}->gray('never')); |
6244
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6245
|
|
|
|
|
|
|
} |
6246
|
|
|
|
|
|
|
|
6247
|
|
|
|
|
|
|
sub processStreamHead { |
6248
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6249
|
0
|
|
|
|
|
0
|
my $head = shift; |
6250
|
|
|
|
|
|
|
|
6251
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6252
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Stream head'); |
6253
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->pRed('The envelope does not mention a stream head.') if ! $head; |
6254
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds open envelope ', $head->hex, ' on ', $o->{actor}->storeReference($o->{store}))); |
6255
|
|
|
|
|
|
|
|
6256
|
|
|
|
|
|
|
# Get the envelope |
6257
|
0
|
|
0
|
|
|
0
|
my $envelope = $o->{actor}->uiGetRecord($head, $o->{store}, $o->{keyPairToken}) // return; |
6258
|
|
|
|
|
|
|
|
6259
|
|
|
|
|
|
|
# Decrypt the content |
6260
|
0
|
|
|
|
|
0
|
my $encryptedContentBytes = $envelope->child('content')->bytesValue; |
6261
|
0
|
|
0
|
|
|
0
|
my $aesKey = $o->decryptAesKey($envelope) // return; |
6262
|
0
|
|
0
|
|
|
0
|
my $contentObject = CDS::Object->fromBytes(CDS::C::aesCrypt($encryptedContentBytes, $aesKey, CDS->zeroCTR)) // return {aesKey => $aesKey}; |
6263
|
0
|
|
|
|
|
0
|
my $signedHash = CDS::Hash->calculateFor($encryptedContentBytes); |
6264
|
0
|
|
0
|
|
|
0
|
my $content = CDS::Record->fromObject($contentObject) // return {aesKey => $aesKey}; |
6265
|
|
|
|
|
|
|
|
6266
|
|
|
|
|
|
|
# Sender |
6267
|
0
|
|
|
|
|
0
|
my $senderHash = $content->child('sender')->hashValue; |
6268
|
0
|
|
|
|
|
0
|
my $senderStoreRecord = $content->child('store'); |
6269
|
0
|
|
|
|
|
0
|
my $senderStore = $o->{actor}->storeForUrl($senderStoreRecord->textValue); |
6270
|
0
|
0
|
0
|
|
|
0
|
return {aesKey => $aesKey, senderHash => $senderHash, senderStore => $senderStore} if ! $senderHash || ! $senderStore; |
6271
|
|
|
|
|
|
|
|
6272
|
0
|
|
|
|
|
0
|
$o->{ui}->pushIndent; |
6273
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6274
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Signed by'); |
6275
|
0
|
|
|
|
|
0
|
my $senderToken = CDS::AccountToken->new($senderStore, $senderHash); |
6276
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{actor}->blueAccountReference($senderToken)); |
6277
|
0
|
|
|
|
|
0
|
$o->showSignature($envelope, $senderHash, $senderStore, $signedHash); |
6278
|
|
|
|
|
|
|
|
6279
|
|
|
|
|
|
|
# Recipients |
6280
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6281
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Encrypted for'); |
6282
|
0
|
|
|
|
|
0
|
for my $child ($envelope->child('encrypted for')->children) { |
6283
|
0
|
|
|
|
|
0
|
$o->showActorHash24($child->bytes); |
6284
|
|
|
|
|
|
|
} |
6285
|
|
|
|
|
|
|
|
6286
|
0
|
|
|
|
|
0
|
$o->{ui}->popIndent; |
6287
|
0
|
|
|
|
|
0
|
return {aesKey => $aesKey, senderHash => $senderHash, senderStore => $senderStore, isValid => 1}; |
6288
|
|
|
|
|
|
|
} |
6289
|
|
|
|
|
|
|
|
6290
|
|
|
|
|
|
|
sub processStreamEnvelope { |
6291
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6292
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6293
|
|
|
|
|
|
|
|
6294
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6295
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Stream envelope'); |
6296
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds show record ', $o->{hash}->hex, ' on ', $o->{actor}->storeReference($o->{store}))); |
6297
|
|
|
|
|
|
|
|
6298
|
|
|
|
|
|
|
# Get the head |
6299
|
0
|
|
|
|
|
0
|
my $streamHead = $o->processStreamHead($envelope->child('head')->hashValue); |
6300
|
0
|
0
|
0
|
|
|
0
|
$o->{ui}->pRed('The stream head cannot be opened. Open the stream head envelope for details.') if ! $streamHead || ! $streamHead->{isValid}; |
6301
|
|
|
|
|
|
|
|
6302
|
|
|
|
|
|
|
# Get the content |
6303
|
0
|
|
|
|
|
0
|
my $encryptedBytes = $envelope->child('content')->bytesValue; |
6304
|
|
|
|
|
|
|
|
6305
|
|
|
|
|
|
|
# Get the CTR |
6306
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6307
|
0
|
|
|
|
|
0
|
$o->{ui}->title('CTR'); |
6308
|
0
|
|
|
|
|
0
|
my $ctr = $envelope->child('ctr')->bytesValue; |
6309
|
0
|
0
|
|
|
|
0
|
if (length $ctr == 16) { |
6310
|
0
|
|
|
|
|
0
|
$o->{ui}->line(unpack('H*', $ctr)); |
6311
|
|
|
|
|
|
|
} else { |
6312
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('The CTR value is invalid.'); |
6313
|
|
|
|
|
|
|
} |
6314
|
|
|
|
|
|
|
|
6315
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->space if ! $streamHead; |
6316
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->space if ! $streamHead->{aesKey}; |
6317
|
|
|
|
|
|
|
|
6318
|
|
|
|
|
|
|
# Get and verify the MAC |
6319
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6320
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Message authentication (MAC)'); |
6321
|
0
|
|
|
|
|
0
|
my $mac = $envelope->child('mac')->bytesValue; |
6322
|
0
|
|
|
|
|
0
|
my $signedHash = CDS::Hash->calculateFor($encryptedBytes); |
6323
|
0
|
|
|
|
|
0
|
my $expectedMac = CDS::C::aesCrypt($signedHash->bytes, $streamHead->{aesKey}, $ctr); |
6324
|
0
|
0
|
|
|
|
0
|
if ($mac eq $expectedMac) { |
6325
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('The MAC valid.'); |
6326
|
|
|
|
|
|
|
} else { |
6327
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('The MAC is invalid.'); |
6328
|
|
|
|
|
|
|
} |
6329
|
|
|
|
|
|
|
|
6330
|
|
|
|
|
|
|
# Decrypt the content |
6331
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6332
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Content'); |
6333
|
0
|
|
|
|
|
0
|
my $contentObject = CDS::Object->fromBytes(CDS::C::aesCrypt($encryptedBytes, $streamHead->{aesKey}, CDS::C::counterPlusInt($ctr, 2))); |
6334
|
0
|
0
|
|
|
|
0
|
if (! $contentObject) { |
6335
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('The embedded content object is invalid, or the provided AES key (', unpack('H*', $streamHead->{aesKey}), ') is wrong.') ; |
6336
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6337
|
0
|
|
|
|
|
0
|
return; |
6338
|
|
|
|
|
|
|
} |
6339
|
|
|
|
|
|
|
|
6340
|
0
|
|
|
|
|
0
|
my $content = CDS::Record->fromObject($contentObject); |
6341
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->pRed('The content is not a record.') if ! $content; |
6342
|
0
|
0
|
|
|
|
0
|
$o->{ui}->recordChildren($content, $streamHead->{senderStore} ? $o->{actor}->storeReference($streamHead->{senderStore}) : undef); |
6343
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6344
|
|
|
|
|
|
|
|
6345
|
|
|
|
|
|
|
# The envelope is valid |
6346
|
|
|
|
|
|
|
#my $source = CDS::Source->new($o->{pool}->{keyPair}, $o->{actorOnStore}, 'messages', $entry->{hash}); |
6347
|
|
|
|
|
|
|
#return CDS::ReceivedMessage->new($o, $entry, $source, $envelope, $streamHead->senderStoreUrl, $streamHead->sender, $content, $streamHead); |
6348
|
|
|
|
|
|
|
|
6349
|
|
|
|
|
|
|
} |
6350
|
|
|
|
|
|
|
|
6351
|
|
|
|
|
|
|
sub showActorHash24 { |
6352
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6353
|
0
|
|
|
|
|
0
|
my $actorHashBytes = shift; |
6354
|
|
|
|
|
|
|
|
6355
|
0
|
|
|
|
|
0
|
my $actorHashHex = unpack('H*', $actorHashBytes); |
6356
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->line($o->{ui}->red($actorHashHex, ' (', length $actorHashBytes, ' instead of 24 bytes)')) if length $actorHashBytes != 24; |
6357
|
|
|
|
|
|
|
|
6358
|
0
|
|
|
|
|
0
|
my $actorName = $o->{actor}->actorLabelByHashStartBytes($actorHashBytes); |
6359
|
0
|
|
|
|
|
0
|
$actorHashHex .= '·' x 16; |
6360
|
|
|
|
|
|
|
|
6361
|
0
|
|
|
|
|
0
|
my $keyPairHashBytes = $o->{keyPairToken}->keyPair->publicKey->hash->bytes; |
6362
|
0
|
|
|
|
|
0
|
my $isMe = substr($keyPairHashBytes, 0, 24) eq $actorHashBytes; |
6363
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($isMe ? $o->{ui}->violet($actorHashHex) : $actorHashHex, (defined $actorName ? $o->{ui}->blue(' '.$actorName) : '')); |
|
|
0
|
|
|
|
|
|
6364
|
0
|
|
|
|
|
0
|
return $isMe; |
6365
|
|
|
|
|
|
|
} |
6366
|
|
|
|
|
|
|
|
6367
|
|
|
|
|
|
|
sub showSignature { |
6368
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6369
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6370
|
0
|
0
|
0
|
|
|
0
|
my $senderHash = shift; die 'wrong type '.ref($senderHash).' for $senderHash' if defined $senderHash && ref $senderHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
6371
|
0
|
|
|
|
|
0
|
my $senderStore = shift; |
6372
|
0
|
0
|
0
|
|
|
0
|
my $signedHash = shift; die 'wrong type '.ref($signedHash).' for $signedHash' if defined $signedHash && ref $signedHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
6373
|
|
|
|
|
|
|
|
6374
|
|
|
|
|
|
|
# Get the public key |
6375
|
0
|
|
|
|
|
0
|
my $publicKey = $o->getPublicKey($senderHash, $senderStore); |
6376
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->line($o->{ui}->orange('The signature cannot be verified, because the signer\'s public key is not available.')) if ! $publicKey; |
6377
|
|
|
|
|
|
|
|
6378
|
|
|
|
|
|
|
# Verify the signature |
6379
|
0
|
0
|
|
|
|
0
|
if (CDS->verifyEnvelopeSignature($envelope, $publicKey, $signedHash)) { |
6380
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('The signature is valid.'); |
6381
|
|
|
|
|
|
|
} else { |
6382
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('The signature is not valid.'); |
6383
|
|
|
|
|
|
|
} |
6384
|
|
|
|
|
|
|
} |
6385
|
|
|
|
|
|
|
|
6386
|
|
|
|
|
|
|
sub getPublicKey { |
6387
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6388
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
6389
|
0
|
|
|
|
|
0
|
my $store = shift; |
6390
|
|
|
|
|
|
|
|
6391
|
0
|
0
|
|
|
|
0
|
return $o->{keyPairToken}->keyPair->publicKey if $hash->equals($o->{keyPairToken}->keyPair->publicKey->hash); |
6392
|
0
|
|
|
|
|
0
|
return $o->{actor}->uiGetPublicKey($hash, $store, $o->{keyPairToken}); |
6393
|
|
|
|
|
|
|
} |
6394
|
|
|
|
|
|
|
|
6395
|
|
|
|
|
|
|
sub showEncryptedFor { |
6396
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6397
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6398
|
|
|
|
|
|
|
|
6399
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6400
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Encrypted for'); |
6401
|
|
|
|
|
|
|
|
6402
|
0
|
|
|
|
|
0
|
my $canDecrypt = 0; |
6403
|
0
|
|
|
|
|
0
|
for my $child ($envelope->child('encrypted for')->children) { |
6404
|
0
|
0
|
|
|
|
0
|
$canDecrypt = 1 if $o->showActorHash24($child->bytes); |
6405
|
|
|
|
|
|
|
} |
6406
|
|
|
|
|
|
|
|
6407
|
0
|
0
|
|
|
|
0
|
return if $canDecrypt; |
6408
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6409
|
0
|
|
|
|
|
0
|
my $keyPairHash = $o->{keyPairToken}->keyPair->publicKey->hash; |
6410
|
0
|
|
|
|
|
0
|
$o->{ui}->pOrange('This envelope is not encrypted for you (', $keyPairHash->shortHex, '). If you possess one of the keypairs mentioned above, add "… using KEYPAIR" to open this envelope.'); |
6411
|
|
|
|
|
|
|
} |
6412
|
|
|
|
|
|
|
|
6413
|
|
|
|
|
|
|
sub decryptAesKey { |
6414
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6415
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6416
|
|
|
|
|
|
|
|
6417
|
0
|
|
|
|
|
0
|
my $keyPair = $o->{keyPairToken}->keyPair; |
6418
|
0
|
|
|
|
|
0
|
my $hashBytes24 = substr($keyPair->publicKey->hash->bytes, 0, 24); |
6419
|
0
|
|
|
|
|
0
|
my $child = $envelope->child('encrypted for')->child($hashBytes24); |
6420
|
|
|
|
|
|
|
|
6421
|
0
|
|
|
|
|
0
|
my $encryptedAesKey = $child->bytesValue; |
6422
|
0
|
0
|
|
|
|
0
|
return if ! length $encryptedAesKey; |
6423
|
|
|
|
|
|
|
|
6424
|
0
|
|
|
|
|
0
|
my $aesKey = $keyPair->decrypt($encryptedAesKey); |
6425
|
0
|
0
|
0
|
|
|
0
|
return $aesKey if defined $aesKey && length $aesKey == 32; |
6426
|
|
|
|
|
|
|
|
6427
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('The AES key failed to decrypt. It either wasn\'t encrypted properly, or the encryption was performed with the wrong public key.'); |
6428
|
0
|
|
|
|
|
0
|
return; |
6429
|
|
|
|
|
|
|
} |
6430
|
|
|
|
|
|
|
|
6431
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
6432
|
|
|
|
|
|
|
package CDS::Commands::Put; |
6433
|
|
|
|
|
|
|
|
6434
|
|
|
|
|
|
|
sub register { |
6435
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
6436
|
0
|
|
|
|
|
0
|
my $cds = shift; |
6437
|
0
|
|
|
|
|
0
|
my $help = shift; |
6438
|
|
|
|
|
|
|
|
6439
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
6440
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
6441
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
6442
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
6443
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
6444
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
6445
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
6446
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
6447
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
6448
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
6449
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
6450
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
6451
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(1); |
6452
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
6453
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(0); |
6454
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(0); |
6455
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(0); |
6456
|
0
|
|
|
|
|
0
|
my $node017 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&put}); |
6457
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 1, 0, 'put'); |
6458
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'put'); |
6459
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'put'); |
6460
|
0
|
|
|
|
|
0
|
$help->addArrow($node007, 1, 0, 'put'); |
6461
|
0
|
|
|
|
|
0
|
$node000->addArrow($node012, 1, 0, 'OBJECTFILE', \&collectObjectfile); |
6462
|
0
|
|
|
|
|
0
|
$node001->addArrow($node003, 1, 0, 'object'); |
6463
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 1, 0, 'public'); |
6464
|
0
|
|
|
|
|
0
|
$node003->addArrow($node008, 1, 0, 'with'); |
6465
|
0
|
|
|
|
|
0
|
$node004->addArrow($node005, 1, 0, 'key'); |
6466
|
0
|
|
|
|
|
0
|
$node005->addArrow($node006, 1, 0, 'of'); |
6467
|
0
|
|
|
|
|
0
|
$node006->addArrow($node012, 1, 0, 'KEYPAIR', \&collectKeypair); |
6468
|
0
|
|
|
|
|
0
|
$node008->addDefault($node009); |
6469
|
0
|
|
|
|
|
0
|
$node008->addDefault($node011); |
6470
|
0
|
|
|
|
|
0
|
$node009->addArrow($node009, 1, 0, 'HASH', \&collectHash); |
6471
|
0
|
|
|
|
|
0
|
$node009->addArrow($node010, 1, 0, 'HASH', \&collectHash); |
6472
|
0
|
|
|
|
|
0
|
$node010->addArrow($node011, 1, 0, 'and'); |
6473
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'FILE', \&collectFile); |
6474
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'encrypted'); |
6475
|
0
|
|
|
|
|
0
|
$node012->addDefault($node015); |
6476
|
0
|
|
|
|
|
0
|
$node013->addArrow($node014, 1, 0, 'with'); |
6477
|
0
|
|
|
|
|
0
|
$node014->addArrow($node015, 1, 0, 'AESKEY', \&collectAeskey); |
6478
|
0
|
|
|
|
|
0
|
$node015->addArrow($node016, 1, 0, 'onto'); |
6479
|
0
|
|
|
|
|
0
|
$node015->addDefault($node017); |
6480
|
0
|
|
|
|
|
0
|
$node016->addArrow($node016, 1, 0, 'STORE', \&collectStore); |
6481
|
0
|
|
|
|
|
0
|
$node016->addArrow($node017, 1, 0, 'STORE', \&collectStore); |
6482
|
|
|
|
|
|
|
} |
6483
|
|
|
|
|
|
|
|
6484
|
|
|
|
|
|
|
sub collectAeskey { |
6485
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6486
|
0
|
|
|
|
|
0
|
my $label = shift; |
6487
|
0
|
|
|
|
|
0
|
my $value = shift; |
6488
|
|
|
|
|
|
|
|
6489
|
0
|
|
|
|
|
0
|
$o->{aesKey} = $value; |
6490
|
|
|
|
|
|
|
} |
6491
|
|
|
|
|
|
|
|
6492
|
|
|
|
|
|
|
sub collectFile { |
6493
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6494
|
0
|
|
|
|
|
0
|
my $label = shift; |
6495
|
0
|
|
|
|
|
0
|
my $value = shift; |
6496
|
|
|
|
|
|
|
|
6497
|
0
|
|
|
|
|
0
|
$o->{dataFile} = $value; |
6498
|
|
|
|
|
|
|
} |
6499
|
|
|
|
|
|
|
|
6500
|
|
|
|
|
|
|
sub collectHash { |
6501
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6502
|
0
|
|
|
|
|
0
|
my $label = shift; |
6503
|
0
|
|
|
|
|
0
|
my $value = shift; |
6504
|
|
|
|
|
|
|
|
6505
|
0
|
|
|
|
|
0
|
push @{$o->{hashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
6506
|
|
|
|
|
|
|
} |
6507
|
|
|
|
|
|
|
|
6508
|
|
|
|
|
|
|
sub collectKeypair { |
6509
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6510
|
0
|
|
|
|
|
0
|
my $label = shift; |
6511
|
0
|
|
|
|
|
0
|
my $value = shift; |
6512
|
|
|
|
|
|
|
|
6513
|
0
|
|
|
|
|
0
|
$o->{object} = $value->keyPair->publicKey->object; |
6514
|
|
|
|
|
|
|
} |
6515
|
|
|
|
|
|
|
|
6516
|
|
|
|
|
|
|
sub collectObjectfile { |
6517
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6518
|
0
|
|
|
|
|
0
|
my $label = shift; |
6519
|
0
|
|
|
|
|
0
|
my $value = shift; |
6520
|
|
|
|
|
|
|
|
6521
|
0
|
|
|
|
|
0
|
$o->{objectFile} = $value; |
6522
|
|
|
|
|
|
|
} |
6523
|
|
|
|
|
|
|
|
6524
|
|
|
|
|
|
|
sub collectStore { |
6525
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6526
|
0
|
|
|
|
|
0
|
my $label = shift; |
6527
|
0
|
|
|
|
|
0
|
my $value = shift; |
6528
|
|
|
|
|
|
|
|
6529
|
0
|
|
|
|
|
0
|
push @{$o->{stores}}, $value; |
|
0
|
|
|
|
|
0
|
|
6530
|
|
|
|
|
|
|
} |
6531
|
|
|
|
|
|
|
|
6532
|
|
|
|
|
|
|
sub new { |
6533
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
6534
|
0
|
|
|
|
|
0
|
my $actor = shift; |
6535
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
6536
|
|
|
|
|
|
|
|
6537
|
|
|
|
|
|
|
# END AUTOGENERATED |
6538
|
|
|
|
|
|
|
|
6539
|
|
|
|
|
|
|
# HTML FOLDER NAME store-put |
6540
|
|
|
|
|
|
|
# HTML TITLE Put |
6541
|
|
|
|
|
|
|
sub help { |
6542
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6543
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
6544
|
|
|
|
|
|
|
|
6545
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
6546
|
0
|
|
|
|
|
0
|
$ui->space; |
6547
|
0
|
|
|
|
|
0
|
$ui->command('cds put FILE* [onto STORE*]'); |
6548
|
0
|
|
|
|
|
0
|
$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.'); |
6549
|
0
|
|
|
|
|
0
|
$ui->space; |
6550
|
0
|
|
|
|
|
0
|
$ui->command('cds put FILE encrypted with AESKEY [onto STORE*]'); |
6551
|
0
|
|
|
|
|
0
|
$ui->p('Encrypts the object before the upload.'); |
6552
|
0
|
|
|
|
|
0
|
$ui->space; |
6553
|
0
|
|
|
|
|
0
|
$ui->command('cds put object with [HASH* and] FILE …'); |
6554
|
0
|
|
|
|
|
0
|
$ui->p('Creates an object with the HASHes as hash list and FILE as data.'); |
6555
|
0
|
|
|
|
|
0
|
$ui->space; |
6556
|
0
|
|
|
|
|
0
|
$ui->command('cds put public key of KEYPAIR …'); |
6557
|
0
|
|
|
|
|
0
|
$ui->p('Uploads the public key of the indicated key pair onto the store.'); |
6558
|
0
|
|
|
|
|
0
|
$ui->space; |
6559
|
|
|
|
|
|
|
} |
6560
|
|
|
|
|
|
|
|
6561
|
|
|
|
|
|
|
sub put { |
6562
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6563
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
6564
|
|
|
|
|
|
|
|
6565
|
0
|
|
|
|
|
0
|
$o->{hashes} = []; |
6566
|
0
|
|
|
|
|
0
|
$o->{stores} = []; |
6567
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
6568
|
|
|
|
|
|
|
|
6569
|
|
|
|
|
|
|
# Stores |
6570
|
0
|
0
|
|
|
|
0
|
push @{$o->{stores}}, $o->{actor}->preferredStore if ! scalar @{$o->{stores}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
6571
|
|
|
|
|
|
|
|
6572
|
0
|
|
|
|
|
0
|
$o->{get} = []; |
6573
|
0
|
0
|
|
|
|
0
|
return $o->putObject($o->{object}) if $o->{object}; |
6574
|
0
|
0
|
|
|
|
0
|
return $o->putObjectFile if $o->{objectFile}; |
6575
|
0
|
|
|
|
|
0
|
$o->putConstructedFile; |
6576
|
|
|
|
|
|
|
} |
6577
|
|
|
|
|
|
|
|
6578
|
|
|
|
|
|
|
sub putObjectFile { |
6579
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6580
|
|
|
|
|
|
|
|
6581
|
0
|
|
|
|
|
0
|
my $object = $o->{objectFile}->object; |
6582
|
|
|
|
|
|
|
|
6583
|
|
|
|
|
|
|
# Display object information |
6584
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6585
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Uploading ', $o->{objectFile}->file, ' ', $o->{ui}->gray($o->{ui}->niceFileSize($object->byteLength))); |
6586
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($object->hashesCount == 1 ? '1 hash' : $object->hashesCount.' hashes'); |
6587
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->niceFileSize(length $object->data).' data'); |
6588
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6589
|
|
|
|
|
|
|
|
6590
|
|
|
|
|
|
|
# Upload |
6591
|
0
|
|
|
|
|
0
|
$o->putObject($object); |
6592
|
|
|
|
|
|
|
} |
6593
|
|
|
|
|
|
|
|
6594
|
|
|
|
|
|
|
sub putConstructedFile { |
6595
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6596
|
|
|
|
|
|
|
|
6597
|
|
|
|
|
|
|
# Create the object |
6598
|
0
|
|
0
|
|
|
0
|
my $data = CDS->readBytesFromFile($o->{dataFile}) // return $o->{ui}->error('Unable to read "', $o->{dataFile}, '".'); |
6599
|
0
|
|
|
|
|
0
|
my $header = pack('L>', scalar @{$o->{hashes}}) . join('', map { $_->bytes } @{$o->{hashes}}); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
6600
|
0
|
|
|
|
|
0
|
my $object = CDS::Object->create($header, $data); |
6601
|
|
|
|
|
|
|
|
6602
|
|
|
|
|
|
|
# Display object information |
6603
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6604
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Uploading new object ', $o->{ui}->gray($o->{ui}->niceFileSize(length $object->bytes))); |
6605
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($object->hashesCount == 1 ? '1 hash' : $object->hashesCount.' hashes'); |
6606
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->niceFileSize(length $object->data).' data from ', $o->{dataFile}); |
6607
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6608
|
|
|
|
|
|
|
|
6609
|
|
|
|
|
|
|
# Upload |
6610
|
0
|
|
|
|
|
0
|
$o->putObject($object); |
6611
|
|
|
|
|
|
|
} |
6612
|
|
|
|
|
|
|
|
6613
|
|
|
|
|
|
|
sub putObject { |
6614
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6615
|
0
|
0
|
0
|
|
|
0
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
0
|
|
6616
|
|
|
|
|
|
|
|
6617
|
0
|
|
|
|
|
0
|
my $keyPair = $o->{actor}->preferredKeyPairToken->keyPair; |
6618
|
|
|
|
|
|
|
|
6619
|
|
|
|
|
|
|
# Encrypt it if desired |
6620
|
0
|
|
|
|
|
0
|
my $objectBytes; |
6621
|
0
|
0
|
|
|
|
0
|
if (defined $o->{aesKey}) { |
6622
|
0
|
|
|
|
|
0
|
$object = $object->crypt($o->{aesKey}); |
6623
|
0
|
|
|
|
|
0
|
unshift @{$o->{get}}, ' decrypted with ', unpack('H*', $o->{aesKey}), ' '; |
|
0
|
|
|
|
|
0
|
|
6624
|
|
|
|
|
|
|
} |
6625
|
|
|
|
|
|
|
|
6626
|
|
|
|
|
|
|
# Calculate the hash |
6627
|
0
|
|
|
|
|
0
|
my $hash = $object->calculateHash; |
6628
|
|
|
|
|
|
|
|
6629
|
|
|
|
|
|
|
# Upload the object |
6630
|
0
|
|
|
|
|
0
|
my $successfulStore; |
6631
|
0
|
|
|
|
|
0
|
for my $store (@{$o->{stores}}) { |
|
0
|
|
|
|
|
0
|
|
6632
|
0
|
|
|
|
|
0
|
my $error = $store->put($hash, $object, $keyPair); |
6633
|
0
|
0
|
|
|
|
0
|
next if $error; |
6634
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('The object was uploaded onto ', $store->url, '.'); |
6635
|
0
|
|
|
|
|
0
|
$successfulStore = $store; |
6636
|
|
|
|
|
|
|
} |
6637
|
|
|
|
|
|
|
|
6638
|
|
|
|
|
|
|
# Show the corresponding download line |
6639
|
0
|
0
|
|
|
|
0
|
return if ! $successfulStore; |
6640
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6641
|
0
|
|
|
|
|
0
|
$o->{ui}->line('To download the object, type:'); |
6642
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds get ', $hash->hex), $o->{ui}->gray(' on ', $successfulStore->url, @{$o->{get}})); |
|
0
|
|
|
|
|
0
|
|
6643
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6644
|
|
|
|
|
|
|
} |
6645
|
|
|
|
|
|
|
|
6646
|
|
|
|
|
|
|
package CDS::Commands::Remember; |
6647
|
|
|
|
|
|
|
|
6648
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
6649
|
|
|
|
|
|
|
|
6650
|
|
|
|
|
|
|
sub register { |
6651
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
6652
|
0
|
|
|
|
|
0
|
my $cds = shift; |
6653
|
0
|
|
|
|
|
0
|
my $help = shift; |
6654
|
|
|
|
|
|
|
|
6655
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0, {constructor => \&new, function => \&showLabels}); |
6656
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
6657
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
6658
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
6659
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
6660
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
6661
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&forget}); |
6662
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(1); |
6663
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
6664
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&remember}); |
6665
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 1, 0, 'remember'); |
6666
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'forget'); |
6667
|
0
|
|
|
|
|
0
|
$help->addArrow($node003, 1, 0, 'forget'); |
6668
|
0
|
|
|
|
|
0
|
$help->addArrow($node003, 1, 0, 'remember'); |
6669
|
0
|
|
|
|
|
0
|
$node000->addArrow($node004, 1, 0, 'ACTOR', \&collectActor); |
6670
|
0
|
|
|
|
|
0
|
$node000->addArrow($node007, 1, 1, 'ACCOUNT', \&collectAccount); |
6671
|
0
|
|
|
|
|
0
|
$node000->addArrow($node007, 1, 0, 'ACTOR', \&collectActor); |
6672
|
0
|
|
|
|
|
0
|
$node000->addArrow($node007, 1, 0, 'KEYPAIR', \&collectKeypair); |
6673
|
0
|
|
|
|
|
0
|
$node000->addArrow($node007, 1, 0, 'STORE', \&collectStore); |
6674
|
0
|
|
|
|
|
0
|
$node001->addDefault($node002); |
6675
|
0
|
|
|
|
|
0
|
$node002->addArrow($node002, 1, 0, 'LABEL', \&collectLabel); |
6676
|
0
|
|
|
|
|
0
|
$node002->addArrow($node006, 1, 0, 'LABEL', \&collectLabel); |
6677
|
0
|
|
|
|
|
0
|
$node004->addArrow($node005, 1, 0, 'on'); |
6678
|
0
|
|
|
|
|
0
|
$node005->addArrow($node007, 1, 0, 'STORE', \&collectStore); |
6679
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'as'); |
6680
|
0
|
|
|
|
|
0
|
$node008->addArrow($node009, 1, 0, 'TEXT', \&collectText); |
6681
|
|
|
|
|
|
|
} |
6682
|
|
|
|
|
|
|
|
6683
|
|
|
|
|
|
|
sub collectAccount { |
6684
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6685
|
0
|
|
|
|
|
0
|
my $label = shift; |
6686
|
0
|
|
|
|
|
0
|
my $value = shift; |
6687
|
|
|
|
|
|
|
|
6688
|
0
|
|
|
|
|
0
|
$o->{store} = $value->cliStore; |
6689
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value->actorHash; |
6690
|
|
|
|
|
|
|
} |
6691
|
|
|
|
|
|
|
|
6692
|
|
|
|
|
|
|
sub collectActor { |
6693
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6694
|
0
|
|
|
|
|
0
|
my $label = shift; |
6695
|
0
|
|
|
|
|
0
|
my $value = shift; |
6696
|
|
|
|
|
|
|
|
6697
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value; |
6698
|
|
|
|
|
|
|
} |
6699
|
|
|
|
|
|
|
|
6700
|
|
|
|
|
|
|
sub collectKeypair { |
6701
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6702
|
0
|
|
|
|
|
0
|
my $label = shift; |
6703
|
0
|
|
|
|
|
0
|
my $value = shift; |
6704
|
|
|
|
|
|
|
|
6705
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
6706
|
|
|
|
|
|
|
} |
6707
|
|
|
|
|
|
|
|
6708
|
|
|
|
|
|
|
sub collectLabel { |
6709
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6710
|
0
|
|
|
|
|
0
|
my $label = shift; |
6711
|
0
|
|
|
|
|
0
|
my $value = shift; |
6712
|
|
|
|
|
|
|
|
6713
|
0
|
|
|
|
|
0
|
push @{$o->{forget}}, $value; |
|
0
|
|
|
|
|
0
|
|
6714
|
|
|
|
|
|
|
} |
6715
|
|
|
|
|
|
|
|
6716
|
|
|
|
|
|
|
sub collectStore { |
6717
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6718
|
0
|
|
|
|
|
0
|
my $label = shift; |
6719
|
0
|
|
|
|
|
0
|
my $value = shift; |
6720
|
|
|
|
|
|
|
|
6721
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
6722
|
|
|
|
|
|
|
} |
6723
|
|
|
|
|
|
|
|
6724
|
|
|
|
|
|
|
sub collectText { |
6725
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6726
|
0
|
|
|
|
|
0
|
my $label = shift; |
6727
|
0
|
|
|
|
|
0
|
my $value = shift; |
6728
|
|
|
|
|
|
|
|
6729
|
0
|
|
|
|
|
0
|
$o->{label} = $value; |
6730
|
|
|
|
|
|
|
} |
6731
|
|
|
|
|
|
|
|
6732
|
|
|
|
|
|
|
sub new { |
6733
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
6734
|
0
|
|
|
|
|
0
|
my $actor = shift; |
6735
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
6736
|
|
|
|
|
|
|
|
6737
|
|
|
|
|
|
|
# END AUTOGENERATED |
6738
|
|
|
|
|
|
|
|
6739
|
|
|
|
|
|
|
# HTML FOLDER NAME remember |
6740
|
|
|
|
|
|
|
# HTML TITLE Remember |
6741
|
|
|
|
|
|
|
sub help { |
6742
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6743
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
6744
|
|
|
|
|
|
|
|
6745
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
6746
|
0
|
|
|
|
|
0
|
$ui->space; |
6747
|
0
|
|
|
|
|
0
|
$ui->command('cds remember'); |
6748
|
0
|
|
|
|
|
0
|
$ui->p('Shows all remembered values.'); |
6749
|
0
|
|
|
|
|
0
|
$ui->space; |
6750
|
0
|
|
|
|
|
0
|
$ui->command('cds remember ACCOUNT|ACTOR|STORE|KEYPAIR as TEXT'); |
6751
|
0
|
|
|
|
|
0
|
$ui->command('cds remember ACTOR on STORE as TEXT'); |
6752
|
0
|
|
|
|
|
0
|
$ui->p('Remembers the indicated actor hash, account, store, or key pair as TEXT. This information is stored in the global state, and therefore persists until the name is deleted (cds forget …) or redefined (cds remember …).'); |
6753
|
0
|
|
|
|
|
0
|
$ui->space; |
6754
|
0
|
|
|
|
|
0
|
$ui->p('Key pairs are stored as link (absolute path) to the key pair file, and specific to the device.'); |
6755
|
0
|
|
|
|
|
0
|
$ui->space; |
6756
|
0
|
|
|
|
|
0
|
$ui->command('cds forget LABEL'); |
6757
|
0
|
|
|
|
|
0
|
$ui->p('Forgets the corresponding item.'); |
6758
|
0
|
|
|
|
|
0
|
$ui->space; |
6759
|
|
|
|
|
|
|
} |
6760
|
|
|
|
|
|
|
|
6761
|
|
|
|
|
|
|
sub remember { |
6762
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6763
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
6764
|
|
|
|
|
|
|
|
6765
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
6766
|
|
|
|
|
|
|
|
6767
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->new; |
6768
|
0
|
0
|
|
|
|
0
|
$record->add('store')->addText($o->{store}->url) if defined $o->{store}; |
6769
|
0
|
0
|
|
|
|
0
|
$record->add('actor')->add($o->{actorHash}->bytes) if defined $o->{actorHash}; |
6770
|
0
|
0
|
|
|
|
0
|
$record->add('key pair')->addText($o->{keyPairToken}->file) if defined $o->{keyPairToken}; |
6771
|
0
|
|
|
|
|
0
|
$o->{actor}->remember($o->{label}, $record); |
6772
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
6773
|
|
|
|
|
|
|
} |
6774
|
|
|
|
|
|
|
|
6775
|
|
|
|
|
|
|
sub forget { |
6776
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6777
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
6778
|
|
|
|
|
|
|
|
6779
|
0
|
|
|
|
|
0
|
$o->{forget} = []; |
6780
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
6781
|
|
|
|
|
|
|
|
6782
|
0
|
|
|
|
|
0
|
for my $label (@{$o->{forget}}) { |
|
0
|
|
|
|
|
0
|
|
6783
|
0
|
|
|
|
|
0
|
$o->{actor}->groupRoot->child('labels')->child($label)->clear; |
6784
|
|
|
|
|
|
|
} |
6785
|
|
|
|
|
|
|
|
6786
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
6787
|
|
|
|
|
|
|
} |
6788
|
|
|
|
|
|
|
|
6789
|
|
|
|
|
|
|
sub showLabels { |
6790
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6791
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
6792
|
|
|
|
|
|
|
|
6793
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6794
|
0
|
|
|
|
|
0
|
$o->showRememberedValues; |
6795
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
6796
|
|
|
|
|
|
|
} |
6797
|
|
|
|
|
|
|
|
6798
|
|
|
|
|
|
|
sub showRememberedValues { |
6799
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6800
|
|
|
|
|
|
|
|
6801
|
0
|
|
|
|
|
0
|
my $hasLabel = 0; |
6802
|
0
|
|
|
|
|
0
|
for my $child (sort { $a->{id} cmp $b->{id} } $o->{actor}->groupRoot->child('labels')->children) { |
|
0
|
|
|
|
|
0
|
|
6803
|
0
|
|
|
|
|
0
|
my $record = $child->record; |
6804
|
0
|
|
|
|
|
0
|
my $label = $o->{ui}->blue($o->{ui}->left(15, Encode::decode_utf8($child->label))); |
6805
|
|
|
|
|
|
|
|
6806
|
0
|
|
|
|
|
0
|
my $actorHash = CDS::Hash->fromBytes($record->child('actor')->bytesValue); |
6807
|
0
|
|
|
|
|
0
|
my $storeUrl = $record->child('store')->textValue; |
6808
|
0
|
|
|
|
|
0
|
my $keyPairFile = $record->child('key pair')->textValue; |
6809
|
|
|
|
|
|
|
|
6810
|
0
|
0
|
|
|
|
0
|
if (length $keyPairFile) { |
6811
|
0
|
|
|
|
|
0
|
$o->{ui}->line($label, ' ', $o->{ui}->gray('key pair'), ' ', $keyPairFile); |
6812
|
0
|
|
|
|
|
0
|
$hasLabel = 1; |
6813
|
|
|
|
|
|
|
} |
6814
|
|
|
|
|
|
|
|
6815
|
0
|
0
|
0
|
|
|
0
|
if ($actorHash && length $storeUrl) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
6816
|
0
|
|
|
|
|
0
|
my $storeReference = $o->{actor}->blueStoreUrlReference($storeUrl); |
6817
|
0
|
|
|
|
|
0
|
$o->{ui}->line($label, ' ', $o->{ui}->gray('account'), ' ', $actorHash->hex, ' on ', $storeReference); |
6818
|
0
|
|
|
|
|
0
|
$hasLabel = 1; |
6819
|
|
|
|
|
|
|
} elsif ($actorHash) { |
6820
|
0
|
|
|
|
|
0
|
$o->{ui}->line($label, ' ', $o->{ui}->gray('actor'), ' ', $actorHash->hex); |
6821
|
0
|
|
|
|
|
0
|
$hasLabel = 1; |
6822
|
|
|
|
|
|
|
} elsif (length $storeUrl) { |
6823
|
0
|
|
|
|
|
0
|
$o->{ui}->line($label, ' ', $o->{ui}->gray('store'), ' ', $storeUrl); |
6824
|
0
|
|
|
|
|
0
|
$hasLabel = 1; |
6825
|
|
|
|
|
|
|
} |
6826
|
|
|
|
|
|
|
|
6827
|
0
|
|
|
|
|
0
|
$o->showActorGroupLabel($label, $record->child('actor group')); |
6828
|
|
|
|
|
|
|
} |
6829
|
|
|
|
|
|
|
|
6830
|
0
|
0
|
|
|
|
0
|
return if $hasLabel; |
6831
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray('none')); |
6832
|
|
|
|
|
|
|
} |
6833
|
|
|
|
|
|
|
|
6834
|
|
|
|
|
|
|
sub showActorGroupLabel { |
6835
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6836
|
0
|
|
|
|
|
0
|
my $label = shift; |
6837
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
6838
|
|
|
|
|
|
|
|
6839
|
0
|
0
|
|
|
|
0
|
return if ! $record->contains('actor group'); |
6840
|
|
|
|
|
|
|
|
6841
|
0
|
|
|
|
|
0
|
my $builder = CDS::ActorGroupBuilder->new; |
6842
|
0
|
|
|
|
|
0
|
$builder->parse($record, 1); |
6843
|
|
|
|
|
|
|
|
6844
|
0
|
|
|
|
|
0
|
my $countActive = 0; |
6845
|
0
|
|
|
|
|
0
|
my $countIdle = 0; |
6846
|
0
|
|
|
|
|
0
|
my $newestActive = undef; |
6847
|
|
|
|
|
|
|
|
6848
|
0
|
|
|
|
|
0
|
for my $member ($builder->members) { |
6849
|
0
|
|
|
|
|
0
|
my $isActive = $member->status eq 'active'; |
6850
|
0
|
0
|
|
|
|
0
|
$countActive += 1 if $isActive; |
6851
|
0
|
0
|
|
|
|
0
|
$countIdle += 1 if $member->status eq 'idle'; |
6852
|
|
|
|
|
|
|
|
6853
|
0
|
0
|
|
|
|
0
|
next if ! $isActive; |
6854
|
0
|
0
|
0
|
|
|
0
|
next if $newestActive && $member->revision <= $newestActive->revision; |
6855
|
0
|
|
|
|
|
0
|
$newestActive = $member; |
6856
|
|
|
|
|
|
|
} |
6857
|
|
|
|
|
|
|
|
6858
|
0
|
|
|
|
|
0
|
my @line; |
6859
|
0
|
|
|
|
|
0
|
push @line, $label, ' ', $o->{ui}->gray('actor group'), ' '; |
6860
|
0
|
0
|
|
|
|
0
|
push @line, $newestActive->hash->hex, ' on ', $o->{actor}->blueStoreUrlReference($newestActive->storeUrl) if $newestActive; |
6861
|
0
|
0
|
|
|
|
0
|
push @line, $o->{ui}->gray('(no active actor)') if ! $newestActive; |
6862
|
0
|
|
|
|
|
0
|
push @line, $o->{ui}->green(' ', $countActive, ' active'); |
6863
|
0
|
|
|
|
|
0
|
my $discovered = $record->child('discovered')->integerValue; |
6864
|
0
|
0
|
|
|
|
0
|
push @line, $o->{ui}->gray(' ', $o->{ui}->niceDateTimeLocal($discovered)) if $discovered; |
6865
|
0
|
|
|
|
|
0
|
$o->{ui}->line(@line); |
6866
|
|
|
|
|
|
|
} |
6867
|
|
|
|
|
|
|
|
6868
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
6869
|
|
|
|
|
|
|
package CDS::Commands::Select; |
6870
|
|
|
|
|
|
|
|
6871
|
|
|
|
|
|
|
sub register { |
6872
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
6873
|
0
|
|
|
|
|
0
|
my $cds = shift; |
6874
|
0
|
|
|
|
|
0
|
my $help = shift; |
6875
|
|
|
|
|
|
|
|
6876
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
6877
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
6878
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
6879
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
6880
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
6881
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
6882
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
6883
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
6884
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
6885
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
6886
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
6887
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
6888
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
6889
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
6890
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(0); |
6891
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(0); |
6892
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
6893
|
0
|
|
|
|
|
0
|
my $node017 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showSelectionCmd}); |
6894
|
0
|
|
|
|
|
0
|
my $node018 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&unselectKeyPair}); |
6895
|
0
|
|
|
|
|
0
|
my $node019 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&unselectStore}); |
6896
|
0
|
|
|
|
|
0
|
my $node020 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&unselectActor}); |
6897
|
0
|
|
|
|
|
0
|
my $node021 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&unselectAll}); |
6898
|
0
|
|
|
|
|
0
|
my $node022 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&select}); |
6899
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 1, 0, 'select'); |
6900
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'select'); |
6901
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'select'); |
6902
|
0
|
|
|
|
|
0
|
$cds->addArrow($node003, 1, 0, 'select'); |
6903
|
0
|
|
|
|
|
0
|
$cds->addArrow($node004, 1, 0, 'select'); |
6904
|
0
|
|
|
|
|
0
|
$cds->addArrow($node005, 1, 0, 'select'); |
6905
|
0
|
|
|
|
|
0
|
$cds->addArrow($node006, 1, 0, 'select'); |
6906
|
0
|
|
|
|
|
0
|
$cds->addArrow($node009, 1, 0, 'unselect'); |
6907
|
0
|
|
|
|
|
0
|
$cds->addArrow($node010, 1, 0, 'unselect'); |
6908
|
0
|
|
|
|
|
0
|
$cds->addArrow($node011, 1, 0, 'unselect'); |
6909
|
0
|
|
|
|
|
0
|
$cds->addArrow($node012, 1, 0, 'unselect'); |
6910
|
0
|
|
|
|
|
0
|
$cds->addArrow($node017, 1, 0, 'select'); |
6911
|
0
|
|
|
|
|
0
|
$help->addArrow($node016, 1, 0, 'select'); |
6912
|
0
|
|
|
|
|
0
|
$node000->addArrow($node022, 1, 0, 'KEYPAIR', \&collectKeypair); |
6913
|
0
|
|
|
|
|
0
|
$node001->addArrow($node022, 1, 0, 'STORE', \&collectStore); |
6914
|
0
|
|
|
|
|
0
|
$node002->addArrow($node014, 1, 0, 'ACTOR', \&collectActor); |
6915
|
0
|
|
|
|
|
0
|
$node003->addArrow($node007, 1, 0, 'storage'); |
6916
|
0
|
|
|
|
|
0
|
$node004->addArrow($node008, 1, 0, 'messaging'); |
6917
|
0
|
|
|
|
|
0
|
$node005->addArrow($node022, 1, 0, 'ACTOR', \&collectActor); |
6918
|
0
|
|
|
|
|
0
|
$node006->addArrow($node022, 1, 1, 'ACCOUNT', \&collectAccount); |
6919
|
0
|
|
|
|
|
0
|
$node007->addArrow($node022, 1, 0, 'store', \&collectStore1); |
6920
|
0
|
|
|
|
|
0
|
$node008->addArrow($node022, 1, 0, 'store', \&collectStore2); |
6921
|
0
|
|
|
|
|
0
|
$node009->addArrow($node013, 1, 0, 'key'); |
6922
|
0
|
|
|
|
|
0
|
$node010->addArrow($node019, 1, 0, 'store'); |
6923
|
0
|
|
|
|
|
0
|
$node011->addArrow($node020, 1, 0, 'actor'); |
6924
|
0
|
|
|
|
|
0
|
$node012->addArrow($node021, 1, 0, 'all'); |
6925
|
0
|
|
|
|
|
0
|
$node013->addArrow($node018, 1, 0, 'pair'); |
6926
|
0
|
|
|
|
|
0
|
$node014->addArrow($node015, 1, 0, 'on'); |
6927
|
0
|
|
|
|
|
0
|
$node015->addArrow($node022, 1, 0, 'STORE', \&collectStore); |
6928
|
|
|
|
|
|
|
} |
6929
|
|
|
|
|
|
|
|
6930
|
|
|
|
|
|
|
sub collectAccount { |
6931
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6932
|
0
|
|
|
|
|
0
|
my $label = shift; |
6933
|
0
|
|
|
|
|
0
|
my $value = shift; |
6934
|
|
|
|
|
|
|
|
6935
|
0
|
|
|
|
|
0
|
$o->{store} = $value->cliStore; |
6936
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value->actorHash; |
6937
|
|
|
|
|
|
|
} |
6938
|
|
|
|
|
|
|
|
6939
|
|
|
|
|
|
|
sub collectActor { |
6940
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6941
|
0
|
|
|
|
|
0
|
my $label = shift; |
6942
|
0
|
|
|
|
|
0
|
my $value = shift; |
6943
|
|
|
|
|
|
|
|
6944
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value; |
6945
|
|
|
|
|
|
|
} |
6946
|
|
|
|
|
|
|
|
6947
|
|
|
|
|
|
|
sub collectKeypair { |
6948
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6949
|
0
|
|
|
|
|
0
|
my $label = shift; |
6950
|
0
|
|
|
|
|
0
|
my $value = shift; |
6951
|
|
|
|
|
|
|
|
6952
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
6953
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value->keyPair->publicKey->hash; |
6954
|
|
|
|
|
|
|
} |
6955
|
|
|
|
|
|
|
|
6956
|
|
|
|
|
|
|
sub collectStore { |
6957
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6958
|
0
|
|
|
|
|
0
|
my $label = shift; |
6959
|
0
|
|
|
|
|
0
|
my $value = shift; |
6960
|
|
|
|
|
|
|
|
6961
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
6962
|
|
|
|
|
|
|
} |
6963
|
|
|
|
|
|
|
|
6964
|
|
|
|
|
|
|
sub collectStore1 { |
6965
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6966
|
0
|
|
|
|
|
0
|
my $label = shift; |
6967
|
0
|
|
|
|
|
0
|
my $value = shift; |
6968
|
|
|
|
|
|
|
|
6969
|
0
|
|
|
|
|
0
|
$o->{store} = $o->{actor}->storageStore; |
6970
|
|
|
|
|
|
|
} |
6971
|
|
|
|
|
|
|
|
6972
|
|
|
|
|
|
|
sub collectStore2 { |
6973
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6974
|
0
|
|
|
|
|
0
|
my $label = shift; |
6975
|
0
|
|
|
|
|
0
|
my $value = shift; |
6976
|
|
|
|
|
|
|
|
6977
|
0
|
|
|
|
|
0
|
$o->{store} = $o->{actor}->messagingStore; |
6978
|
|
|
|
|
|
|
} |
6979
|
|
|
|
|
|
|
|
6980
|
|
|
|
|
|
|
sub new { |
6981
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
6982
|
0
|
|
|
|
|
0
|
my $actor = shift; |
6983
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
6984
|
|
|
|
|
|
|
|
6985
|
|
|
|
|
|
|
# END AUTOGENERATED |
6986
|
|
|
|
|
|
|
|
6987
|
|
|
|
|
|
|
# HTML FOLDER NAME select |
6988
|
|
|
|
|
|
|
# HTML TITLE Select |
6989
|
|
|
|
|
|
|
sub help { |
6990
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
6991
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
6992
|
|
|
|
|
|
|
|
6993
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
6994
|
0
|
|
|
|
|
0
|
$ui->space; |
6995
|
0
|
|
|
|
|
0
|
$ui->command('cds select'); |
6996
|
0
|
|
|
|
|
0
|
$ui->p('Shows the current selection.'); |
6997
|
0
|
|
|
|
|
0
|
$ui->space; |
6998
|
0
|
|
|
|
|
0
|
$ui->command('cds select KEYPAIR'); |
6999
|
0
|
|
|
|
|
0
|
$ui->p('Selects KEYPAIR on this terminal. Some commands will use this key pair by default.'); |
7000
|
0
|
|
|
|
|
0
|
$ui->space; |
7001
|
0
|
|
|
|
|
0
|
$ui->command('cds unselect key pair'); |
7002
|
0
|
|
|
|
|
0
|
$ui->p('Removes the key pair selection.'); |
7003
|
0
|
|
|
|
|
0
|
$ui->space; |
7004
|
0
|
|
|
|
|
0
|
$ui->command('cds select STORE'); |
7005
|
0
|
|
|
|
|
0
|
$ui->p('Selects STORE on this terminal. Some commands will use this store by default.'); |
7006
|
0
|
|
|
|
|
0
|
$ui->space; |
7007
|
0
|
|
|
|
|
0
|
$ui->command('cds unselect store'); |
7008
|
0
|
|
|
|
|
0
|
$ui->p('Removes the store selection.'); |
7009
|
0
|
|
|
|
|
0
|
$ui->space; |
7010
|
0
|
|
|
|
|
0
|
$ui->command('cds select ACTOR'); |
7011
|
0
|
|
|
|
|
0
|
$ui->p('Selects ACTOR on this terminal. Some commands will use this store by default.'); |
7012
|
0
|
|
|
|
|
0
|
$ui->space; |
7013
|
0
|
|
|
|
|
0
|
$ui->command('cds unselect actor'); |
7014
|
0
|
|
|
|
|
0
|
$ui->p('Removes the actor selection.'); |
7015
|
0
|
|
|
|
|
0
|
$ui->space; |
7016
|
0
|
|
|
|
|
0
|
$ui->command('cds unselect'); |
7017
|
0
|
|
|
|
|
0
|
$ui->p('Removes any selection.'); |
7018
|
0
|
|
|
|
|
0
|
$ui->space; |
7019
|
|
|
|
|
|
|
} |
7020
|
|
|
|
|
|
|
|
7021
|
|
|
|
|
|
|
sub select { |
7022
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7023
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7024
|
|
|
|
|
|
|
|
7025
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
7026
|
|
|
|
|
|
|
|
7027
|
0
|
0
|
|
|
|
0
|
if ($o->{keyPairToken}) { |
7028
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('selected key pair')->setText($o->{keyPairToken}->file); |
7029
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Key pair ', $o->{keyPairToken}->file, ' selected.'); |
7030
|
|
|
|
|
|
|
} |
7031
|
|
|
|
|
|
|
|
7032
|
0
|
0
|
|
|
|
0
|
if ($o->{store}) { |
7033
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('selected store')->setText($o->{store}->url); |
7034
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Store ', $o->{store}->url, ' selected.'); |
7035
|
|
|
|
|
|
|
} |
7036
|
|
|
|
|
|
|
|
7037
|
0
|
0
|
|
|
|
0
|
if ($o->{actorHash}) { |
7038
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('selected actor')->setBytes($o->{actorHash}->bytes); |
7039
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Actor ', $o->{actorHash}->hex, ' selected.'); |
7040
|
|
|
|
|
|
|
} |
7041
|
|
|
|
|
|
|
|
7042
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
7043
|
|
|
|
|
|
|
} |
7044
|
|
|
|
|
|
|
|
7045
|
|
|
|
|
|
|
sub unselectKeyPair { |
7046
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7047
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7048
|
|
|
|
|
|
|
|
7049
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('selected key pair')->clear; |
7050
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Key pair selection cleared.'); |
7051
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
7052
|
|
|
|
|
|
|
} |
7053
|
|
|
|
|
|
|
|
7054
|
|
|
|
|
|
|
sub unselectStore { |
7055
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7056
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7057
|
|
|
|
|
|
|
|
7058
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('selected store')->clear; |
7059
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Store selection cleared.'); |
7060
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
7061
|
|
|
|
|
|
|
} |
7062
|
|
|
|
|
|
|
|
7063
|
|
|
|
|
|
|
sub unselectActor { |
7064
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7065
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7066
|
|
|
|
|
|
|
|
7067
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('selected actor')->clear; |
7068
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Actor selection cleared.'); |
7069
|
0
|
|
|
|
|
0
|
$o->{actor}->saveOrShowError; |
7070
|
|
|
|
|
|
|
} |
7071
|
|
|
|
|
|
|
|
7072
|
|
|
|
|
|
|
sub unselectAll { |
7073
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7074
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7075
|
|
|
|
|
|
|
|
7076
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('selected key pair')->clear; |
7077
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('selected store')->clear; |
7078
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('selected actor')->clear; |
7079
|
0
|
|
0
|
|
|
0
|
$o->{actor}->saveOrShowError // return; |
7080
|
0
|
|
|
|
|
0
|
$o->showSelection; |
7081
|
|
|
|
|
|
|
} |
7082
|
|
|
|
|
|
|
|
7083
|
|
|
|
|
|
|
sub showSelectionCmd { |
7084
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7085
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7086
|
|
|
|
|
|
|
|
7087
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7088
|
0
|
|
|
|
|
0
|
$o->showSelection; |
7089
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7090
|
|
|
|
|
|
|
} |
7091
|
|
|
|
|
|
|
|
7092
|
|
|
|
|
|
|
sub showSelection { |
7093
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7094
|
|
|
|
|
|
|
|
7095
|
0
|
|
|
|
|
0
|
my $keyPairFile = $o->{actor}->sessionRoot->child('selected key pair')->textValue; |
7096
|
0
|
|
|
|
|
0
|
my $storeUrl = $o->{actor}->sessionRoot->child('selected store')->textValue; |
7097
|
0
|
|
|
|
|
0
|
my $actorBytes = $o->{actor}->sessionRoot->child('selected actor')->bytesValue; |
7098
|
|
|
|
|
|
|
|
7099
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->darkBold('Selected key pair '), length $keyPairFile ? $keyPairFile : $o->{ui}->gray('none')); |
7100
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->darkBold('Selected store '), length $storeUrl ? $storeUrl : $o->{ui}->gray('none')); |
7101
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->darkBold('Selected actor '), length $actorBytes == 32 ? unpack('H*', $actorBytes) : $o->{ui}->gray('none')); |
7102
|
|
|
|
|
|
|
} |
7103
|
|
|
|
|
|
|
|
7104
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
7105
|
|
|
|
|
|
|
package CDS::Commands::ShowCard; |
7106
|
|
|
|
|
|
|
|
7107
|
|
|
|
|
|
|
sub register { |
7108
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
7109
|
0
|
|
|
|
|
0
|
my $cds = shift; |
7110
|
0
|
|
|
|
|
0
|
my $help = shift; |
7111
|
|
|
|
|
|
|
|
7112
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
7113
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
7114
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
7115
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
7116
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
7117
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showMyCard}); |
7118
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(1); |
7119
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
7120
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
7121
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
7122
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
7123
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
7124
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
7125
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showCard}); |
7126
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'show'); |
7127
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'show'); |
7128
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'show'); |
7129
|
0
|
|
|
|
|
0
|
$node000->addArrow($node004, 1, 0, 'card'); |
7130
|
0
|
|
|
|
|
0
|
$node001->addArrow($node006, 1, 0, 'card'); |
7131
|
0
|
|
|
|
|
0
|
$node002->addArrow($node003, 1, 0, 'my'); |
7132
|
0
|
|
|
|
|
0
|
$node003->addArrow($node005, 1, 0, 'card'); |
7133
|
0
|
|
|
|
|
0
|
$node006->addArrow($node007, 1, 0, 'of'); |
7134
|
0
|
|
|
|
|
0
|
$node006->addArrow($node008, 1, 0, 'of'); |
7135
|
0
|
|
|
|
|
0
|
$node006->addArrow($node009, 1, 0, 'of'); |
7136
|
0
|
|
|
|
|
0
|
$node006->addArrow($node010, 1, 0, 'of'); |
7137
|
0
|
|
|
|
|
0
|
$node006->addDefault($node011); |
7138
|
0
|
|
|
|
|
0
|
$node007->addArrow($node007, 1, 0, 'ACCOUNT', \&collectAccount); |
7139
|
0
|
|
|
|
|
0
|
$node007->addArrow($node013, 1, 1, 'ACCOUNT', \&collectAccount); |
7140
|
0
|
|
|
|
|
0
|
$node008->addArrow($node013, 1, 0, 'ACTORGROUP', \&collectActorgroup); |
7141
|
0
|
|
|
|
|
0
|
$node009->addArrow($node011, 1, 0, 'KEYPAIR', \&collectKeypair); |
7142
|
0
|
|
|
|
|
0
|
$node010->addArrow($node011, 1, 0, 'ACTOR', \&collectActor); |
7143
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'on'); |
7144
|
0
|
|
|
|
|
0
|
$node011->addDefault($node013); |
7145
|
0
|
|
|
|
|
0
|
$node012->addArrow($node012, 1, 0, 'STORE', \&collectStore); |
7146
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'STORE', \&collectStore); |
7147
|
|
|
|
|
|
|
} |
7148
|
|
|
|
|
|
|
|
7149
|
|
|
|
|
|
|
sub collectAccount { |
7150
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7151
|
0
|
|
|
|
|
0
|
my $label = shift; |
7152
|
0
|
|
|
|
|
0
|
my $value = shift; |
7153
|
|
|
|
|
|
|
|
7154
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, $value; |
|
0
|
|
|
|
|
0
|
|
7155
|
|
|
|
|
|
|
} |
7156
|
|
|
|
|
|
|
|
7157
|
|
|
|
|
|
|
sub collectActor { |
7158
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7159
|
0
|
|
|
|
|
0
|
my $label = shift; |
7160
|
0
|
|
|
|
|
0
|
my $value = shift; |
7161
|
|
|
|
|
|
|
|
7162
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value; |
7163
|
|
|
|
|
|
|
} |
7164
|
|
|
|
|
|
|
|
7165
|
|
|
|
|
|
|
sub collectActorgroup { |
7166
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7167
|
0
|
|
|
|
|
0
|
my $label = shift; |
7168
|
0
|
|
|
|
|
0
|
my $value = shift; |
7169
|
|
|
|
|
|
|
|
7170
|
0
|
|
|
|
|
0
|
for my $member ($value->actorGroup->members) { |
7171
|
0
|
|
|
|
|
0
|
my $actorOnStore = $member->actorOnStore; |
7172
|
0
|
|
|
|
|
0
|
$o->addKnownPublicKey($actorOnStore->publicKey); |
7173
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, CDS::AccountToken->new($actorOnStore->store, $actorOnStore->publicKey->hash); |
|
0
|
|
|
|
|
0
|
|
7174
|
|
|
|
|
|
|
} |
7175
|
|
|
|
|
|
|
} |
7176
|
|
|
|
|
|
|
|
7177
|
|
|
|
|
|
|
sub collectKeypair { |
7178
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7179
|
0
|
|
|
|
|
0
|
my $label = shift; |
7180
|
0
|
|
|
|
|
0
|
my $value = shift; |
7181
|
|
|
|
|
|
|
|
7182
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
7183
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value->keyPair->publicKey->hash; |
7184
|
|
|
|
|
|
|
} |
7185
|
|
|
|
|
|
|
|
7186
|
|
|
|
|
|
|
sub collectStore { |
7187
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7188
|
0
|
|
|
|
|
0
|
my $label = shift; |
7189
|
0
|
|
|
|
|
0
|
my $value = shift; |
7190
|
|
|
|
|
|
|
|
7191
|
0
|
|
|
|
|
0
|
push @{$o->{stores}}, $value; |
|
0
|
|
|
|
|
0
|
|
7192
|
|
|
|
|
|
|
} |
7193
|
|
|
|
|
|
|
|
7194
|
|
|
|
|
|
|
sub new { |
7195
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
7196
|
0
|
|
|
|
|
0
|
my $actor = shift; |
7197
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
7198
|
|
|
|
|
|
|
|
7199
|
|
|
|
|
|
|
# END AUTOGENERATED |
7200
|
|
|
|
|
|
|
|
7201
|
|
|
|
|
|
|
# HTML FOLDER NAME show-card |
7202
|
|
|
|
|
|
|
# HTML TITLE Show an actor's public card |
7203
|
|
|
|
|
|
|
sub help { |
7204
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7205
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7206
|
|
|
|
|
|
|
|
7207
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
7208
|
0
|
|
|
|
|
0
|
$ui->space; |
7209
|
0
|
|
|
|
|
0
|
$ui->command('cds show card of ACCOUNT'); |
7210
|
0
|
|
|
|
|
0
|
$ui->command('cds show card of ACTOR [on STORE]'); |
7211
|
0
|
|
|
|
|
0
|
$ui->command('cds show card of KEYPAIR [on STORE]'); |
7212
|
0
|
|
|
|
|
0
|
$ui->p('Shows the card(s) of an actor.'); |
7213
|
0
|
|
|
|
|
0
|
$ui->space; |
7214
|
0
|
|
|
|
|
0
|
$ui->command('cds show card of ACTORGROUP'); |
7215
|
0
|
|
|
|
|
0
|
$ui->p('Shows all cards of an actor group.'); |
7216
|
0
|
|
|
|
|
0
|
$ui->space; |
7217
|
0
|
|
|
|
|
0
|
$ui->command('cds show card'); |
7218
|
0
|
|
|
|
|
0
|
$ui->p('Shows the card of the selected actor on the selected store.'); |
7219
|
0
|
|
|
|
|
0
|
$ui->space; |
7220
|
0
|
|
|
|
|
0
|
$ui->command('cds show my card'); |
7221
|
0
|
|
|
|
|
0
|
$ui->p('Shows your own card.'); |
7222
|
0
|
|
|
|
|
0
|
$ui->space; |
7223
|
0
|
|
|
|
|
0
|
$ui->p('An actor usually has one card. If no cards are shown, the corresponding actor does not exist, is not using that store, or has not properly announced itself. Two cards may exist while the actor is updating its card. Such a state is temporary, but may exist for hours or days if the actor has intermittent network access. Three or more cards may point to an error in the way the actor updates his card, an error in the synchronization code (if the account is synchronized). Two or more cards may also occur naturally when stores are merged.'); |
7224
|
0
|
|
|
|
|
0
|
$ui->space; |
7225
|
0
|
|
|
|
|
0
|
$ui->p('A peer consists of one or more actors, which all publish their own card. The cards are usually different, but should contain consistent information.'); |
7226
|
0
|
|
|
|
|
0
|
$ui->space; |
7227
|
0
|
|
|
|
|
0
|
$ui->p('You can publish your own card (i.e. the card of your main key pair) using'); |
7228
|
0
|
|
|
|
|
0
|
$ui->p(' cds announce'); |
7229
|
0
|
|
|
|
|
0
|
$ui->space; |
7230
|
|
|
|
|
|
|
} |
7231
|
|
|
|
|
|
|
|
7232
|
|
|
|
|
|
|
sub showCard { |
7233
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7234
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7235
|
|
|
|
|
|
|
|
7236
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken; |
7237
|
0
|
|
|
|
|
0
|
$o->{stores} = []; |
7238
|
0
|
|
|
|
|
0
|
$o->{accountTokens} = []; |
7239
|
0
|
|
|
|
|
0
|
$o->{knownPublicKeys} = {}; |
7240
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
7241
|
|
|
|
|
|
|
|
7242
|
|
|
|
|
|
|
# Use actorHash/store |
7243
|
0
|
0
|
|
|
|
0
|
if (! scalar @{$o->{accountTokens}}) { |
|
0
|
|
|
|
|
0
|
|
7244
|
0
|
0
|
|
|
|
0
|
$o->{actorHash} = $o->{actor}->preferredActorHash if ! $o->{actorHash}; |
7245
|
0
|
0
|
|
|
|
0
|
push @{$o->{stores}}, $o->{actor}->preferredStores if ! scalar @{$o->{stores}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
7246
|
0
|
|
|
|
|
0
|
for my $store (@{$o->{stores}}) { |
|
0
|
|
|
|
|
0
|
|
7247
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, CDS::AccountToken->new($store, $o->{actorHash}); |
|
0
|
|
|
|
|
0
|
|
7248
|
|
|
|
|
|
|
} |
7249
|
|
|
|
|
|
|
} |
7250
|
|
|
|
|
|
|
|
7251
|
|
|
|
|
|
|
# Show the cards |
7252
|
0
|
|
|
|
|
0
|
$o->addKnownPublicKey($o->{keyPairToken}->keyPair->publicKey); |
7253
|
0
|
|
|
|
|
0
|
$o->addKnownPublicKey($o->{actor}->keyPair->publicKey); |
7254
|
0
|
|
|
|
|
0
|
for my $accountToken (@{$o->{accountTokens}}) { |
|
0
|
|
|
|
|
0
|
|
7255
|
0
|
|
|
|
|
0
|
$o->processAccount($accountToken); |
7256
|
|
|
|
|
|
|
} |
7257
|
|
|
|
|
|
|
|
7258
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7259
|
|
|
|
|
|
|
} |
7260
|
|
|
|
|
|
|
|
7261
|
|
|
|
|
|
|
sub showMyCard { |
7262
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7263
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7264
|
|
|
|
|
|
|
|
7265
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken; |
7266
|
0
|
|
|
|
|
0
|
$o->processAccount(CDS::AccountToken->new($o->{actor}->messagingStore, $o->{actor}->keyPair->publicKey->hash)); |
7267
|
0
|
0
|
|
|
|
0
|
$o->processAccount(CDS::AccountToken->new($o->{actor}->storageStore, $o->{actor}->keyPair->publicKey->hash)) if $o->{actor}->storageStore->url ne $o->{actor}->messagingStore->url; |
7268
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7269
|
|
|
|
|
|
|
} |
7270
|
|
|
|
|
|
|
|
7271
|
|
|
|
|
|
|
sub processAccount { |
7272
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7273
|
0
|
|
|
|
|
0
|
my $accountToken = shift; |
7274
|
|
|
|
|
|
|
|
7275
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7276
|
|
|
|
|
|
|
|
7277
|
|
|
|
|
|
|
# Query the store |
7278
|
0
|
|
|
|
|
0
|
my $store = $accountToken->cliStore; |
7279
|
0
|
|
|
|
|
0
|
my ($hashes, $storeError) = $store->list($accountToken->actorHash, 'public', 0); |
7280
|
0
|
0
|
|
|
|
0
|
if (defined $storeError) { |
7281
|
0
|
|
|
|
|
0
|
$o->{ui}->title('public box of ', $o->{actor}->blueAccountReference($accountToken)); |
7282
|
0
|
|
|
|
|
0
|
return; |
7283
|
|
|
|
|
|
|
} |
7284
|
|
|
|
|
|
|
|
7285
|
|
|
|
|
|
|
# Print the result |
7286
|
0
|
|
|
|
|
0
|
my $count = scalar @$hashes; |
7287
|
0
|
0
|
|
|
|
0
|
$o->{ui}->title('public box of ', $o->{actor}->blueAccountReference($accountToken), ' ', $o->{ui}->blue($count == 0 ? 'no cards' : $count == 1 ? '1 card' : $count.' cards')); |
|
|
0
|
|
|
|
|
|
7288
|
0
|
0
|
|
|
|
0
|
return if ! $count; |
7289
|
|
|
|
|
|
|
|
7290
|
0
|
|
|
|
|
0
|
foreach my $hash (sort { $a->bytes cmp $b->bytes } @$hashes) { |
|
0
|
|
|
|
|
0
|
|
7291
|
0
|
|
|
|
|
0
|
$o->processEntry($accountToken, $hash); |
7292
|
|
|
|
|
|
|
} |
7293
|
|
|
|
|
|
|
} |
7294
|
|
|
|
|
|
|
|
7295
|
|
|
|
|
|
|
sub processEntry { |
7296
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7297
|
0
|
|
|
|
|
0
|
my $accountToken = shift; |
7298
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
7299
|
|
|
|
|
|
|
|
7300
|
0
|
|
|
|
|
0
|
my $keyPair = $o->{keyPairToken}->keyPair; |
7301
|
0
|
|
|
|
|
0
|
my $store = $accountToken->cliStore; |
7302
|
0
|
|
|
|
|
0
|
my $storeReference = $o->{actor}->storeReference($store); |
7303
|
|
|
|
|
|
|
|
7304
|
|
|
|
|
|
|
# Open the envelope |
7305
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds open envelope ', $hash->hex), $o->{ui}->gray(' from ', $accountToken->actorHash->hex, ' on ', $storeReference)); |
7306
|
|
|
|
|
|
|
|
7307
|
0
|
|
0
|
|
|
0
|
my $envelope = $o->{actor}->uiGetRecord($hash, $accountToken->cliStore, $o->{keyPairToken}) // return; |
7308
|
0
|
|
0
|
|
|
0
|
my $publicKey = $o->getPublicKey($accountToken) // $o->{ui}->pRed('The owner\'s public key is missing. Skipping signature verification.'); |
7309
|
0
|
|
0
|
|
|
0
|
my $cardHash = $envelope->child('content')->hashValue // $o->{ui}->pRed('Missing content hash.'); |
7310
|
0
|
0
|
0
|
|
|
0
|
return $o->{ui}->pRed('Invalid signature.') if $publicKey && $cardHash && ! CDS->verifyEnvelopeSignature($envelope, $publicKey, $cardHash); |
|
|
|
0
|
|
|
|
|
7311
|
|
|
|
|
|
|
|
7312
|
|
|
|
|
|
|
# Read and show the card |
7313
|
0
|
0
|
|
|
|
0
|
return if ! $cardHash; |
7314
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold('cds show record ', $cardHash->hex), $o->{ui}->gray(' on ', $storeReference)); |
7315
|
0
|
|
0
|
|
|
0
|
my $card = $o->{actor}->uiGetRecord($cardHash, $accountToken->cliStore, $o->{keyPairToken}) // return; |
7316
|
|
|
|
|
|
|
|
7317
|
0
|
|
|
|
|
0
|
$o->{ui}->pushIndent; |
7318
|
0
|
|
|
|
|
0
|
$o->{ui}->recordChildren($card, $storeReference); |
7319
|
0
|
|
|
|
|
0
|
$o->{ui}->popIndent; |
7320
|
0
|
|
|
|
|
0
|
return; |
7321
|
|
|
|
|
|
|
} |
7322
|
|
|
|
|
|
|
|
7323
|
|
|
|
|
|
|
sub getPublicKey { |
7324
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7325
|
0
|
|
|
|
|
0
|
my $accountToken = shift; |
7326
|
|
|
|
|
|
|
|
7327
|
0
|
|
|
|
|
0
|
my $hash = $accountToken->actorHash; |
7328
|
0
|
|
|
|
|
0
|
my $knownPublicKey = $o->{knownPublicKeys}->{$hash->bytes}; |
7329
|
0
|
0
|
|
|
|
0
|
return $knownPublicKey if $knownPublicKey; |
7330
|
0
|
|
0
|
|
|
0
|
my $publicKey = $o->{actor}->uiGetPublicKey($hash, $accountToken->cliStore, $o->{keyPairToken}) // return; |
7331
|
0
|
|
|
|
|
0
|
$o->addKnownPublicKey($publicKey); |
7332
|
0
|
|
|
|
|
0
|
return $publicKey; |
7333
|
|
|
|
|
|
|
} |
7334
|
|
|
|
|
|
|
|
7335
|
|
|
|
|
|
|
sub addKnownPublicKey { |
7336
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7337
|
0
|
0
|
0
|
|
|
0
|
my $publicKey = shift; die 'wrong type '.ref($publicKey).' for $publicKey' if defined $publicKey && ref $publicKey ne 'CDS::PublicKey'; |
|
0
|
|
|
|
|
0
|
|
7338
|
|
|
|
|
|
|
|
7339
|
0
|
|
|
|
|
0
|
$o->{knownPublicKeys}->{$publicKey->hash->bytes} = $publicKey; |
7340
|
|
|
|
|
|
|
} |
7341
|
|
|
|
|
|
|
|
7342
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
7343
|
|
|
|
|
|
|
package CDS::Commands::ShowKeyPair; |
7344
|
|
|
|
|
|
|
|
7345
|
|
|
|
|
|
|
sub register { |
7346
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
7347
|
0
|
|
|
|
|
0
|
my $cds = shift; |
7348
|
0
|
|
|
|
|
0
|
my $help = shift; |
7349
|
|
|
|
|
|
|
|
7350
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
7351
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
7352
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
7353
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
7354
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
7355
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
7356
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
7357
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
7358
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
7359
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showKeyPair}); |
7360
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showMyKeyPair}); |
7361
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showSelectedKeyPair}); |
7362
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'show'); |
7363
|
0
|
|
|
|
|
0
|
$cds->addArrow($node003, 1, 0, 'show'); |
7364
|
0
|
|
|
|
|
0
|
$cds->addArrow($node004, 1, 0, 'show'); |
7365
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'show'); |
7366
|
0
|
|
|
|
|
0
|
$node000->addArrow($node001, 1, 0, 'key'); |
7367
|
0
|
|
|
|
|
0
|
$node001->addArrow($node008, 1, 0, 'pair'); |
7368
|
0
|
|
|
|
|
0
|
$node002->addArrow($node009, 1, 0, 'KEYPAIR', \&collectKeypair); |
7369
|
0
|
|
|
|
|
0
|
$node003->addArrow($node005, 1, 0, 'my'); |
7370
|
0
|
|
|
|
|
0
|
$node004->addArrow($node006, 1, 0, 'key'); |
7371
|
0
|
|
|
|
|
0
|
$node005->addArrow($node007, 1, 0, 'key'); |
7372
|
0
|
|
|
|
|
0
|
$node006->addArrow($node011, 1, 0, 'pair'); |
7373
|
0
|
|
|
|
|
0
|
$node007->addArrow($node010, 1, 0, 'pair'); |
7374
|
|
|
|
|
|
|
} |
7375
|
|
|
|
|
|
|
|
7376
|
|
|
|
|
|
|
sub collectKeypair { |
7377
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7378
|
0
|
|
|
|
|
0
|
my $label = shift; |
7379
|
0
|
|
|
|
|
0
|
my $value = shift; |
7380
|
|
|
|
|
|
|
|
7381
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
7382
|
|
|
|
|
|
|
} |
7383
|
|
|
|
|
|
|
|
7384
|
|
|
|
|
|
|
sub new { |
7385
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
7386
|
0
|
|
|
|
|
0
|
my $actor = shift; |
7387
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
7388
|
|
|
|
|
|
|
|
7389
|
|
|
|
|
|
|
# END AUTOGENERATED |
7390
|
|
|
|
|
|
|
|
7391
|
|
|
|
|
|
|
# HTML FOLDER NAME show-key-pair |
7392
|
|
|
|
|
|
|
# HTML TITLE Show key pair |
7393
|
|
|
|
|
|
|
sub help { |
7394
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7395
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7396
|
|
|
|
|
|
|
|
7397
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
7398
|
0
|
|
|
|
|
0
|
$ui->space; |
7399
|
0
|
|
|
|
|
0
|
$ui->command('cds show KEYPAIR'); |
7400
|
0
|
|
|
|
|
0
|
$ui->command('cds show my key pair'); |
7401
|
0
|
|
|
|
|
0
|
$ui->command('cds show key pair'); |
7402
|
0
|
|
|
|
|
0
|
$ui->p('Shows information about KEYPAIR, your key pair, or the currently selected key pair (see "cds use …").'); |
7403
|
0
|
|
|
|
|
0
|
$ui->space; |
7404
|
|
|
|
|
|
|
} |
7405
|
|
|
|
|
|
|
|
7406
|
|
|
|
|
|
|
sub showKeyPair { |
7407
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7408
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7409
|
|
|
|
|
|
|
|
7410
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
7411
|
0
|
|
|
|
|
0
|
$o->showAll($o->{keyPairToken}); |
7412
|
|
|
|
|
|
|
} |
7413
|
|
|
|
|
|
|
|
7414
|
|
|
|
|
|
|
sub showMyKeyPair { |
7415
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7416
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7417
|
|
|
|
|
|
|
|
7418
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
7419
|
0
|
|
|
|
|
0
|
$o->showAll($o->{actor}->keyPairToken); |
7420
|
|
|
|
|
|
|
} |
7421
|
|
|
|
|
|
|
|
7422
|
|
|
|
|
|
|
sub showSelectedKeyPair { |
7423
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7424
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7425
|
|
|
|
|
|
|
|
7426
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
7427
|
0
|
|
|
|
|
0
|
$o->showAll($o->{actor}->preferredKeyPairToken); |
7428
|
|
|
|
|
|
|
} |
7429
|
|
|
|
|
|
|
|
7430
|
|
|
|
|
|
|
sub show { |
7431
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7432
|
0
|
|
|
|
|
0
|
my $keyPairToken = shift; |
7433
|
|
|
|
|
|
|
|
7434
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->darkBold('File '), $keyPairToken->file) if defined $keyPairToken->file; |
7435
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->darkBold('Hash '), $keyPairToken->keyPair->publicKey->hash->hex); |
7436
|
|
|
|
|
|
|
} |
7437
|
|
|
|
|
|
|
|
7438
|
|
|
|
|
|
|
sub showAll { |
7439
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7440
|
0
|
|
|
|
|
0
|
my $keyPairToken = shift; |
7441
|
|
|
|
|
|
|
|
7442
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7443
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Key pair'); |
7444
|
0
|
|
|
|
|
0
|
$o->show($keyPairToken); |
7445
|
0
|
|
|
|
|
0
|
$o->showPublicKeyObject($keyPairToken); |
7446
|
0
|
|
|
|
|
0
|
$o->showPublicKey($keyPairToken); |
7447
|
0
|
|
|
|
|
0
|
$o->showPrivateKey($keyPairToken); |
7448
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7449
|
|
|
|
|
|
|
} |
7450
|
|
|
|
|
|
|
|
7451
|
|
|
|
|
|
|
sub showPublicKeyObject { |
7452
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7453
|
0
|
|
|
|
|
0
|
my $keyPairToken = shift; |
7454
|
|
|
|
|
|
|
|
7455
|
0
|
|
|
|
|
0
|
my $object = $keyPairToken->keyPair->publicKey->object; |
7456
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7457
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Public key object'); |
7458
|
0
|
|
|
|
|
0
|
$o->byteData(' ', $object->bytes); |
7459
|
|
|
|
|
|
|
} |
7460
|
|
|
|
|
|
|
|
7461
|
|
|
|
|
|
|
sub showPublicKey { |
7462
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7463
|
0
|
|
|
|
|
0
|
my $keyPairToken = shift; |
7464
|
|
|
|
|
|
|
|
7465
|
0
|
|
|
|
|
0
|
my $rsaPublicKey = $keyPairToken->keyPair->publicKey->{rsaPublicKey}; |
7466
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7467
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Public key'); |
7468
|
0
|
|
|
|
|
0
|
$o->byteData('e ', CDS::C::publicKeyE($rsaPublicKey)); |
7469
|
0
|
|
|
|
|
0
|
$o->byteData('n ', CDS::C::publicKeyN($rsaPublicKey)); |
7470
|
|
|
|
|
|
|
} |
7471
|
|
|
|
|
|
|
|
7472
|
|
|
|
|
|
|
sub showPrivateKey { |
7473
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7474
|
0
|
|
|
|
|
0
|
my $keyPairToken = shift; |
7475
|
|
|
|
|
|
|
|
7476
|
0
|
|
|
|
|
0
|
my $rsaPrivateKey = $keyPairToken->keyPair->{rsaPrivateKey}; |
7477
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7478
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Private key'); |
7479
|
0
|
|
|
|
|
0
|
$o->byteData('e ', CDS::C::privateKeyE($rsaPrivateKey)); |
7480
|
0
|
|
|
|
|
0
|
$o->byteData('p ', CDS::C::privateKeyP($rsaPrivateKey)); |
7481
|
0
|
|
|
|
|
0
|
$o->byteData('q ', CDS::C::privateKeyQ($rsaPrivateKey)); |
7482
|
|
|
|
|
|
|
} |
7483
|
|
|
|
|
|
|
|
7484
|
|
|
|
|
|
|
sub byteData { |
7485
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7486
|
0
|
|
|
|
|
0
|
my $label = shift; |
7487
|
0
|
|
|
|
|
0
|
my $bytes = shift; |
7488
|
|
|
|
|
|
|
|
7489
|
0
|
|
|
|
|
0
|
my $hex = unpack('H*', $bytes); |
7490
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->darkBold($label), substr($hex, 0, 64)); |
7491
|
|
|
|
|
|
|
|
7492
|
0
|
|
|
|
|
0
|
my $start = 64; |
7493
|
0
|
|
|
|
|
0
|
my $spaces = ' ' x length $label; |
7494
|
0
|
|
|
|
|
0
|
while ($start < length $hex) { |
7495
|
0
|
|
|
|
|
0
|
$o->{ui}->line($spaces, substr($hex, $start, 64)); |
7496
|
0
|
|
|
|
|
0
|
$start += 64; |
7497
|
|
|
|
|
|
|
} |
7498
|
|
|
|
|
|
|
} |
7499
|
|
|
|
|
|
|
|
7500
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
7501
|
|
|
|
|
|
|
package CDS::Commands::ShowMessages; |
7502
|
|
|
|
|
|
|
|
7503
|
|
|
|
|
|
|
sub register { |
7504
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
7505
|
0
|
|
|
|
|
0
|
my $cds = shift; |
7506
|
0
|
|
|
|
|
0
|
my $help = shift; |
7507
|
|
|
|
|
|
|
|
7508
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
7509
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
7510
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
7511
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
7512
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
7513
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
7514
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
7515
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
7516
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
7517
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showMessagesOfSelected}); |
7518
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showMyMessages}); |
7519
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showOurMessages}); |
7520
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(1); |
7521
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
7522
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(0); |
7523
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showMessages}); |
7524
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'show'); |
7525
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'show'); |
7526
|
0
|
|
|
|
|
0
|
$cds->addArrow($node003, 1, 0, 'show'); |
7527
|
0
|
|
|
|
|
0
|
$cds->addArrow($node004, 1, 0, 'show'); |
7528
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'show'); |
7529
|
0
|
|
|
|
|
0
|
$node000->addArrow($node008, 1, 0, 'messages'); |
7530
|
0
|
|
|
|
|
0
|
$node001->addArrow($node005, 1, 0, 'messages'); |
7531
|
0
|
|
|
|
|
0
|
$node002->addArrow($node006, 1, 0, 'my'); |
7532
|
0
|
|
|
|
|
0
|
$node003->addArrow($node009, 1, 0, 'messages'); |
7533
|
0
|
|
|
|
|
0
|
$node004->addArrow($node007, 1, 0, 'our'); |
7534
|
0
|
|
|
|
|
0
|
$node005->addArrow($node012, 1, 0, 'of'); |
7535
|
0
|
|
|
|
|
0
|
$node006->addArrow($node010, 1, 0, 'messages'); |
7536
|
0
|
|
|
|
|
0
|
$node007->addArrow($node011, 1, 0, 'messages'); |
7537
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'ACTOR', \&collectActor); |
7538
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'KEYPAIR', \&collectKeypair); |
7539
|
0
|
|
|
|
|
0
|
$node012->addArrow($node015, 1, 1, 'ACCOUNT', \&collectAccount); |
7540
|
0
|
|
|
|
|
0
|
$node012->addArrow($node015, 1, 0, 'ACTOR', \&collectActor1); |
7541
|
0
|
|
|
|
|
0
|
$node012->addArrow($node015, 1, 0, 'ACTORGROUP', \&collectActorgroup); |
7542
|
0
|
|
|
|
|
0
|
$node012->addArrow($node015, 1, 0, 'KEYPAIR', \&collectKeypair1); |
7543
|
0
|
|
|
|
|
0
|
$node013->addArrow($node014, 1, 0, 'on'); |
7544
|
0
|
|
|
|
|
0
|
$node014->addArrow($node015, 1, 0, 'STORE', \&collectStore); |
7545
|
|
|
|
|
|
|
} |
7546
|
|
|
|
|
|
|
|
7547
|
|
|
|
|
|
|
sub collectAccount { |
7548
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7549
|
0
|
|
|
|
|
0
|
my $label = shift; |
7550
|
0
|
|
|
|
|
0
|
my $value = shift; |
7551
|
|
|
|
|
|
|
|
7552
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, $value; |
|
0
|
|
|
|
|
0
|
|
7553
|
|
|
|
|
|
|
} |
7554
|
|
|
|
|
|
|
|
7555
|
|
|
|
|
|
|
sub collectActor { |
7556
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7557
|
0
|
|
|
|
|
0
|
my $label = shift; |
7558
|
0
|
|
|
|
|
0
|
my $value = shift; |
7559
|
|
|
|
|
|
|
|
7560
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value; |
7561
|
|
|
|
|
|
|
} |
7562
|
|
|
|
|
|
|
|
7563
|
|
|
|
|
|
|
sub collectActor1 { |
7564
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7565
|
0
|
|
|
|
|
0
|
my $label = shift; |
7566
|
0
|
|
|
|
|
0
|
my $value = shift; |
7567
|
|
|
|
|
|
|
|
7568
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, CDS::AccountToken->new($o->{actor}->preferredStore, $value); |
|
0
|
|
|
|
|
0
|
|
7569
|
|
|
|
|
|
|
} |
7570
|
|
|
|
|
|
|
|
7571
|
|
|
|
|
|
|
sub collectActorgroup { |
7572
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7573
|
0
|
|
|
|
|
0
|
my $label = shift; |
7574
|
0
|
|
|
|
|
0
|
my $value = shift; |
7575
|
|
|
|
|
|
|
|
7576
|
0
|
|
|
|
|
0
|
for my $member ($value->actorGroup->members) { |
7577
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, CDS::AccountToken->new($member->actorOnStore->store, $member->actorOnStore->publicKey->hash); |
|
0
|
|
|
|
|
0
|
|
7578
|
|
|
|
|
|
|
} |
7579
|
|
|
|
|
|
|
} |
7580
|
|
|
|
|
|
|
|
7581
|
|
|
|
|
|
|
sub collectKeypair { |
7582
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7583
|
0
|
|
|
|
|
0
|
my $label = shift; |
7584
|
0
|
|
|
|
|
0
|
my $value = shift; |
7585
|
|
|
|
|
|
|
|
7586
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
7587
|
0
|
|
|
|
|
0
|
$o->{actorHash} = $value->keyPair->publicKey->hash; |
7588
|
|
|
|
|
|
|
} |
7589
|
|
|
|
|
|
|
|
7590
|
|
|
|
|
|
|
sub collectKeypair1 { |
7591
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7592
|
0
|
|
|
|
|
0
|
my $label = shift; |
7593
|
0
|
|
|
|
|
0
|
my $value = shift; |
7594
|
|
|
|
|
|
|
|
7595
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $value; |
7596
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, CDS::AccountToken->new($o->{actor}->preferredStore, $value->publicKey->hash); |
|
0
|
|
|
|
|
0
|
|
7597
|
|
|
|
|
|
|
} |
7598
|
|
|
|
|
|
|
|
7599
|
|
|
|
|
|
|
sub collectStore { |
7600
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7601
|
0
|
|
|
|
|
0
|
my $label = shift; |
7602
|
0
|
|
|
|
|
0
|
my $value = shift; |
7603
|
|
|
|
|
|
|
|
7604
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, CDS::AccountToken->new($value, $o->{actorHash}); |
|
0
|
|
|
|
|
0
|
|
7605
|
0
|
|
|
|
|
0
|
delete $o->{actorHash}; |
7606
|
|
|
|
|
|
|
} |
7607
|
|
|
|
|
|
|
|
7608
|
|
|
|
|
|
|
sub new { |
7609
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
7610
|
0
|
|
|
|
|
0
|
my $actor = shift; |
7611
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
7612
|
|
|
|
|
|
|
|
7613
|
|
|
|
|
|
|
# END AUTOGENERATED |
7614
|
|
|
|
|
|
|
|
7615
|
|
|
|
|
|
|
# HTML FOLDER NAME show-messages |
7616
|
|
|
|
|
|
|
# HTML TITLE Show messages |
7617
|
|
|
|
|
|
|
sub help { |
7618
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7619
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7620
|
|
|
|
|
|
|
|
7621
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
7622
|
0
|
|
|
|
|
0
|
$ui->space; |
7623
|
0
|
|
|
|
|
0
|
$ui->command('cds show messages of ACCOUNT'); |
7624
|
0
|
|
|
|
|
0
|
$ui->command('cds show messages of ACTOR|KEYPAIR [on STORE]'); |
7625
|
0
|
|
|
|
|
0
|
$ui->p('Shows all (unprocessed) messages of an actor ordered by their envelope hash. If store is omitted, the selected store is used.'); |
7626
|
0
|
|
|
|
|
0
|
$ui->space; |
7627
|
0
|
|
|
|
|
0
|
$ui->command('cds show messages of ACTORGROUP'); |
7628
|
0
|
|
|
|
|
0
|
$ui->p('Shows all messages of all actors of that group.'); |
7629
|
0
|
|
|
|
|
0
|
$ui->space; |
7630
|
0
|
|
|
|
|
0
|
$ui->command('cds show messages'); |
7631
|
0
|
|
|
|
|
0
|
$ui->p('Shows the messages of the selected key pair on the selected store.'); |
7632
|
0
|
|
|
|
|
0
|
$ui->space; |
7633
|
0
|
|
|
|
|
0
|
$ui->command('cds show my messages'); |
7634
|
0
|
|
|
|
|
0
|
$ui->p('Shows your messages.'); |
7635
|
0
|
|
|
|
|
0
|
$ui->space; |
7636
|
0
|
|
|
|
|
0
|
$ui->command('cds show our messages'); |
7637
|
0
|
|
|
|
|
0
|
$ui->p('Shows all messages of your actor group.'); |
7638
|
0
|
|
|
|
|
0
|
$ui->space; |
7639
|
0
|
|
|
|
|
0
|
$ui->p('Unprocessed messages are stored in the message box of an actor. Each entry points to an envelope, which in turn points to a record object. The envelope is signed by the sender, but does not hold any date. If the application relies on dates, it must include this date in the message.'); |
7640
|
0
|
|
|
|
|
0
|
$ui->space; |
7641
|
0
|
|
|
|
|
0
|
$ui->p('While the envelope hash is stored on the actor\'s store, the envelope and the message are stored on the sender\'s store, and are downloaded from there. Depending on the reachability and responsiveness of that store, messages may not always be accessible.'); |
7642
|
0
|
|
|
|
|
0
|
$ui->space; |
7643
|
0
|
|
|
|
|
0
|
$ui->p('Senders typically keep sent messages for about 10 days on their store. After that, the envelope hash may still be in the message box, but the actual message may have vanished.'); |
7644
|
0
|
|
|
|
|
0
|
$ui->space; |
7645
|
|
|
|
|
|
|
} |
7646
|
|
|
|
|
|
|
|
7647
|
|
|
|
|
|
|
sub showMessagesOfSelected { |
7648
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7649
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7650
|
|
|
|
|
|
|
|
7651
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken; |
7652
|
0
|
|
|
|
|
0
|
$o->processAccounts(CDS::AccountToken->new($o->{actor}->preferredStore, $o->{actor}->preferredActorHash)); |
7653
|
|
|
|
|
|
|
} |
7654
|
|
|
|
|
|
|
|
7655
|
|
|
|
|
|
|
sub showMyMessages { |
7656
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7657
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7658
|
|
|
|
|
|
|
|
7659
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->keyPairToken; |
7660
|
0
|
|
|
|
|
0
|
my $actorHash = $o->{actor}->keyPair->publicKey->hash; |
7661
|
0
|
|
|
|
|
0
|
my $store = $o->{actor}->messagingStore; |
7662
|
0
|
|
|
|
|
0
|
$o->processAccounts(CDS::AccountToken->new($store, $actorHash)); |
7663
|
|
|
|
|
|
|
} |
7664
|
|
|
|
|
|
|
|
7665
|
|
|
|
|
|
|
sub showOurMessages { |
7666
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7667
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7668
|
|
|
|
|
|
|
|
7669
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->keyPairToken; |
7670
|
|
|
|
|
|
|
|
7671
|
0
|
|
|
|
|
0
|
my @accountTokens; |
7672
|
0
|
|
|
|
|
0
|
for my $child ($o->{actor}->actorGroupSelector->children) { |
7673
|
0
|
0
|
|
|
|
0
|
next if $child->child('revoked')->isSet; |
7674
|
0
|
0
|
|
|
|
0
|
next if ! $child->child('active')->isSet; |
7675
|
|
|
|
|
|
|
|
7676
|
0
|
|
|
|
|
0
|
my $record = $child->record; |
7677
|
0
|
|
0
|
|
|
0
|
my $actorHash = $record->child('hash')->hashValue // next; |
7678
|
0
|
|
|
|
|
0
|
my $storeUrl = $record->child('store')->textValue; |
7679
|
0
|
|
0
|
|
|
0
|
my $store = $o->{actor}->storeForUrl($storeUrl) // next; |
7680
|
0
|
|
|
|
|
0
|
push @accountTokens, CDS::AccountToken->new($store, $actorHash); |
7681
|
|
|
|
|
|
|
} |
7682
|
|
|
|
|
|
|
|
7683
|
0
|
|
|
|
|
0
|
$o->processAccounts(@accountTokens); |
7684
|
|
|
|
|
|
|
} |
7685
|
|
|
|
|
|
|
|
7686
|
|
|
|
|
|
|
sub showMessages { |
7687
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7688
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7689
|
|
|
|
|
|
|
|
7690
|
0
|
|
|
|
|
0
|
$o->{accountTokens} = []; |
7691
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
7692
|
|
|
|
|
|
|
|
7693
|
|
|
|
|
|
|
# Unless a key pair was provided, use the selected key pair |
7694
|
0
|
0
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->keyPairToken if ! $o->{keyPairToken}; |
7695
|
|
|
|
|
|
|
|
7696
|
0
|
|
|
|
|
0
|
$o->processAccounts(@{$o->{accountTokens}}); |
|
0
|
|
|
|
|
0
|
|
7697
|
|
|
|
|
|
|
} |
7698
|
|
|
|
|
|
|
|
7699
|
|
|
|
|
|
|
sub processAccounts { |
7700
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7701
|
|
|
|
|
|
|
|
7702
|
|
|
|
|
|
|
# Initialize the statistics |
7703
|
0
|
|
|
|
|
0
|
$o->{countValid} = 0; |
7704
|
0
|
|
|
|
|
0
|
$o->{countInvalid} = 0; |
7705
|
|
|
|
|
|
|
|
7706
|
|
|
|
|
|
|
# Show the messages of all selected accounts |
7707
|
0
|
|
|
|
|
0
|
for my $accountToken (@_) { |
7708
|
0
|
|
|
|
|
0
|
CDS::Commands::ShowMessages::ProcessAccount->new($o, $accountToken); |
7709
|
|
|
|
|
|
|
} |
7710
|
|
|
|
|
|
|
|
7711
|
|
|
|
|
|
|
# Show the statistics |
7712
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7713
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Total'); |
7714
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line(scalar @_, ' account', scalar @_ == 1 ? '' : 's'); |
7715
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($o->{countValid}, ' message', $o->{countValid} == 1 ? '' : 's'); |
7716
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($o->{countInvalid}, ' invalid message', $o->{countInvalid} == 1 ? '' : 's') if $o->{countInvalid}; |
|
|
0
|
|
|
|
|
|
7717
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
7718
|
|
|
|
|
|
|
} |
7719
|
|
|
|
|
|
|
|
7720
|
|
|
|
|
|
|
package CDS::Commands::ShowMessages::ProcessAccount; |
7721
|
|
|
|
|
|
|
|
7722
|
|
|
|
|
|
|
sub new { |
7723
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
7724
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7725
|
0
|
|
|
|
|
0
|
my $accountToken = shift; |
7726
|
|
|
|
|
|
|
|
7727
|
0
|
|
|
|
|
0
|
my $o = bless { |
7728
|
|
|
|
|
|
|
cmd => $cmd, |
7729
|
|
|
|
|
|
|
accountToken => $accountToken, |
7730
|
|
|
|
|
|
|
countValid => 0, |
7731
|
|
|
|
|
|
|
countInvalid => 0, |
7732
|
|
|
|
|
|
|
}; |
7733
|
|
|
|
|
|
|
|
7734
|
0
|
|
|
|
|
0
|
$cmd->{ui}->space; |
7735
|
0
|
|
|
|
|
0
|
$cmd->{ui}->title('Messages of ', $cmd->{actor}->blueAccountReference($accountToken)); |
7736
|
|
|
|
|
|
|
|
7737
|
|
|
|
|
|
|
# Get the public key |
7738
|
0
|
|
0
|
|
|
0
|
my $publicKey = $o->getPublicKey // return; |
7739
|
|
|
|
|
|
|
|
7740
|
|
|
|
|
|
|
# Read all messages |
7741
|
0
|
|
|
|
|
0
|
my $publicKeyCache = CDS::PublicKeyCache->new(128); |
7742
|
0
|
|
|
|
|
0
|
my $pool = CDS::MessageBoxReaderPool->new($cmd->{keyPairToken}->keyPair, $publicKeyCache, $o); |
7743
|
0
|
|
|
|
|
0
|
my $reader = CDS::MessageBoxReader->new($pool, CDS::ActorOnStore->new($publicKey, $accountToken->cliStore)); |
7744
|
0
|
|
|
|
|
0
|
$reader->read; |
7745
|
|
|
|
|
|
|
|
7746
|
0
|
0
|
|
|
|
0
|
$cmd->{ui}->line($cmd->{ui}->gray('No messages.')) if $o->{countValid} + $o->{countInvalid} == 0; |
7747
|
|
|
|
|
|
|
} |
7748
|
|
|
|
|
|
|
|
7749
|
|
|
|
|
|
|
sub getPublicKey { |
7750
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7751
|
|
|
|
|
|
|
|
7752
|
|
|
|
|
|
|
# Use the keypair's public key if possible |
7753
|
0
|
0
|
|
|
|
0
|
return $o->{cmd}->{keyPairToken}->keyPair->publicKey if $o->{accountToken}->actorHash->equals($o->{cmd}->{keyPairToken}->keyPair->publicKey->hash); |
7754
|
|
|
|
|
|
|
|
7755
|
|
|
|
|
|
|
# Retrieve the public key |
7756
|
0
|
|
|
|
|
0
|
return $o->{cmd}->{actor}->uiGetPublicKey($o->{accountToken}->actorHash, $o->{accountToken}->cliStore, $o->{cmd}->{keyPairToken}); |
7757
|
|
|
|
|
|
|
} |
7758
|
|
|
|
|
|
|
|
7759
|
|
|
|
|
|
|
sub onMessageBoxVerifyStore { |
7760
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7761
|
0
|
|
|
|
|
0
|
my $senderStoreUrl = shift; |
7762
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
7763
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
7764
|
0
|
0
|
0
|
|
|
0
|
my $senderHash = shift; die 'wrong type '.ref($senderHash).' for $senderHash' if defined $senderHash && ref $senderHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
7765
|
|
|
|
|
|
|
|
7766
|
0
|
|
|
|
|
0
|
return $o->{cmd}->{actor}->storeForUrl($senderStoreUrl); |
7767
|
|
|
|
|
|
|
} |
7768
|
|
|
|
|
|
|
|
7769
|
|
|
|
|
|
|
sub onMessageBoxEntry { |
7770
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7771
|
0
|
|
|
|
|
0
|
my $message = shift; |
7772
|
|
|
|
|
|
|
|
7773
|
0
|
|
|
|
|
0
|
$o->{countValid} += 1; |
7774
|
0
|
|
|
|
|
0
|
$o->{cmd}->{countValid} += 1; |
7775
|
|
|
|
|
|
|
|
7776
|
0
|
|
|
|
|
0
|
my $ui = $o->{cmd}->{ui}; |
7777
|
0
|
|
|
|
|
0
|
my $sender = CDS::AccountToken->new($message->sender->store, $message->sender->publicKey->hash); |
7778
|
|
|
|
|
|
|
|
7779
|
0
|
|
|
|
|
0
|
$ui->space; |
7780
|
0
|
|
|
|
|
0
|
$ui->title($message->source->hash->hex); |
7781
|
0
|
|
|
|
|
0
|
$ui->line('from ', $o->{cmd}->{actor}->blueAccountReference($sender)); |
7782
|
0
|
|
|
|
|
0
|
$ui->line('for ', $o->{cmd}->{actor}->blueAccountReference($o->{accountToken})); |
7783
|
0
|
|
|
|
|
0
|
$ui->space; |
7784
|
0
|
|
|
|
|
0
|
$ui->recordChildren($message->content); |
7785
|
|
|
|
|
|
|
} |
7786
|
|
|
|
|
|
|
|
7787
|
|
|
|
|
|
|
sub onMessageBoxInvalidEntry { |
7788
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7789
|
0
|
0
|
0
|
|
|
0
|
my $source = shift; die 'wrong type '.ref($source).' for $source' if defined $source && ref $source ne 'CDS::Source'; |
|
0
|
|
|
|
|
0
|
|
7790
|
0
|
|
|
|
|
0
|
my $reason = shift; |
7791
|
|
|
|
|
|
|
|
7792
|
0
|
|
|
|
|
0
|
$o->{countInvalid} += 1; |
7793
|
0
|
|
|
|
|
0
|
$o->{cmd}->{countInvalid} += 1; |
7794
|
|
|
|
|
|
|
|
7795
|
0
|
|
|
|
|
0
|
my $ui = $o->{cmd}->{ui}; |
7796
|
0
|
|
|
|
|
0
|
my $hashHex = $source->hash->hex; |
7797
|
0
|
|
|
|
|
0
|
my $storeReference = $o->{cmd}->{actor}->storeReference($o->{accountToken}->cliStore); |
7798
|
|
|
|
|
|
|
|
7799
|
0
|
|
|
|
|
0
|
$ui->space; |
7800
|
0
|
|
|
|
|
0
|
$ui->title($hashHex); |
7801
|
0
|
|
|
|
|
0
|
$ui->pOrange($reason); |
7802
|
0
|
|
|
|
|
0
|
$ui->space; |
7803
|
0
|
|
|
|
|
0
|
$ui->p('You may use the following commands to check out the envelope:'); |
7804
|
0
|
|
|
|
|
0
|
$ui->line($ui->gold(' cds open envelope ', $hashHex, ' on ', $storeReference)); |
7805
|
0
|
|
|
|
|
0
|
$ui->line($ui->gold(' cds show record ', $hashHex, ' on ', $storeReference)); |
7806
|
0
|
|
|
|
|
0
|
$ui->line($ui->gold(' cds show hashes and data of ', $hashHex, ' on ', $storeReference)); |
7807
|
|
|
|
|
|
|
} |
7808
|
|
|
|
|
|
|
|
7809
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
7810
|
|
|
|
|
|
|
package CDS::Commands::ShowObject; |
7811
|
|
|
|
|
|
|
|
7812
|
|
|
|
|
|
|
sub register { |
7813
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
7814
|
0
|
|
|
|
|
0
|
my $cds = shift; |
7815
|
0
|
|
|
|
|
0
|
my $help = shift; |
7816
|
|
|
|
|
|
|
|
7817
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
7818
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
7819
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
7820
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
7821
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
7822
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(1); |
7823
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
7824
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
7825
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
7826
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
7827
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(1); |
7828
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
7829
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
7830
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&show}); |
7831
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 1, 0, 'show'); |
7832
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'show'); |
7833
|
0
|
|
|
|
|
0
|
$cds->addArrow($node003, 1, 0, 'show'); |
7834
|
0
|
|
|
|
|
0
|
$help->addArrow($node002, 1, 0, 'show'); |
7835
|
0
|
|
|
|
|
0
|
$node000->addArrow($node006, 1, 0, 'object', \&collectObject); |
7836
|
0
|
|
|
|
|
0
|
$node001->addArrow($node006, 1, 0, 'record', \&collectRecord); |
7837
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 1, 0, 'bytes'); |
7838
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 1, 0, 'data'); |
7839
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 1, 0, 'hash'); |
7840
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 1, 0, 'hashes'); |
7841
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 1, 0, 'object'); |
7842
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 1, 0, 'record'); |
7843
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 1, 0, 'size'); |
7844
|
0
|
|
|
|
|
0
|
$node003->addArrow($node005, 1, 0, 'bytes', \&collectBytes); |
7845
|
0
|
|
|
|
|
0
|
$node003->addArrow($node005, 1, 0, 'data', \&collectData); |
7846
|
0
|
|
|
|
|
0
|
$node003->addArrow($node005, 1, 0, 'hash', \&collectHash); |
7847
|
0
|
|
|
|
|
0
|
$node003->addArrow($node005, 1, 0, 'hashes', \&collectHashes); |
7848
|
0
|
|
|
|
|
0
|
$node003->addArrow($node005, 1, 0, 'record', \&collectRecord); |
7849
|
0
|
|
|
|
|
0
|
$node003->addArrow($node005, 1, 0, 'size', \&collectSize); |
7850
|
0
|
|
|
|
|
0
|
$node005->addArrow($node003, 1, 0, 'and'); |
7851
|
0
|
|
|
|
|
0
|
$node005->addArrow($node006, 1, 0, 'of'); |
7852
|
0
|
|
|
|
|
0
|
$node006->addArrow($node007, 1, 0, 'HASH', \&collectHash1); |
7853
|
0
|
|
|
|
|
0
|
$node006->addArrow($node010, 1, 1, 'FILE', \&collectFile); |
7854
|
0
|
|
|
|
|
0
|
$node006->addArrow($node010, 1, 0, 'HASH', \&collectHash2); |
7855
|
0
|
|
|
|
|
0
|
$node006->addArrow($node010, 1, 0, 'OBJECT', \&collectObject1); |
7856
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'on'); |
7857
|
0
|
|
|
|
|
0
|
$node007->addArrow($node009, 0, 0, 'from'); |
7858
|
0
|
|
|
|
|
0
|
$node008->addArrow($node010, 1, 0, 'STORE', \&collectStore); |
7859
|
0
|
|
|
|
|
0
|
$node009->addArrow($node010, 0, 0, 'STORE', \&collectStore); |
7860
|
0
|
|
|
|
|
0
|
$node010->addArrow($node011, 1, 0, 'decrypted'); |
7861
|
0
|
|
|
|
|
0
|
$node010->addDefault($node013); |
7862
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'with'); |
7863
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'AESKEY', \&collectAeskey); |
7864
|
|
|
|
|
|
|
} |
7865
|
|
|
|
|
|
|
|
7866
|
|
|
|
|
|
|
sub collectAeskey { |
7867
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7868
|
0
|
|
|
|
|
0
|
my $label = shift; |
7869
|
0
|
|
|
|
|
0
|
my $value = shift; |
7870
|
|
|
|
|
|
|
|
7871
|
0
|
|
|
|
|
0
|
$o->{aesKey} = $value; |
7872
|
|
|
|
|
|
|
} |
7873
|
|
|
|
|
|
|
|
7874
|
|
|
|
|
|
|
sub collectBytes { |
7875
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7876
|
0
|
|
|
|
|
0
|
my $label = shift; |
7877
|
0
|
|
|
|
|
0
|
my $value = shift; |
7878
|
|
|
|
|
|
|
|
7879
|
0
|
|
|
|
|
0
|
$o->{showBytes} = 1; |
7880
|
|
|
|
|
|
|
} |
7881
|
|
|
|
|
|
|
|
7882
|
|
|
|
|
|
|
sub collectData { |
7883
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7884
|
0
|
|
|
|
|
0
|
my $label = shift; |
7885
|
0
|
|
|
|
|
0
|
my $value = shift; |
7886
|
|
|
|
|
|
|
|
7887
|
0
|
|
|
|
|
0
|
$o->{showData} = 1; |
7888
|
|
|
|
|
|
|
} |
7889
|
|
|
|
|
|
|
|
7890
|
|
|
|
|
|
|
sub collectFile { |
7891
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7892
|
0
|
|
|
|
|
0
|
my $label = shift; |
7893
|
0
|
|
|
|
|
0
|
my $value = shift; |
7894
|
|
|
|
|
|
|
|
7895
|
0
|
|
|
|
|
0
|
$o->{file} = $value; |
7896
|
|
|
|
|
|
|
} |
7897
|
|
|
|
|
|
|
|
7898
|
|
|
|
|
|
|
sub collectHash { |
7899
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7900
|
0
|
|
|
|
|
0
|
my $label = shift; |
7901
|
0
|
|
|
|
|
0
|
my $value = shift; |
7902
|
|
|
|
|
|
|
|
7903
|
0
|
|
|
|
|
0
|
$o->{showHash} = 1; |
7904
|
|
|
|
|
|
|
} |
7905
|
|
|
|
|
|
|
|
7906
|
|
|
|
|
|
|
sub collectHash1 { |
7907
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7908
|
0
|
|
|
|
|
0
|
my $label = shift; |
7909
|
0
|
|
|
|
|
0
|
my $value = shift; |
7910
|
|
|
|
|
|
|
|
7911
|
0
|
|
|
|
|
0
|
$o->{hash} = $value; |
7912
|
|
|
|
|
|
|
} |
7913
|
|
|
|
|
|
|
|
7914
|
|
|
|
|
|
|
sub collectHash2 { |
7915
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7916
|
0
|
|
|
|
|
0
|
my $label = shift; |
7917
|
0
|
|
|
|
|
0
|
my $value = shift; |
7918
|
|
|
|
|
|
|
|
7919
|
0
|
|
|
|
|
0
|
$o->{hash} = $value; |
7920
|
0
|
|
|
|
|
0
|
$o->{store} = $o->{actor}->preferredStore; |
7921
|
|
|
|
|
|
|
} |
7922
|
|
|
|
|
|
|
|
7923
|
|
|
|
|
|
|
sub collectHashes { |
7924
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7925
|
0
|
|
|
|
|
0
|
my $label = shift; |
7926
|
0
|
|
|
|
|
0
|
my $value = shift; |
7927
|
|
|
|
|
|
|
|
7928
|
0
|
|
|
|
|
0
|
$o->{showHashes} = 1; |
7929
|
|
|
|
|
|
|
} |
7930
|
|
|
|
|
|
|
|
7931
|
|
|
|
|
|
|
sub collectObject { |
7932
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7933
|
0
|
|
|
|
|
0
|
my $label = shift; |
7934
|
0
|
|
|
|
|
0
|
my $value = shift; |
7935
|
|
|
|
|
|
|
|
7936
|
0
|
|
|
|
|
0
|
$o->{showHashes} = 1; |
7937
|
0
|
|
|
|
|
0
|
$o->{showData} = 1; |
7938
|
|
|
|
|
|
|
} |
7939
|
|
|
|
|
|
|
|
7940
|
|
|
|
|
|
|
sub collectObject1 { |
7941
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7942
|
0
|
|
|
|
|
0
|
my $label = shift; |
7943
|
0
|
|
|
|
|
0
|
my $value = shift; |
7944
|
|
|
|
|
|
|
|
7945
|
0
|
|
|
|
|
0
|
$o->{hash} = $value->hash; |
7946
|
0
|
|
|
|
|
0
|
$o->{store} = $value->cliStore; |
7947
|
|
|
|
|
|
|
} |
7948
|
|
|
|
|
|
|
|
7949
|
|
|
|
|
|
|
sub collectRecord { |
7950
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7951
|
0
|
|
|
|
|
0
|
my $label = shift; |
7952
|
0
|
|
|
|
|
0
|
my $value = shift; |
7953
|
|
|
|
|
|
|
|
7954
|
0
|
|
|
|
|
0
|
$o->{showRecord} = 1; |
7955
|
|
|
|
|
|
|
} |
7956
|
|
|
|
|
|
|
|
7957
|
|
|
|
|
|
|
sub collectSize { |
7958
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7959
|
0
|
|
|
|
|
0
|
my $label = shift; |
7960
|
0
|
|
|
|
|
0
|
my $value = shift; |
7961
|
|
|
|
|
|
|
|
7962
|
0
|
|
|
|
|
0
|
$o->{showSize} = 1; |
7963
|
|
|
|
|
|
|
} |
7964
|
|
|
|
|
|
|
|
7965
|
|
|
|
|
|
|
sub collectStore { |
7966
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7967
|
0
|
|
|
|
|
0
|
my $label = shift; |
7968
|
0
|
|
|
|
|
0
|
my $value = shift; |
7969
|
|
|
|
|
|
|
|
7970
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
7971
|
|
|
|
|
|
|
} |
7972
|
|
|
|
|
|
|
|
7973
|
|
|
|
|
|
|
sub new { |
7974
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
7975
|
0
|
|
|
|
|
0
|
my $actor = shift; |
7976
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
7977
|
|
|
|
|
|
|
|
7978
|
|
|
|
|
|
|
# END AUTOGENERATED |
7979
|
|
|
|
|
|
|
|
7980
|
|
|
|
|
|
|
# HTML FOLDER NAME show-object |
7981
|
|
|
|
|
|
|
# HTML TITLE Show objects |
7982
|
|
|
|
|
|
|
sub help { |
7983
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
7984
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
7985
|
|
|
|
|
|
|
|
7986
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
7987
|
0
|
|
|
|
|
0
|
$ui->space; |
7988
|
0
|
|
|
|
|
0
|
$ui->command('cds show record OBJECT'); |
7989
|
0
|
|
|
|
|
0
|
$ui->command('cds show record HASH on STORE'); |
7990
|
0
|
|
|
|
|
0
|
$ui->p('Downloads an object, and shows the containing record. The stores are tried in the order they are indicated, until one succeeds. If the object is not found, or not a valid Condensation object, the program quits with exit code 1.'); |
7991
|
0
|
|
|
|
|
0
|
$ui->space; |
7992
|
0
|
|
|
|
|
0
|
$ui->line('The following object properties can be displayed:'); |
7993
|
0
|
|
|
|
|
0
|
$ui->line(' cds show hash of …'); |
7994
|
0
|
|
|
|
|
0
|
$ui->line(' cds show size of …'); |
7995
|
0
|
|
|
|
|
0
|
$ui->line(' cds show bytes of …'); |
7996
|
0
|
|
|
|
|
0
|
$ui->line(' cds show hashes of …'); |
7997
|
0
|
|
|
|
|
0
|
$ui->line(' cds show data of …'); |
7998
|
0
|
|
|
|
|
0
|
$ui->line(' cds show record …'); |
7999
|
0
|
|
|
|
|
0
|
$ui->space; |
8000
|
0
|
|
|
|
|
0
|
$ui->p('Multiple properties may be combined with "and", e.g.:'); |
8001
|
0
|
|
|
|
|
0
|
$ui->line(' cds show size and hashes and record of …'); |
8002
|
0
|
|
|
|
|
0
|
$ui->space; |
8003
|
0
|
|
|
|
|
0
|
$ui->command('cds show record HASH'); |
8004
|
0
|
|
|
|
|
0
|
$ui->p('As above, but uses the selected store.'); |
8005
|
0
|
|
|
|
|
0
|
$ui->space; |
8006
|
0
|
|
|
|
|
0
|
$ui->command('cds show record FILE'); |
8007
|
0
|
|
|
|
|
0
|
$ui->p('As above, but loads the object from FILE rather than from an object store.'); |
8008
|
0
|
|
|
|
|
0
|
$ui->space; |
8009
|
0
|
|
|
|
|
0
|
$ui->command('… decrypted with AESKEY'); |
8010
|
0
|
|
|
|
|
0
|
$ui->p('Decrypts the object after retrieval.'); |
8011
|
0
|
|
|
|
|
0
|
$ui->space; |
8012
|
0
|
|
|
|
|
0
|
$ui->command('cds show object …'); |
8013
|
0
|
|
|
|
|
0
|
$ui->p('A shortcut for "cds show hashes and data of …".'); |
8014
|
0
|
|
|
|
|
0
|
$ui->space; |
8015
|
0
|
|
|
|
|
0
|
$ui->title('Related commands'); |
8016
|
0
|
|
|
|
|
0
|
$ui->line('cds get OBJECT [decrypted with AESKEY]'); |
8017
|
0
|
|
|
|
|
0
|
$ui->line('cds save [data of] OBJECT [decrypted with AESKEY] as FILE'); |
8018
|
0
|
|
|
|
|
0
|
$ui->line('cds open envelope OBJECT [on STORE] [using KEYPAIR]'); |
8019
|
0
|
|
|
|
|
0
|
$ui->line('cds show document OBJECT [on STORE]'); |
8020
|
0
|
|
|
|
|
0
|
$ui->space; |
8021
|
|
|
|
|
|
|
} |
8022
|
|
|
|
|
|
|
|
8023
|
|
|
|
|
|
|
sub show { |
8024
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8025
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8026
|
|
|
|
|
|
|
|
8027
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
8028
|
|
|
|
|
|
|
|
8029
|
|
|
|
|
|
|
# Get and decrypt the object |
8030
|
0
|
0
|
|
|
|
0
|
$o->{object} = defined $o->{file} ? $o->loadObjectFromFile : $o->loadObjectFromStore; |
8031
|
0
|
0
|
|
|
|
0
|
return if ! $o->{object}; |
8032
|
0
|
0
|
|
|
|
0
|
$o->{object} = $o->{object}->crypt($o->{aesKey}) if defined $o->{aesKey}; |
8033
|
|
|
|
|
|
|
|
8034
|
|
|
|
|
|
|
# Show the desired information |
8035
|
0
|
0
|
|
|
|
0
|
$o->showHash if $o->{showHash}; |
8036
|
0
|
0
|
|
|
|
0
|
$o->showSize if $o->{showSize}; |
8037
|
0
|
0
|
|
|
|
0
|
$o->showBytes if $o->{showBytes}; |
8038
|
0
|
0
|
|
|
|
0
|
$o->showHashes if $o->{showHashes}; |
8039
|
0
|
0
|
|
|
|
0
|
$o->showData if $o->{showData}; |
8040
|
0
|
0
|
|
|
|
0
|
$o->showRecord if $o->{showRecord}; |
8041
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8042
|
|
|
|
|
|
|
} |
8043
|
|
|
|
|
|
|
|
8044
|
|
|
|
|
|
|
sub loadObjectFromFile { |
8045
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8046
|
|
|
|
|
|
|
|
8047
|
0
|
|
0
|
|
|
0
|
my $bytes = CDS->readBytesFromFile($o->{file}) // return $o->{ui}->error('Unable to read "', $o->{file}, '".'); |
8048
|
0
|
|
0
|
|
|
0
|
return CDS::Object->fromBytes($bytes) // return $o->{ui}->error('"', $o->{file}, '" does not contain a valid Condensation object.'); |
8049
|
|
|
|
|
|
|
} |
8050
|
|
|
|
|
|
|
|
8051
|
|
|
|
|
|
|
sub loadObjectFromStore { |
8052
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8053
|
|
|
|
|
|
|
|
8054
|
0
|
|
|
|
|
0
|
return $o->{actor}->uiGetObject($o->{hash}, $o->{store}, $o->{actor}->preferredKeyPairToken); |
8055
|
|
|
|
|
|
|
} |
8056
|
|
|
|
|
|
|
|
8057
|
|
|
|
|
|
|
sub loadCommand { |
8058
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8059
|
|
|
|
|
|
|
|
8060
|
0
|
0
|
|
|
|
0
|
my $decryption = defined $o->{aesKey} ? ' decrypted with '.unpack('H*', $o->{aesKey}) : ''; |
8061
|
0
|
0
|
|
|
|
0
|
return $o->{file}.$decryption if defined $o->{file}; |
8062
|
0
|
|
|
|
|
0
|
return $o->{hash}->hex.' on '.$o->{actor}->storeReference($o->{store}).$decryption; |
8063
|
|
|
|
|
|
|
} |
8064
|
|
|
|
|
|
|
|
8065
|
|
|
|
|
|
|
sub showHash { |
8066
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8067
|
|
|
|
|
|
|
|
8068
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8069
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Object hash'); |
8070
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{object}->calculateHash->hex); |
8071
|
|
|
|
|
|
|
} |
8072
|
|
|
|
|
|
|
|
8073
|
|
|
|
|
|
|
sub showSize { |
8074
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8075
|
|
|
|
|
|
|
|
8076
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8077
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Object size'); |
8078
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->niceFileSize(length $o->{object}->bytes), ' total (', length $o->{object}->bytes, ' bytes)'); |
8079
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{object}->hashesCount, ' hashes (', length $o->{object}->header, ' bytes)'); |
8080
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->niceFileSize(length $o->{object}->data), ' data (', length $o->{object}->data, ' bytes)'); |
8081
|
|
|
|
|
|
|
} |
8082
|
|
|
|
|
|
|
|
8083
|
|
|
|
|
|
|
sub showBytes { |
8084
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8085
|
|
|
|
|
|
|
|
8086
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8087
|
0
|
|
|
|
|
0
|
my $bytes = $o->{object}->bytes; |
8088
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Object bytes (', $o->{ui}->niceFileSize(length $bytes), ')'); |
8089
|
0
|
0
|
|
|
|
0
|
return if ! length $bytes; |
8090
|
|
|
|
|
|
|
|
8091
|
0
|
|
|
|
|
0
|
my $hexDump = $o->{ui}->hexDump($bytes); |
8092
|
0
|
|
|
|
|
0
|
my $dataStart = $hexDump->styleHashList(0); |
8093
|
0
|
0
|
|
|
|
0
|
my $end = $dataStart ? $hexDump->styleRecord($dataStart) : 0; |
8094
|
0
|
|
|
|
|
0
|
$hexDump->changeStyle({at => $end, style => $hexDump->reset}); |
8095
|
0
|
|
|
|
|
0
|
$hexDump->display; |
8096
|
|
|
|
|
|
|
} |
8097
|
|
|
|
|
|
|
|
8098
|
|
|
|
|
|
|
sub showHashes { |
8099
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8100
|
|
|
|
|
|
|
|
8101
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8102
|
0
|
|
|
|
|
0
|
my $hashesCount = $o->{object}->hashesCount; |
8103
|
0
|
0
|
|
|
|
0
|
$o->{ui}->title($hashesCount == 1 ? '1 hash' : $hashesCount.' hashes'); |
8104
|
0
|
|
|
|
|
0
|
my $count = 0; |
8105
|
0
|
|
|
|
|
0
|
for my $hash ($o->{object}->hashes) { |
8106
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->violet(unpack('H4', pack('S>', $count))), ' ', $hash->hex); |
8107
|
0
|
|
|
|
|
0
|
$count += 1; |
8108
|
|
|
|
|
|
|
} |
8109
|
|
|
|
|
|
|
} |
8110
|
|
|
|
|
|
|
|
8111
|
|
|
|
|
|
|
sub showData { |
8112
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8113
|
|
|
|
|
|
|
|
8114
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8115
|
0
|
|
|
|
|
0
|
my $data = $o->{object}->data; |
8116
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Data (', $o->{ui}->niceFileSize(length $data), ')'); |
8117
|
0
|
0
|
|
|
|
0
|
return if ! length $data; |
8118
|
|
|
|
|
|
|
|
8119
|
0
|
|
|
|
|
0
|
my $hexDump = $o->{ui}->hexDump($data); |
8120
|
0
|
|
|
|
|
0
|
my $end = $hexDump->styleRecord(0); |
8121
|
0
|
|
|
|
|
0
|
$hexDump->changeStyle({at => $end, style => $hexDump->reset}); |
8122
|
0
|
|
|
|
|
0
|
$hexDump->display; |
8123
|
|
|
|
|
|
|
} |
8124
|
|
|
|
|
|
|
|
8125
|
|
|
|
|
|
|
sub showRecord { |
8126
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8127
|
|
|
|
|
|
|
|
8128
|
|
|
|
|
|
|
# Title |
8129
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8130
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Data interpreted as record'); |
8131
|
|
|
|
|
|
|
|
8132
|
|
|
|
|
|
|
# Empty object (empty record) |
8133
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->line($o->{ui}->gray('(empty record)')) if ! length $o->{object}->data; |
8134
|
|
|
|
|
|
|
|
8135
|
|
|
|
|
|
|
# Record |
8136
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->new; |
8137
|
0
|
|
|
|
|
0
|
my $reader = CDS::RecordReader->new($o->{object}); |
8138
|
0
|
|
|
|
|
0
|
$reader->readChildren($record); |
8139
|
0
|
0
|
|
|
|
0
|
if ($reader->hasError) { |
8140
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('This is not a record.'); |
8141
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8142
|
0
|
|
|
|
|
0
|
$o->{ui}->p('You may use one of the following commands to check out the content:'); |
8143
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold(' cds show object ', $o->loadCommand)); |
8144
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold(' cds show data of ', $o->loadCommand)); |
8145
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gold(' cds save data of ', $o->loadCommand, ' as FILENAME')); |
8146
|
0
|
|
|
|
|
0
|
return; |
8147
|
|
|
|
|
|
|
} |
8148
|
|
|
|
|
|
|
|
8149
|
0
|
0
|
|
|
|
0
|
$o->{ui}->recordChildren($record, $o->{store} ? $o->{actor}->blueStoreReference($o->{store}) : ''); |
8150
|
|
|
|
|
|
|
|
8151
|
|
|
|
|
|
|
# Trailer |
8152
|
0
|
|
|
|
|
0
|
my $trailer = $reader->trailer; |
8153
|
0
|
0
|
|
|
|
0
|
if (length $trailer) { |
8154
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8155
|
0
|
|
|
|
|
0
|
$o->{ui}->pRed('This is probably not a record, because ', length $trailer, ' bytes remain behind the record. Use "cds show data of …" to investigate the raw object content. If this object is encrypted, provide the decryption key using "… and decrypted with KEY".'); |
8156
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8157
|
|
|
|
|
|
|
} |
8158
|
|
|
|
|
|
|
} |
8159
|
|
|
|
|
|
|
|
8160
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
8161
|
|
|
|
|
|
|
package CDS::Commands::ShowPrivateData; |
8162
|
|
|
|
|
|
|
|
8163
|
|
|
|
|
|
|
sub register { |
8164
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
8165
|
0
|
|
|
|
|
0
|
my $cds = shift; |
8166
|
0
|
|
|
|
|
0
|
my $help = shift; |
8167
|
|
|
|
|
|
|
|
8168
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
8169
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
8170
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
8171
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
8172
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
8173
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
8174
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
8175
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
8176
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
8177
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
8178
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
8179
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
8180
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
8181
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showGroupData}); |
8182
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showLocalData}); |
8183
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showSentList}); |
8184
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(0); |
8185
|
0
|
|
|
|
|
0
|
my $node017 = CDS::Parser::Node->new(0); |
8186
|
0
|
|
|
|
|
0
|
my $node018 = CDS::Parser::Node->new(0); |
8187
|
0
|
|
|
|
|
0
|
my $node019 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showSentList}); |
8188
|
0
|
|
|
|
|
0
|
$cds->addArrow($node006, 1, 0, 'show'); |
8189
|
0
|
|
|
|
|
0
|
$cds->addArrow($node007, 1, 0, 'show'); |
8190
|
0
|
|
|
|
|
0
|
$cds->addArrow($node008, 1, 0, 'show'); |
8191
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'show'); |
8192
|
0
|
|
|
|
|
0
|
$help->addArrow($node001, 1, 0, 'show'); |
8193
|
0
|
|
|
|
|
0
|
$help->addArrow($node002, 1, 0, 'show'); |
8194
|
0
|
|
|
|
|
0
|
$node000->addArrow($node003, 1, 0, 'group'); |
8195
|
0
|
|
|
|
|
0
|
$node001->addArrow($node004, 1, 0, 'local'); |
8196
|
0
|
|
|
|
|
0
|
$node002->addArrow($node005, 1, 0, 'sent'); |
8197
|
0
|
|
|
|
|
0
|
$node003->addArrow($node012, 1, 0, 'data'); |
8198
|
0
|
|
|
|
|
0
|
$node004->addArrow($node012, 1, 0, 'data'); |
8199
|
0
|
|
|
|
|
0
|
$node005->addArrow($node012, 1, 0, 'list'); |
8200
|
0
|
|
|
|
|
0
|
$node006->addArrow($node009, 1, 0, 'group'); |
8201
|
0
|
|
|
|
|
0
|
$node007->addArrow($node010, 1, 0, 'local'); |
8202
|
0
|
|
|
|
|
0
|
$node008->addArrow($node011, 1, 0, 'sent'); |
8203
|
0
|
|
|
|
|
0
|
$node009->addArrow($node013, 1, 0, 'data'); |
8204
|
0
|
|
|
|
|
0
|
$node010->addArrow($node014, 1, 0, 'data'); |
8205
|
0
|
|
|
|
|
0
|
$node011->addArrow($node015, 1, 0, 'list'); |
8206
|
0
|
|
|
|
|
0
|
$node015->addArrow($node016, 1, 0, 'ordered'); |
8207
|
0
|
|
|
|
|
0
|
$node016->addArrow($node017, 1, 0, 'by'); |
8208
|
0
|
|
|
|
|
0
|
$node017->addArrow($node018, 1, 0, 'envelope'); |
8209
|
0
|
|
|
|
|
0
|
$node017->addArrow($node019, 1, 0, 'date', \&collectDate); |
8210
|
0
|
|
|
|
|
0
|
$node017->addArrow($node019, 1, 0, 'id', \&collectId); |
8211
|
0
|
|
|
|
|
0
|
$node018->addArrow($node019, 1, 0, 'hash', \&collectHash); |
8212
|
|
|
|
|
|
|
} |
8213
|
|
|
|
|
|
|
|
8214
|
|
|
|
|
|
|
sub collectDate { |
8215
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8216
|
0
|
|
|
|
|
0
|
my $label = shift; |
8217
|
0
|
|
|
|
|
0
|
my $value = shift; |
8218
|
|
|
|
|
|
|
|
8219
|
0
|
|
|
|
|
0
|
$o->{orderedBy} = 'date'; |
8220
|
|
|
|
|
|
|
} |
8221
|
|
|
|
|
|
|
|
8222
|
|
|
|
|
|
|
sub collectHash { |
8223
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8224
|
0
|
|
|
|
|
0
|
my $label = shift; |
8225
|
0
|
|
|
|
|
0
|
my $value = shift; |
8226
|
|
|
|
|
|
|
|
8227
|
0
|
|
|
|
|
0
|
$o->{orderedBy} = 'envelope hash'; |
8228
|
|
|
|
|
|
|
} |
8229
|
|
|
|
|
|
|
|
8230
|
|
|
|
|
|
|
sub collectId { |
8231
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8232
|
0
|
|
|
|
|
0
|
my $label = shift; |
8233
|
0
|
|
|
|
|
0
|
my $value = shift; |
8234
|
|
|
|
|
|
|
|
8235
|
0
|
|
|
|
|
0
|
$o->{orderedBy} = 'id'; |
8236
|
|
|
|
|
|
|
} |
8237
|
|
|
|
|
|
|
|
8238
|
|
|
|
|
|
|
sub new { |
8239
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
8240
|
0
|
|
|
|
|
0
|
my $actor = shift; |
8241
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
8242
|
|
|
|
|
|
|
|
8243
|
|
|
|
|
|
|
# END AUTOGENERATED |
8244
|
|
|
|
|
|
|
|
8245
|
|
|
|
|
|
|
# HTML FOLDER NAME show-private-data |
8246
|
|
|
|
|
|
|
# HTML TITLE Show the private data |
8247
|
|
|
|
|
|
|
sub help { |
8248
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8249
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8250
|
|
|
|
|
|
|
|
8251
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
8252
|
0
|
|
|
|
|
0
|
$ui->space; |
8253
|
0
|
|
|
|
|
0
|
$ui->command('cds show group data'); |
8254
|
0
|
|
|
|
|
0
|
$ui->p('Shows the group document. This document is shared among all group members.'); |
8255
|
0
|
|
|
|
|
0
|
$ui->space; |
8256
|
0
|
|
|
|
|
0
|
$ui->command('cds show local data'); |
8257
|
0
|
|
|
|
|
0
|
$ui->p('Shows the local document. This document is stored locally, and private to this actor.'); |
8258
|
0
|
|
|
|
|
0
|
$ui->space; |
8259
|
0
|
|
|
|
|
0
|
$ui->command('cds show sent list'); |
8260
|
0
|
|
|
|
|
0
|
$ui->p('Shows the list of sent messages with their expiry date, envelope hash, and content hash.'); |
8261
|
0
|
|
|
|
|
0
|
$ui->space; |
8262
|
0
|
|
|
|
|
0
|
$ui->command('… ordered by id'); |
8263
|
0
|
|
|
|
|
0
|
$ui->command('… ordered by date'); |
8264
|
0
|
|
|
|
|
0
|
$ui->command('… ordered by envelope hash'); |
8265
|
0
|
|
|
|
|
0
|
$ui->p('Sorts the list accordingly. By default, the list is sorted by id.'); |
8266
|
0
|
|
|
|
|
0
|
$ui->space; |
8267
|
|
|
|
|
|
|
} |
8268
|
|
|
|
|
|
|
|
8269
|
|
|
|
|
|
|
sub showGroupData { |
8270
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8271
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8272
|
|
|
|
|
|
|
|
8273
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8274
|
0
|
|
|
|
|
0
|
$o->{ui}->selector($o->{actor}->groupRoot, 'Group data'); |
8275
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8276
|
|
|
|
|
|
|
} |
8277
|
|
|
|
|
|
|
|
8278
|
|
|
|
|
|
|
sub showLocalData { |
8279
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8280
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8281
|
|
|
|
|
|
|
|
8282
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8283
|
0
|
|
|
|
|
0
|
$o->{ui}->selector($o->{actor}->localRoot, 'Local data'); |
8284
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8285
|
|
|
|
|
|
|
} |
8286
|
|
|
|
|
|
|
|
8287
|
|
|
|
|
|
|
sub showSentList { |
8288
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8289
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8290
|
|
|
|
|
|
|
|
8291
|
0
|
|
|
|
|
0
|
$o->{orderedBy} = 'id'; |
8292
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
8293
|
|
|
|
|
|
|
|
8294
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8295
|
0
|
|
|
|
|
0
|
$o->{ui}->title('Sent list'); |
8296
|
|
|
|
|
|
|
|
8297
|
0
|
|
0
|
|
|
0
|
$o->{actor}->procureSentList // return; |
8298
|
0
|
|
|
|
|
0
|
my $sentList = $o->{actor}->sentList; |
8299
|
0
|
|
|
|
|
0
|
my @items = sort { $a->id cmp $b->id } values %{$sentList->{items}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
8300
|
0
|
0
|
|
|
|
0
|
@items = sort { $a->envelopeHashBytes cmp $b->envelopeHashBytes } @items if $o->{orderedBy} eq 'envelope hash'; |
|
0
|
|
|
|
|
0
|
|
8301
|
0
|
0
|
|
|
|
0
|
@items = sort { $a->validUntil <=> $b->validUntil } @items if $o->{orderedBy} eq 'date'; |
|
0
|
|
|
|
|
0
|
|
8302
|
0
|
|
|
|
|
0
|
my $noHash = '-' x 64; |
8303
|
0
|
|
|
|
|
0
|
for my $item (@items) { |
8304
|
0
|
|
|
|
|
0
|
my $id = $item->id; |
8305
|
0
|
|
|
|
|
0
|
my $envelopeHash = $item->envelopeHash; |
8306
|
0
|
|
|
|
|
0
|
my $message = $item->message; |
8307
|
0
|
|
|
|
|
0
|
my $label = $o->{ui}->niceBytes($id, 32); |
8308
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray($o->{ui}->niceDateTimeLocal($item->validUntil)), ' ', $envelopeHash ? $envelopeHash->hex : $noHash, ' ', $o->{ui}->blue($label)); |
8309
|
0
|
|
|
|
|
0
|
$o->{ui}->recordChildren($message); |
8310
|
|
|
|
|
|
|
} |
8311
|
|
|
|
|
|
|
|
8312
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8313
|
|
|
|
|
|
|
} |
8314
|
|
|
|
|
|
|
|
8315
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
8316
|
|
|
|
|
|
|
package CDS::Commands::ShowTree; |
8317
|
|
|
|
|
|
|
|
8318
|
|
|
|
|
|
|
sub register { |
8319
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
8320
|
0
|
|
|
|
|
0
|
my $cds = shift; |
8321
|
0
|
|
|
|
|
0
|
my $help = shift; |
8322
|
|
|
|
|
|
|
|
8323
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
8324
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
8325
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
8326
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
8327
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
8328
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
8329
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
8330
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
8331
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
8332
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
8333
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&showTree}); |
8334
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'show'); |
8335
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 0, 0, 'show'); |
8336
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'show'); |
8337
|
0
|
|
|
|
|
0
|
$node000->addArrow($node003, 1, 0, 'tree'); |
8338
|
0
|
|
|
|
|
0
|
$node001->addArrow($node004, 1, 0, 'tree'); |
8339
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 0, 0, 'trees'); |
8340
|
0
|
|
|
|
|
0
|
$node004->addDefault($node005); |
8341
|
0
|
|
|
|
|
0
|
$node004->addDefault($node006); |
8342
|
0
|
|
|
|
|
0
|
$node004->addDefault($node007); |
8343
|
0
|
|
|
|
|
0
|
$node005->addArrow($node005, 1, 0, 'HASH', \&collectHash); |
8344
|
0
|
|
|
|
|
0
|
$node005->addArrow($node010, 1, 0, 'HASH', \&collectHash); |
8345
|
0
|
|
|
|
|
0
|
$node006->addArrow($node006, 1, 0, 'HASH', \&collectHash); |
8346
|
0
|
|
|
|
|
0
|
$node006->addArrow($node008, 1, 0, 'HASH', \&collectHash); |
8347
|
0
|
|
|
|
|
0
|
$node007->addArrow($node007, 1, 0, 'OBJECT', \&collectObject); |
8348
|
0
|
|
|
|
|
0
|
$node007->addArrow($node010, 1, 0, 'OBJECT', \&collectObject); |
8349
|
0
|
|
|
|
|
0
|
$node008->addArrow($node009, 1, 0, 'on'); |
8350
|
0
|
|
|
|
|
0
|
$node009->addArrow($node010, 1, 0, 'STORE', \&collectStore); |
8351
|
|
|
|
|
|
|
} |
8352
|
|
|
|
|
|
|
|
8353
|
|
|
|
|
|
|
sub collectHash { |
8354
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8355
|
0
|
|
|
|
|
0
|
my $label = shift; |
8356
|
0
|
|
|
|
|
0
|
my $value = shift; |
8357
|
|
|
|
|
|
|
|
8358
|
0
|
|
|
|
|
0
|
push @{$o->{hashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
8359
|
|
|
|
|
|
|
} |
8360
|
|
|
|
|
|
|
|
8361
|
|
|
|
|
|
|
sub collectObject { |
8362
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8363
|
0
|
|
|
|
|
0
|
my $label = shift; |
8364
|
0
|
|
|
|
|
0
|
my $value = shift; |
8365
|
|
|
|
|
|
|
|
8366
|
0
|
|
|
|
|
0
|
push @{$o->{objectTokens}}, $value; |
|
0
|
|
|
|
|
0
|
|
8367
|
|
|
|
|
|
|
} |
8368
|
|
|
|
|
|
|
|
8369
|
|
|
|
|
|
|
sub collectStore { |
8370
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8371
|
0
|
|
|
|
|
0
|
my $label = shift; |
8372
|
0
|
|
|
|
|
0
|
my $value = shift; |
8373
|
|
|
|
|
|
|
|
8374
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
8375
|
|
|
|
|
|
|
} |
8376
|
|
|
|
|
|
|
|
8377
|
|
|
|
|
|
|
sub new { |
8378
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
8379
|
0
|
|
|
|
|
0
|
my $actor = shift; |
8380
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
8381
|
|
|
|
|
|
|
|
8382
|
|
|
|
|
|
|
# END AUTOGENERATED |
8383
|
|
|
|
|
|
|
|
8384
|
|
|
|
|
|
|
# HTML FOLDER NAME show-tree |
8385
|
|
|
|
|
|
|
# HTML TITLE Show trees |
8386
|
|
|
|
|
|
|
sub help { |
8387
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8388
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8389
|
|
|
|
|
|
|
|
8390
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
8391
|
0
|
|
|
|
|
0
|
$ui->space; |
8392
|
0
|
|
|
|
|
0
|
$ui->command('cds show tree OBJECT*'); |
8393
|
0
|
|
|
|
|
0
|
$ui->command('cds show tree HASH* on STORE'); |
8394
|
0
|
|
|
|
|
0
|
$ui->p('Downloads a tree, and shows the tree hierarchy. If an object has been traversed before, it is listed as "reported above".'); |
8395
|
0
|
|
|
|
|
0
|
$ui->space; |
8396
|
0
|
|
|
|
|
0
|
$ui->command('cds show tree HASH*'); |
8397
|
0
|
|
|
|
|
0
|
$ui->p('As above, but uses the selected store.'); |
8398
|
0
|
|
|
|
|
0
|
$ui->space; |
8399
|
|
|
|
|
|
|
} |
8400
|
|
|
|
|
|
|
|
8401
|
|
|
|
|
|
|
sub showTree { |
8402
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8403
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8404
|
|
|
|
|
|
|
|
8405
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken; |
8406
|
0
|
|
|
|
|
0
|
$o->{objectTokens} = []; |
8407
|
0
|
|
|
|
|
0
|
$o->{hashes} = []; |
8408
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
8409
|
|
|
|
|
|
|
|
8410
|
|
|
|
|
|
|
# Process all trees |
8411
|
0
|
|
|
|
|
0
|
for my $objectToken (@{$o->{objectTokens}}) { |
|
0
|
|
|
|
|
0
|
|
8412
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8413
|
0
|
|
|
|
|
0
|
$o->process($objectToken->hash, $objectToken->cliStore); |
8414
|
|
|
|
|
|
|
} |
8415
|
|
|
|
|
|
|
|
8416
|
0
|
0
|
|
|
|
0
|
if (scalar @{$o->{hashes}}) { |
|
0
|
|
|
|
|
0
|
|
8417
|
0
|
|
0
|
|
|
0
|
my $store = $o->{store} // $o->{actor}->preferredStore; |
8418
|
0
|
|
|
|
|
0
|
for my $hash (@{$o->{hashes}}) { |
|
0
|
|
|
|
|
0
|
|
8419
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8420
|
0
|
|
|
|
|
0
|
$o->process($hash, $store); |
8421
|
|
|
|
|
|
|
} |
8422
|
|
|
|
|
|
|
} |
8423
|
|
|
|
|
|
|
|
8424
|
|
|
|
|
|
|
# Report the total size |
8425
|
0
|
|
|
|
|
0
|
my $totalSize = 0; |
8426
|
0
|
|
|
|
|
0
|
my $totalDataSize = 0; |
8427
|
0
|
|
|
|
|
0
|
map { $totalSize += $_->{size} ; $totalDataSize += $_->{dataSize} } values %{$o->{objects}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
8428
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8429
|
0
|
|
|
|
|
0
|
$o->{ui}->p(scalar keys %{$o->{objects}}, ' unique objects ', $o->{ui}->bold($o->{ui}->niceFileSize($totalSize)), $o->{ui}->gray(' (', $o->{ui}->niceFileSize($totalSize - $totalDataSize), ' header and ', $o->{ui}->niceFileSize($totalDataSize), ' data)')); |
|
0
|
|
|
|
|
0
|
|
8430
|
0
|
0
|
|
|
|
0
|
$o->{ui}->pRed(scalar keys %{$o->{missingObjects}}, ' or more objects are missing') if scalar keys %{$o->{missingObjects}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
8431
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8432
|
|
|
|
|
|
|
} |
8433
|
|
|
|
|
|
|
|
8434
|
|
|
|
|
|
|
sub process { |
8435
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8436
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
8437
|
0
|
|
|
|
|
0
|
my $store = shift; |
8438
|
|
|
|
|
|
|
|
8439
|
0
|
|
|
|
|
0
|
my $hashHex = $hash->hex; |
8440
|
|
|
|
|
|
|
|
8441
|
|
|
|
|
|
|
# Check if we retrieved this object before |
8442
|
0
|
0
|
|
|
|
0
|
if (exists $o->{objects}->{$hashHex}) { |
8443
|
0
|
|
|
|
|
0
|
$o->{ui}->line($hash->hex, ' reported above') ; |
8444
|
0
|
|
|
|
|
0
|
return 1; |
8445
|
|
|
|
|
|
|
} |
8446
|
|
|
|
|
|
|
|
8447
|
|
|
|
|
|
|
# Retrieve the object |
8448
|
0
|
|
|
|
|
0
|
my ($object, $storeError) = $store->get($hash, $o->{keyPairToken}->keyPair); |
8449
|
0
|
0
|
|
|
|
0
|
return if defined $storeError; |
8450
|
|
|
|
|
|
|
|
8451
|
0
|
0
|
|
|
|
0
|
if (! $object) { |
8452
|
0
|
|
|
|
|
0
|
$o->{missingObjects}->{$hashHex} = 1; |
8453
|
0
|
|
|
|
|
0
|
return $o->{ui}->line($hashHex, ' ', $o->{ui}->red('is missing')); |
8454
|
|
|
|
|
|
|
} |
8455
|
|
|
|
|
|
|
|
8456
|
|
|
|
|
|
|
# Display |
8457
|
0
|
|
|
|
|
0
|
my $size = $object->byteLength; |
8458
|
0
|
|
|
|
|
0
|
$o->{objects}->{$hashHex} = {size => $size, dataSize => length $object->data}; |
8459
|
0
|
|
|
|
|
0
|
$o->{ui}->line($hashHex, ' ', $o->{ui}->bold($o->{ui}->niceFileSize($size)), ' ', $o->{ui}->gray($object->hashesCount, ' hashes')); |
8460
|
|
|
|
|
|
|
|
8461
|
|
|
|
|
|
|
# Process all children |
8462
|
0
|
|
|
|
|
0
|
$o->{ui}->pushIndent; |
8463
|
0
|
|
|
|
|
0
|
foreach my $hash ($object->hashes) { |
8464
|
0
|
|
0
|
|
|
0
|
$o->process($hash, $store) // return; |
8465
|
|
|
|
|
|
|
} |
8466
|
0
|
|
|
|
|
0
|
$o->{ui}->popIndent; |
8467
|
0
|
|
|
|
|
0
|
return 1; |
8468
|
|
|
|
|
|
|
} |
8469
|
|
|
|
|
|
|
|
8470
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
8471
|
|
|
|
|
|
|
package CDS::Commands::StartHTTPServer; |
8472
|
|
|
|
|
|
|
|
8473
|
|
|
|
|
|
|
sub register { |
8474
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
8475
|
0
|
|
|
|
|
0
|
my $cds = shift; |
8476
|
0
|
|
|
|
|
0
|
my $help = shift; |
8477
|
|
|
|
|
|
|
|
8478
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
8479
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
8480
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
8481
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
8482
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
8483
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
8484
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
8485
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
8486
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
8487
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(1); |
8488
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
8489
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(1); |
8490
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
8491
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
8492
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(0); |
8493
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(0); |
8494
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(1); |
8495
|
0
|
|
|
|
|
0
|
my $node017 = CDS::Parser::Node->new(0); |
8496
|
0
|
|
|
|
|
0
|
my $node018 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&startHttpServer}); |
8497
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'start'); |
8498
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'http'); |
8499
|
0
|
|
|
|
|
0
|
$node000->addArrow($node005, 1, 0, 'server'); |
8500
|
0
|
|
|
|
|
0
|
$node001->addArrow($node002, 1, 0, 'http'); |
8501
|
0
|
|
|
|
|
0
|
$node002->addArrow($node003, 1, 0, 'server'); |
8502
|
0
|
|
|
|
|
0
|
$node003->addArrow($node004, 1, 0, 'for'); |
8503
|
0
|
|
|
|
|
0
|
$node004->addArrow($node006, 1, 0, 'STORE', \&collectStore); |
8504
|
0
|
|
|
|
|
0
|
$node006->addArrow($node007, 1, 0, 'on'); |
8505
|
0
|
|
|
|
|
0
|
$node007->addArrow($node008, 1, 0, 'port'); |
8506
|
0
|
|
|
|
|
0
|
$node008->addArrow($node009, 1, 0, 'PORT', \&collectPort); |
8507
|
0
|
|
|
|
|
0
|
$node009->addArrow($node010, 1, 0, 'at'); |
8508
|
0
|
|
|
|
|
0
|
$node009->addDefault($node011); |
8509
|
0
|
|
|
|
|
0
|
$node010->addArrow($node011, 1, 0, 'TEXT', \&collectText); |
8510
|
0
|
|
|
|
|
0
|
$node011->addArrow($node012, 1, 0, 'with'); |
8511
|
0
|
|
|
|
|
0
|
$node011->addDefault($node016); |
8512
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'static'); |
8513
|
0
|
|
|
|
|
0
|
$node013->addArrow($node014, 1, 0, 'files'); |
8514
|
0
|
|
|
|
|
0
|
$node014->addArrow($node015, 1, 0, 'from'); |
8515
|
0
|
|
|
|
|
0
|
$node015->addArrow($node016, 1, 0, 'FOLDER', \&collectFolder); |
8516
|
0
|
|
|
|
|
0
|
$node016->addArrow($node017, 1, 0, 'for'); |
8517
|
0
|
|
|
|
|
0
|
$node016->addDefault($node018); |
8518
|
0
|
|
|
|
|
0
|
$node017->addArrow($node018, 1, 0, 'everybody', \&collectEverybody); |
8519
|
|
|
|
|
|
|
} |
8520
|
|
|
|
|
|
|
|
8521
|
|
|
|
|
|
|
sub collectEverybody { |
8522
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8523
|
0
|
|
|
|
|
0
|
my $label = shift; |
8524
|
0
|
|
|
|
|
0
|
my $value = shift; |
8525
|
|
|
|
|
|
|
|
8526
|
0
|
|
|
|
|
0
|
$o->{corsAllowEverybody} = 1; |
8527
|
|
|
|
|
|
|
} |
8528
|
|
|
|
|
|
|
|
8529
|
|
|
|
|
|
|
sub collectFolder { |
8530
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8531
|
0
|
|
|
|
|
0
|
my $label = shift; |
8532
|
0
|
|
|
|
|
0
|
my $value = shift; |
8533
|
|
|
|
|
|
|
|
8534
|
0
|
|
|
|
|
0
|
$o->{staticFolder} = $value; |
8535
|
|
|
|
|
|
|
} |
8536
|
|
|
|
|
|
|
|
8537
|
|
|
|
|
|
|
sub collectPort { |
8538
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8539
|
0
|
|
|
|
|
0
|
my $label = shift; |
8540
|
0
|
|
|
|
|
0
|
my $value = shift; |
8541
|
|
|
|
|
|
|
|
8542
|
0
|
|
|
|
|
0
|
$o->{port} = $value; |
8543
|
|
|
|
|
|
|
} |
8544
|
|
|
|
|
|
|
|
8545
|
|
|
|
|
|
|
sub collectStore { |
8546
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8547
|
0
|
|
|
|
|
0
|
my $label = shift; |
8548
|
0
|
|
|
|
|
0
|
my $value = shift; |
8549
|
|
|
|
|
|
|
|
8550
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
8551
|
|
|
|
|
|
|
} |
8552
|
|
|
|
|
|
|
|
8553
|
|
|
|
|
|
|
sub collectText { |
8554
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8555
|
0
|
|
|
|
|
0
|
my $label = shift; |
8556
|
0
|
|
|
|
|
0
|
my $value = shift; |
8557
|
|
|
|
|
|
|
|
8558
|
0
|
|
|
|
|
0
|
$o->{root} = $value; |
8559
|
|
|
|
|
|
|
} |
8560
|
|
|
|
|
|
|
|
8561
|
|
|
|
|
|
|
sub new { |
8562
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
8563
|
0
|
|
|
|
|
0
|
my $actor = shift; |
8564
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
8565
|
|
|
|
|
|
|
|
8566
|
|
|
|
|
|
|
# END AUTOGENERATED |
8567
|
|
|
|
|
|
|
|
8568
|
|
|
|
|
|
|
# HTML FOLDER NAME start-http-server |
8569
|
|
|
|
|
|
|
# HTML TITLE HTTP store server |
8570
|
|
|
|
|
|
|
sub help { |
8571
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8572
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8573
|
|
|
|
|
|
|
|
8574
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
8575
|
0
|
|
|
|
|
0
|
$ui->space; |
8576
|
0
|
|
|
|
|
0
|
$ui->command('cds start http server for STORE on port PORT'); |
8577
|
0
|
|
|
|
|
0
|
$ui->p('Starts a simple HTTP server listening on port PORT. The server handles requests within /objects and /accounts, and uses STORE as backend. Requests on the root URL (/) deliver a short message.'); |
8578
|
0
|
|
|
|
|
0
|
$ui->p('You may need superuser (root) privileges to use the default HTTP port 80.'); |
8579
|
0
|
|
|
|
|
0
|
$ui->p('This server is very useful for small to medium-size projects, but not particularly efficient for large-scale applications. It makes no effort to use DMA or similar features to speed up delivery, and handles only one request at a time (single-threaded). However, when using a front-end web server with load-balancing capabilities, multiple HTTP servers for the same store may be started to handle multiple requests in parallel.'); |
8580
|
0
|
|
|
|
|
0
|
$ui->space; |
8581
|
0
|
|
|
|
|
0
|
$ui->command('… at TEXT'); |
8582
|
0
|
|
|
|
|
0
|
$ui->p('As above, but makes the store accessible at /TEXT/objects and /TEXT/accounts.'); |
8583
|
0
|
|
|
|
|
0
|
$ui->space; |
8584
|
0
|
|
|
|
|
0
|
$ui->command('… with static files from FOLDER'); |
8585
|
0
|
|
|
|
|
0
|
$ui->p('Delivers static files from FOLDER for URLs outside of /objects and /accounts. This is useful for self-contained web apps.'); |
8586
|
0
|
|
|
|
|
0
|
$ui->space; |
8587
|
0
|
|
|
|
|
0
|
$ui->command('… for everybody'); |
8588
|
0
|
|
|
|
|
0
|
$ui->p('Sets CORS headers to allow everybody to access the store from within a web browser.'); |
8589
|
0
|
|
|
|
|
0
|
$ui->space; |
8590
|
0
|
|
|
|
|
0
|
$ui->p('For more options, write a Perl script instantiating and configuring a CDS::HTTPServer.'); |
8591
|
0
|
|
|
|
|
0
|
$ui->space; |
8592
|
|
|
|
|
|
|
} |
8593
|
|
|
|
|
|
|
|
8594
|
|
|
|
|
|
|
sub startHttpServer { |
8595
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8596
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8597
|
|
|
|
|
|
|
|
8598
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
8599
|
|
|
|
|
|
|
|
8600
|
0
|
|
|
|
|
0
|
my $httpServer = CDS::HTTPServer->new($o->{port}); |
8601
|
0
|
|
|
|
|
0
|
$httpServer->setLogger(CDS::Commands::StartHTTPServer::Logger->new($o->{ui})); |
8602
|
0
|
|
|
|
|
0
|
$httpServer->setCorsAllowEverybody($o->{corsAllowEverybody}); |
8603
|
0
|
|
0
|
|
|
0
|
$httpServer->addHandler(CDS::HTTPServer::StoreHandler->new($o->{root} // '/', $o->{store})); |
8604
|
0
|
0
|
0
|
|
|
0
|
$httpServer->addHandler(CDS::HTTPServer::IdentificationHandler->new($o->{root} // '/')) if ! defined $o->{staticFolder}; |
8605
|
0
|
0
|
|
|
|
0
|
$httpServer->addHandler(CDS::HTTPServer::StaticFilesHandler->new('/', $o->{staticFolder}, 'index.html')) if defined $o->{staticFolder}; |
8606
|
0
|
|
|
|
|
0
|
eval { $httpServer->run; }; |
|
0
|
|
|
|
|
0
|
|
8607
|
0
|
0
|
|
|
|
0
|
if ($@) { |
8608
|
0
|
|
|
|
|
0
|
my $error = $@; |
8609
|
0
|
0
|
|
|
|
0
|
$error = $1 if $error =~ /^(.*?)( at |\n)/; |
8610
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8611
|
0
|
|
|
|
|
0
|
$o->{ui}->p('Failed to run server on port '.$o->{port}.': '.$error); |
8612
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8613
|
|
|
|
|
|
|
} |
8614
|
|
|
|
|
|
|
} |
8615
|
|
|
|
|
|
|
|
8616
|
|
|
|
|
|
|
package CDS::Commands::StartHTTPServer::Logger; |
8617
|
|
|
|
|
|
|
|
8618
|
|
|
|
|
|
|
sub new { |
8619
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
8620
|
0
|
|
|
|
|
0
|
my $ui = shift; |
8621
|
|
|
|
|
|
|
|
8622
|
0
|
|
|
|
|
0
|
return bless {ui => $ui}; |
8623
|
|
|
|
|
|
|
} |
8624
|
|
|
|
|
|
|
|
8625
|
|
|
|
|
|
|
sub onServerStarts { |
8626
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8627
|
0
|
|
|
|
|
0
|
my $port = shift; |
8628
|
|
|
|
|
|
|
|
8629
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
8630
|
0
|
|
|
|
|
0
|
$ui->space; |
8631
|
0
|
|
|
|
|
0
|
$ui->line($o->{ui}->gray($ui->niceDateTimeLocal), ' ', $ui->green('Server ready at http://localhost:', $port)); |
8632
|
|
|
|
|
|
|
} |
8633
|
|
|
|
|
|
|
|
8634
|
|
|
|
|
|
|
sub onRequestStarts { |
8635
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8636
|
0
|
|
|
|
|
0
|
my $request = shift; |
8637
|
|
|
|
|
|
|
} |
8638
|
|
|
|
|
|
|
|
8639
|
|
|
|
|
|
|
sub onRequestError { |
8640
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8641
|
0
|
|
|
|
|
0
|
my $request = shift; |
8642
|
|
|
|
|
|
|
|
8643
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
8644
|
0
|
|
|
|
|
0
|
$ui->line($o->{ui}->gray($ui->niceDateTimeLocal), ' ', $ui->blue($ui->left(15, $request->peerAddress)), ' ', $request->method, ' ', $request->path, ' ', $ui->red(@_)); |
8645
|
|
|
|
|
|
|
} |
8646
|
|
|
|
|
|
|
|
8647
|
|
|
|
|
|
|
sub onRequestDone { |
8648
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8649
|
0
|
|
|
|
|
0
|
my $request = shift; |
8650
|
0
|
|
|
|
|
0
|
my $responseCode = shift; |
8651
|
|
|
|
|
|
|
|
8652
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
8653
|
0
|
|
|
|
|
0
|
$ui->line($o->{ui}->gray($ui->niceDateTimeLocal), ' ', $ui->blue($ui->left(15, $request->peerAddress)), ' ', $request->method, ' ', $request->path, ' ', $ui->bold($responseCode)); |
8654
|
|
|
|
|
|
|
} |
8655
|
|
|
|
|
|
|
|
8656
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
8657
|
|
|
|
|
|
|
package CDS::Commands::Transfer; |
8658
|
|
|
|
|
|
|
|
8659
|
|
|
|
|
|
|
sub register { |
8660
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
8661
|
0
|
|
|
|
|
0
|
my $cds = shift; |
8662
|
0
|
|
|
|
|
0
|
my $help = shift; |
8663
|
|
|
|
|
|
|
|
8664
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
8665
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
8666
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
8667
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
8668
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
8669
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
8670
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(0); |
8671
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(0); |
8672
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(0); |
8673
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(0); |
8674
|
0
|
|
|
|
|
0
|
my $node010 = CDS::Parser::Node->new(0); |
8675
|
0
|
|
|
|
|
0
|
my $node011 = CDS::Parser::Node->new(0); |
8676
|
0
|
|
|
|
|
0
|
my $node012 = CDS::Parser::Node->new(0); |
8677
|
0
|
|
|
|
|
0
|
my $node013 = CDS::Parser::Node->new(0); |
8678
|
0
|
|
|
|
|
0
|
my $node014 = CDS::Parser::Node->new(0); |
8679
|
0
|
|
|
|
|
0
|
my $node015 = CDS::Parser::Node->new(0); |
8680
|
0
|
|
|
|
|
0
|
my $node016 = CDS::Parser::Node->new(0); |
8681
|
0
|
|
|
|
|
0
|
my $node017 = CDS::Parser::Node->new(1); |
8682
|
0
|
|
|
|
|
0
|
my $node018 = CDS::Parser::Node->new(0); |
8683
|
0
|
|
|
|
|
0
|
my $node019 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&transfer}); |
8684
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 1, 0, 'thoroughly'); |
8685
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 0, 0, 'leniently'); |
8686
|
0
|
|
|
|
|
0
|
$cds->addDefault($node003); |
8687
|
0
|
|
|
|
|
0
|
$cds->addArrow($node003, 1, 0, 'leniently', \&collectLeniently); |
8688
|
0
|
|
|
|
|
0
|
$cds->addArrow($node003, 1, 0, 'thoroughly', \&collectThoroughly); |
8689
|
0
|
|
|
|
|
0
|
$help->addArrow($node002, 1, 0, 'transfer'); |
8690
|
0
|
|
|
|
|
0
|
$node000->addArrow($node003, 1, 0, 'leniently', \&collectLeniently1); |
8691
|
0
|
|
|
|
|
0
|
$node001->addArrow($node003, 0, 0, 'thoroughly', \&collectLeniently1); |
8692
|
0
|
|
|
|
|
0
|
$node003->addArrow($node004, 1, 0, 'transfer'); |
8693
|
0
|
|
|
|
|
0
|
$node004->addDefault($node005); |
8694
|
0
|
|
|
|
|
0
|
$node004->addDefault($node006); |
8695
|
0
|
|
|
|
|
0
|
$node004->addDefault($node007); |
8696
|
0
|
|
|
|
|
0
|
$node004->addDefault($node008); |
8697
|
0
|
|
|
|
|
0
|
$node004->addArrow($node009, 1, 0, 'message'); |
8698
|
0
|
|
|
|
|
0
|
$node004->addDefault($node010); |
8699
|
0
|
|
|
|
|
0
|
$node004->addArrow($node011, 1, 0, 'private'); |
8700
|
0
|
|
|
|
|
0
|
$node004->addArrow($node012, 1, 0, 'public'); |
8701
|
0
|
|
|
|
|
0
|
$node004->addArrow($node013, 1, 0, 'all', \&collectAll); |
8702
|
0
|
|
|
|
|
0
|
$node004->addArrow($node013, 0, 0, 'messages', \&collectMessages); |
8703
|
0
|
|
|
|
|
0
|
$node004->addArrow($node013, 0, 0, 'private', \&collectPrivate); |
8704
|
0
|
|
|
|
|
0
|
$node004->addArrow($node013, 0, 0, 'public', \&collectPublic); |
8705
|
0
|
|
|
|
|
0
|
$node005->addArrow($node005, 1, 0, 'HASH', \&collectHash); |
8706
|
0
|
|
|
|
|
0
|
$node005->addArrow($node017, 1, 0, 'HASH', \&collectHash); |
8707
|
0
|
|
|
|
|
0
|
$node006->addArrow($node006, 1, 0, 'OBJECT', \&collectObject); |
8708
|
0
|
|
|
|
|
0
|
$node006->addArrow($node017, 1, 0, 'OBJECT', \&collectObject); |
8709
|
0
|
|
|
|
|
0
|
$node007->addArrow($node007, 1, 0, 'ACCOUNT', \&collectAccount); |
8710
|
0
|
|
|
|
|
0
|
$node007->addArrow($node017, 1, 0, 'ACCOUNT', \&collectAccount); |
8711
|
0
|
|
|
|
|
0
|
$node008->addArrow($node008, 1, 0, 'BOX', \&collectBox); |
8712
|
0
|
|
|
|
|
0
|
$node008->addArrow($node017, 1, 0, 'BOX', \&collectBox); |
8713
|
0
|
|
|
|
|
0
|
$node009->addArrow($node013, 1, 0, 'box', \&collectMessages); |
8714
|
0
|
|
|
|
|
0
|
$node010->addArrow($node010, 1, 0, 'HASH', \&collectHash); |
8715
|
0
|
|
|
|
|
0
|
$node010->addArrow($node015, 1, 0, 'HASH', \&collectHash); |
8716
|
0
|
|
|
|
|
0
|
$node011->addArrow($node013, 1, 0, 'box', \&collectPrivate); |
8717
|
0
|
|
|
|
|
0
|
$node012->addArrow($node013, 1, 0, 'box', \&collectPublic); |
8718
|
0
|
|
|
|
|
0
|
$node013->addArrow($node014, 1, 0, 'of'); |
8719
|
0
|
|
|
|
|
0
|
$node014->addArrow($node014, 1, 0, 'HASH', \&collectHash1); |
8720
|
0
|
|
|
|
|
0
|
$node014->addArrow($node015, 1, 0, 'HASH', \&collectHash1); |
8721
|
0
|
|
|
|
|
0
|
$node015->addArrow($node016, 1, 0, 'from'); |
8722
|
0
|
|
|
|
|
0
|
$node016->addArrow($node017, 1, 0, 'STORE', \&collectStore); |
8723
|
0
|
|
|
|
|
0
|
$node017->addArrow($node018, 1, 0, 'to'); |
8724
|
0
|
|
|
|
|
0
|
$node018->addArrow($node018, 1, 0, 'STORE', \&collectStore1); |
8725
|
0
|
|
|
|
|
0
|
$node018->addArrow($node019, 1, 0, 'STORE', \&collectStore1); |
8726
|
|
|
|
|
|
|
} |
8727
|
|
|
|
|
|
|
|
8728
|
|
|
|
|
|
|
sub collectAccount { |
8729
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8730
|
0
|
|
|
|
|
0
|
my $label = shift; |
8731
|
0
|
|
|
|
|
0
|
my $value = shift; |
8732
|
|
|
|
|
|
|
|
8733
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, $value; |
|
0
|
|
|
|
|
0
|
|
8734
|
|
|
|
|
|
|
} |
8735
|
|
|
|
|
|
|
|
8736
|
|
|
|
|
|
|
sub collectAll { |
8737
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8738
|
0
|
|
|
|
|
0
|
my $label = shift; |
8739
|
0
|
|
|
|
|
0
|
my $value = shift; |
8740
|
|
|
|
|
|
|
|
8741
|
0
|
|
|
|
|
0
|
push @{$o->{boxLabels}}, 'public', 'private', 'messages'; |
|
0
|
|
|
|
|
0
|
|
8742
|
|
|
|
|
|
|
} |
8743
|
|
|
|
|
|
|
|
8744
|
|
|
|
|
|
|
sub collectBox { |
8745
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8746
|
0
|
|
|
|
|
0
|
my $label = shift; |
8747
|
0
|
|
|
|
|
0
|
my $value = shift; |
8748
|
|
|
|
|
|
|
|
8749
|
0
|
|
|
|
|
0
|
push @{$o->{boxTokens}}, $value; |
|
0
|
|
|
|
|
0
|
|
8750
|
|
|
|
|
|
|
} |
8751
|
|
|
|
|
|
|
|
8752
|
|
|
|
|
|
|
sub collectHash { |
8753
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8754
|
0
|
|
|
|
|
0
|
my $label = shift; |
8755
|
0
|
|
|
|
|
0
|
my $value = shift; |
8756
|
|
|
|
|
|
|
|
8757
|
0
|
|
|
|
|
0
|
push @{$o->{objectHashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
8758
|
|
|
|
|
|
|
} |
8759
|
|
|
|
|
|
|
|
8760
|
|
|
|
|
|
|
sub collectHash1 { |
8761
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8762
|
0
|
|
|
|
|
0
|
my $label = shift; |
8763
|
0
|
|
|
|
|
0
|
my $value = shift; |
8764
|
|
|
|
|
|
|
|
8765
|
0
|
|
|
|
|
0
|
push @{$o->{accountHashes}}, $value; |
|
0
|
|
|
|
|
0
|
|
8766
|
|
|
|
|
|
|
} |
8767
|
|
|
|
|
|
|
|
8768
|
|
|
|
|
|
|
sub collectLeniently { |
8769
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8770
|
0
|
|
|
|
|
0
|
my $label = shift; |
8771
|
0
|
|
|
|
|
0
|
my $value = shift; |
8772
|
|
|
|
|
|
|
|
8773
|
0
|
|
|
|
|
0
|
$o->{leniently} = 1; |
8774
|
|
|
|
|
|
|
} |
8775
|
|
|
|
|
|
|
|
8776
|
|
|
|
|
|
|
sub collectLeniently1 { |
8777
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8778
|
0
|
|
|
|
|
0
|
my $label = shift; |
8779
|
0
|
|
|
|
|
0
|
my $value = shift; |
8780
|
|
|
|
|
|
|
|
8781
|
0
|
|
|
|
|
0
|
$o->{leniently} = 1; |
8782
|
0
|
|
|
|
|
0
|
$o->{thoroughly} = 1; |
8783
|
|
|
|
|
|
|
} |
8784
|
|
|
|
|
|
|
|
8785
|
|
|
|
|
|
|
sub collectMessages { |
8786
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8787
|
0
|
|
|
|
|
0
|
my $label = shift; |
8788
|
0
|
|
|
|
|
0
|
my $value = shift; |
8789
|
|
|
|
|
|
|
|
8790
|
0
|
|
|
|
|
0
|
push @{$o->{boxLabels}}, 'messages'; |
|
0
|
|
|
|
|
0
|
|
8791
|
|
|
|
|
|
|
} |
8792
|
|
|
|
|
|
|
|
8793
|
|
|
|
|
|
|
sub collectObject { |
8794
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8795
|
0
|
|
|
|
|
0
|
my $label = shift; |
8796
|
0
|
|
|
|
|
0
|
my $value = shift; |
8797
|
|
|
|
|
|
|
|
8798
|
0
|
|
|
|
|
0
|
push @{$o->{objectTokens}}, $value; |
|
0
|
|
|
|
|
0
|
|
8799
|
|
|
|
|
|
|
} |
8800
|
|
|
|
|
|
|
|
8801
|
|
|
|
|
|
|
sub collectPrivate { |
8802
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8803
|
0
|
|
|
|
|
0
|
my $label = shift; |
8804
|
0
|
|
|
|
|
0
|
my $value = shift; |
8805
|
|
|
|
|
|
|
|
8806
|
0
|
|
|
|
|
0
|
push @{$o->{boxLabels}}, 'private'; |
|
0
|
|
|
|
|
0
|
|
8807
|
|
|
|
|
|
|
} |
8808
|
|
|
|
|
|
|
|
8809
|
|
|
|
|
|
|
sub collectPublic { |
8810
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8811
|
0
|
|
|
|
|
0
|
my $label = shift; |
8812
|
0
|
|
|
|
|
0
|
my $value = shift; |
8813
|
|
|
|
|
|
|
|
8814
|
0
|
|
|
|
|
0
|
push @{$o->{boxLabels}}, 'public'; |
|
0
|
|
|
|
|
0
|
|
8815
|
|
|
|
|
|
|
} |
8816
|
|
|
|
|
|
|
|
8817
|
|
|
|
|
|
|
sub collectStore { |
8818
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8819
|
0
|
|
|
|
|
0
|
my $label = shift; |
8820
|
0
|
|
|
|
|
0
|
my $value = shift; |
8821
|
|
|
|
|
|
|
|
8822
|
0
|
|
|
|
|
0
|
$o->{fromStore} = $value; |
8823
|
|
|
|
|
|
|
} |
8824
|
|
|
|
|
|
|
|
8825
|
|
|
|
|
|
|
sub collectStore1 { |
8826
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8827
|
0
|
|
|
|
|
0
|
my $label = shift; |
8828
|
0
|
|
|
|
|
0
|
my $value = shift; |
8829
|
|
|
|
|
|
|
|
8830
|
0
|
|
|
|
|
0
|
push @{$o->{toStores}}, $value; |
|
0
|
|
|
|
|
0
|
|
8831
|
|
|
|
|
|
|
} |
8832
|
|
|
|
|
|
|
|
8833
|
|
|
|
|
|
|
sub collectThoroughly { |
8834
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8835
|
0
|
|
|
|
|
0
|
my $label = shift; |
8836
|
0
|
|
|
|
|
0
|
my $value = shift; |
8837
|
|
|
|
|
|
|
|
8838
|
0
|
|
|
|
|
0
|
$o->{thoroughly} = 1; |
8839
|
|
|
|
|
|
|
} |
8840
|
|
|
|
|
|
|
|
8841
|
|
|
|
|
|
|
sub new { |
8842
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
8843
|
0
|
|
|
|
|
0
|
my $actor = shift; |
8844
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
8845
|
|
|
|
|
|
|
|
8846
|
|
|
|
|
|
|
# END AUTOGENERATED |
8847
|
|
|
|
|
|
|
|
8848
|
|
|
|
|
|
|
# HTML FOLDER NAME transfer |
8849
|
|
|
|
|
|
|
# HTML TITLE Transfer |
8850
|
|
|
|
|
|
|
sub help { |
8851
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8852
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8853
|
|
|
|
|
|
|
|
8854
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
8855
|
0
|
|
|
|
|
0
|
$ui->space; |
8856
|
0
|
|
|
|
|
0
|
$ui->command('cds transfer BOX* to STORE*'); |
8857
|
0
|
|
|
|
|
0
|
$ui->command('cds transfer ACCOUNT* to STORE*'); |
8858
|
0
|
|
|
|
|
0
|
$ui->command('cds transfer all of HASH* from STORE to STORE*'); |
8859
|
0
|
|
|
|
|
0
|
$ui->command('cds transfer BOXLABEL of HASH* from STORE to STORE*'); |
8860
|
0
|
|
|
|
|
0
|
$ui->p('Copies an account (or some of its boxes) including all referenced trees from one store to another. If the source store is omitted, the selected store is used.'); |
8861
|
0
|
|
|
|
|
0
|
$ui->space; |
8862
|
0
|
|
|
|
|
0
|
$ui->command('cds transfer OBJECT* to STORE*'); |
8863
|
0
|
|
|
|
|
0
|
$ui->command('cds transfer HASH* from STORE to STORE*'); |
8864
|
0
|
|
|
|
|
0
|
$ui->p('Copies a tree from one store to another. If the source store is omitted, the selected store is used.'); |
8865
|
0
|
|
|
|
|
0
|
$ui->space; |
8866
|
0
|
|
|
|
|
0
|
$ui->command('cds ', $ui->underlined('leniently'), ' transfer …'); |
8867
|
0
|
|
|
|
|
0
|
$ui->p('Warns about missing objects, but ignores them and proceeds with the rest.'); |
8868
|
0
|
|
|
|
|
0
|
$ui->space; |
8869
|
0
|
|
|
|
|
0
|
$ui->command('cds ', $ui->underlined('thoroughly'), ' transfer …'); |
8870
|
0
|
|
|
|
|
0
|
$ui->p('Check subtrees of objects existing at the destination. This may be used to fix missing objects on the destination store.'); |
8871
|
0
|
|
|
|
|
0
|
$ui->space; |
8872
|
|
|
|
|
|
|
} |
8873
|
|
|
|
|
|
|
|
8874
|
|
|
|
|
|
|
sub transfer { |
8875
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8876
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
8877
|
|
|
|
|
|
|
|
8878
|
|
|
|
|
|
|
# Collect the arguments |
8879
|
0
|
|
|
|
|
0
|
$o->{keyPairToken} = $o->{actor}->preferredKeyPairToken; |
8880
|
0
|
|
|
|
|
0
|
$o->{accountTokens} = []; |
8881
|
0
|
|
|
|
|
0
|
$o->{accountHashes} = []; |
8882
|
0
|
|
|
|
|
0
|
$o->{boxTokens} = []; |
8883
|
0
|
|
|
|
|
0
|
$o->{boxLabels} = []; |
8884
|
0
|
|
|
|
|
0
|
$o->{objectTokens} = []; |
8885
|
0
|
|
|
|
|
0
|
$o->{objectHashes} = []; |
8886
|
0
|
|
|
|
|
0
|
$o->{toStores} = []; |
8887
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
8888
|
|
|
|
|
|
|
|
8889
|
|
|
|
|
|
|
# Use the selected store |
8890
|
0
|
0
|
0
|
|
|
0
|
$o->{fromStore} = $o->{actor}->preferredStore if (scalar @{$o->{accountHashes}} || scalar @{$o->{objectHashes}}) && ! $o->{fromStore}; |
|
|
|
0
|
|
|
|
|
8891
|
|
|
|
|
|
|
|
8892
|
|
|
|
|
|
|
# Prepare the object tokens |
8893
|
0
|
|
|
|
|
0
|
for my $hash (@{$o->{objectHashes}}) { |
|
0
|
|
|
|
|
0
|
|
8894
|
0
|
|
|
|
|
0
|
push @{$o->{objectTokens}}, CDS::ObjectToken->new($o->{fromStore}, $hash); |
|
0
|
|
|
|
|
0
|
|
8895
|
|
|
|
|
|
|
} |
8896
|
|
|
|
|
|
|
|
8897
|
|
|
|
|
|
|
# Prepare the account tokens |
8898
|
0
|
|
|
|
|
0
|
for my $hash (@{$o->{accountHashes}}) { |
|
0
|
|
|
|
|
0
|
|
8899
|
0
|
|
|
|
|
0
|
push @{$o->{accountTokens}}, CDS::AccountToken->new($o->{fromStore}, $hash); |
|
0
|
|
|
|
|
0
|
|
8900
|
|
|
|
|
|
|
} |
8901
|
|
|
|
|
|
|
|
8902
|
|
|
|
|
|
|
# Prepare the box tokens |
8903
|
0
|
|
|
|
|
0
|
for my $accountToken (@{$o->{accountTokens}}) { |
|
0
|
|
|
|
|
0
|
|
8904
|
0
|
|
|
|
|
0
|
for my $boxLabel (@{$o->{boxLabels}}) { |
|
0
|
|
|
|
|
0
|
|
8905
|
0
|
|
|
|
|
0
|
push @{$o->{boxTokens}}, CDS::BoxToken->new($accountToken, $boxLabel); |
|
0
|
|
|
|
|
0
|
|
8906
|
|
|
|
|
|
|
} |
8907
|
|
|
|
|
|
|
} |
8908
|
|
|
|
|
|
|
|
8909
|
|
|
|
|
|
|
# Copy the public key of every account first |
8910
|
0
|
|
|
|
|
0
|
my %done; |
8911
|
0
|
|
|
|
|
0
|
for my $boxToken (@{$o->{boxTokens}}) { |
|
0
|
|
|
|
|
0
|
|
8912
|
0
|
|
|
|
|
0
|
my $actorHash = $boxToken->accountToken->actorHash; |
8913
|
0
|
0
|
|
|
|
0
|
next if $done{$actorHash->bytes}; |
8914
|
0
|
|
|
|
|
0
|
$done{$actorHash->bytes} = 1; |
8915
|
0
|
|
|
|
|
0
|
push @{$o->{objectTokens}}, CDS::ObjectToken->new($boxToken->accountToken->cliStore, $actorHash); |
|
0
|
|
|
|
|
0
|
|
8916
|
|
|
|
|
|
|
} |
8917
|
|
|
|
|
|
|
|
8918
|
|
|
|
|
|
|
# Prepare the destination stores |
8919
|
0
|
|
|
|
|
0
|
my $toStores = []; |
8920
|
0
|
|
|
|
|
0
|
for my $toStore (@{$o->{toStores}}) { |
|
0
|
|
|
|
|
0
|
|
8921
|
0
|
|
|
|
|
0
|
push @$toStores, {store => $toStore, storeError => undef, needed => [1]}; |
8922
|
|
|
|
|
|
|
} |
8923
|
|
|
|
|
|
|
|
8924
|
|
|
|
|
|
|
# Print the stores |
8925
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8926
|
0
|
|
|
|
|
0
|
my $n = scalar @$toStores; |
8927
|
0
|
|
|
|
|
0
|
for my $i (0 .. $n - 1) { |
8928
|
0
|
|
|
|
|
0
|
my $toStore = $toStores->[$i]; |
8929
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray(' │' x $i, ' ┌', '──' x ($n - $i), ' ', $toStore->{store}->url)); |
8930
|
|
|
|
|
|
|
} |
8931
|
|
|
|
|
|
|
|
8932
|
|
|
|
|
|
|
# Process all trees |
8933
|
0
|
|
|
|
|
0
|
$o->{objects} = {}; |
8934
|
0
|
|
|
|
|
0
|
$o->{missingObjects} = {}; |
8935
|
0
|
|
|
|
|
0
|
for my $objectToken (@{$o->{objectTokens}}) { |
|
0
|
|
|
|
|
0
|
|
8936
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray(' │' x $n)); |
8937
|
0
|
|
|
|
|
0
|
$o->process($objectToken->hash, $objectToken->cliStore, $toStores, 1); |
8938
|
|
|
|
|
|
|
} |
8939
|
|
|
|
|
|
|
|
8940
|
|
|
|
|
|
|
# Process all accounts |
8941
|
0
|
|
|
|
|
0
|
my $keyPair = $o->{keyPairToken}->keyPair; |
8942
|
0
|
|
|
|
|
0
|
for my $boxToken (@{$o->{boxTokens}}) { |
|
0
|
|
|
|
|
0
|
|
8943
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray(' │' x $n)); |
8944
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray(' │' x $n, ' Transferring ', $boxToken->boxLabel, ' box of ', $boxToken->accountToken->actorHash->hex)); |
8945
|
0
|
|
|
|
|
0
|
my ($hashes, $listError) = $boxToken->accountToken->cliStore->list($boxToken->accountToken->actorHash, $boxToken->boxLabel, 0, $keyPair); |
8946
|
0
|
0
|
|
|
|
0
|
next if $listError; |
8947
|
|
|
|
|
|
|
|
8948
|
0
|
|
|
|
|
0
|
for my $hash (@$hashes) { |
8949
|
0
|
|
0
|
|
|
0
|
$o->process($hash, $boxToken->accountToken->cliStore, $toStores, 1) // next; |
8950
|
|
|
|
|
|
|
|
8951
|
0
|
|
|
|
|
0
|
for my $toStore (@$toStores) { |
8952
|
0
|
0
|
|
|
|
0
|
next if defined $toStore->{storeError}; |
8953
|
0
|
|
|
|
|
0
|
$toStore->{storeError} = $toStore->{store}->add($boxToken->accountToken->actorHash, $boxToken->boxLabel, $hash, $keyPair); |
8954
|
|
|
|
|
|
|
} |
8955
|
|
|
|
|
|
|
} |
8956
|
|
|
|
|
|
|
} |
8957
|
|
|
|
|
|
|
|
8958
|
|
|
|
|
|
|
# Print the stores again, with their errors |
8959
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray(' │' x $n)); |
8960
|
0
|
|
|
|
|
0
|
for my $i (reverse 0 .. $n - 1) { |
8961
|
0
|
|
|
|
|
0
|
my $toStore = $toStores->[$i]; |
8962
|
0
|
0
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->gray(' │' x $i, ' └', '──' x ($n - $i), ' ', $toStore->{store}->url), ' ', defined $toStore->{storeError} ? $o->{ui}->red($toStore->{storeError}) : ''); |
8963
|
|
|
|
|
|
|
} |
8964
|
|
|
|
|
|
|
|
8965
|
|
|
|
|
|
|
# Report the total size |
8966
|
0
|
|
|
|
|
0
|
my $totalSize = 0; |
8967
|
0
|
|
|
|
|
0
|
my $totalDataSize = 0; |
8968
|
0
|
|
|
|
|
0
|
map { $totalSize += $_->{size} ; $totalDataSize += $_->{dataSize} } values %{$o->{objects}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
8969
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8970
|
0
|
|
|
|
|
0
|
$o->{ui}->p(scalar keys %{$o->{objects}}, ' unique objects ', $o->{ui}->bold($o->{ui}->niceFileSize($totalSize)), ' ', $o->{ui}->gray($o->{ui}->niceFileSize($totalDataSize), ' data')); |
|
0
|
|
|
|
|
0
|
|
8971
|
0
|
0
|
|
|
|
0
|
$o->{ui}->pOrange(scalar keys %{$o->{missingObjects}}, ' or more objects are missing') if scalar keys %{$o->{missingObjects}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
8972
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
8973
|
|
|
|
|
|
|
} |
8974
|
|
|
|
|
|
|
|
8975
|
|
|
|
|
|
|
sub process { |
8976
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
8977
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
8978
|
0
|
|
|
|
|
0
|
my $fromStore = shift; |
8979
|
0
|
|
|
|
|
0
|
my $toStores = shift; |
8980
|
0
|
|
|
|
|
0
|
my $depth = shift; |
8981
|
|
|
|
|
|
|
|
8982
|
0
|
|
|
|
|
0
|
my $hashHex = $hash->hex; |
8983
|
0
|
|
|
|
|
0
|
my $keyPair = $o->{keyPairToken}->keyPair; |
8984
|
|
|
|
|
|
|
|
8985
|
|
|
|
|
|
|
# Check if we retrieved this object before |
8986
|
0
|
0
|
|
|
|
0
|
if (exists $o->{objects}->{$hashHex}) { |
8987
|
0
|
|
|
|
|
0
|
$o->report($hash->hex, $toStores, $depth, $o->{ui}->green('copied before')); |
8988
|
0
|
|
|
|
|
0
|
return 1; |
8989
|
|
|
|
|
|
|
} |
8990
|
|
|
|
|
|
|
|
8991
|
|
|
|
|
|
|
# Try to book the object on all active stores |
8992
|
0
|
|
|
|
|
0
|
my $countNeeded = 0; |
8993
|
0
|
|
|
|
|
0
|
my $hasActiveStore = 0; |
8994
|
0
|
|
|
|
|
0
|
for my $toStore (@$toStores) { |
8995
|
0
|
0
|
|
|
|
0
|
next if defined $toStore->{storeError}; |
8996
|
0
|
|
|
|
|
0
|
$hasActiveStore = 1; |
8997
|
0
|
0
|
0
|
|
|
0
|
next if ! $o->{thoroughly} && ! $toStore->{needed}->[$depth - 1]; |
8998
|
|
|
|
|
|
|
|
8999
|
0
|
|
|
|
|
0
|
my ($found, $bookError) = $toStore->{store}->book($hash); |
9000
|
0
|
0
|
|
|
|
0
|
if (defined $bookError) { |
9001
|
0
|
|
|
|
|
0
|
$toStore->{storeError} = $bookError; |
9002
|
0
|
|
|
|
|
0
|
next; |
9003
|
|
|
|
|
|
|
} |
9004
|
|
|
|
|
|
|
|
9005
|
0
|
0
|
|
|
|
0
|
next if $found; |
9006
|
0
|
|
|
|
|
0
|
$toStore->{needed}->[$depth] = 1; |
9007
|
0
|
|
|
|
|
0
|
$countNeeded += 1; |
9008
|
|
|
|
|
|
|
} |
9009
|
|
|
|
|
|
|
|
9010
|
|
|
|
|
|
|
# Return if all stores reported an error |
9011
|
0
|
0
|
|
|
|
0
|
return if ! $hasActiveStore; |
9012
|
|
|
|
|
|
|
|
9013
|
|
|
|
|
|
|
# Ignore existing subtrees at the destination unless "thoroughly" is set |
9014
|
0
|
0
|
0
|
|
|
0
|
if (! $o->{thoroughly} && ! $countNeeded) { |
9015
|
0
|
|
|
|
|
0
|
$o->report($hashHex, $toStores, $depth, $o->{ui}->gray('skipping subtree')); |
9016
|
0
|
|
|
|
|
0
|
return 1; |
9017
|
|
|
|
|
|
|
} |
9018
|
|
|
|
|
|
|
|
9019
|
|
|
|
|
|
|
# Retrieve the object |
9020
|
0
|
|
|
|
|
0
|
my ($object, $getError) = $fromStore->get($hash, $keyPair); |
9021
|
0
|
0
|
|
|
|
0
|
return if defined $getError; |
9022
|
|
|
|
|
|
|
|
9023
|
0
|
0
|
|
|
|
0
|
if (! defined $object) { |
9024
|
0
|
|
|
|
|
0
|
$o->{missingObjects}->{$hashHex} = 1; |
9025
|
0
|
|
|
|
|
0
|
$o->report($hashHex, $toStores, $depth, $o->{ui}->orange('is missing')); |
9026
|
0
|
0
|
|
|
|
0
|
return if ! $o->{leniently}; |
9027
|
|
|
|
|
|
|
} |
9028
|
|
|
|
|
|
|
|
9029
|
|
|
|
|
|
|
# Display |
9030
|
0
|
|
|
|
|
0
|
my $size = $object->byteLength; |
9031
|
0
|
|
|
|
|
0
|
$o->{objects}->{$hashHex} = {needed => $countNeeded, size => $size, dataSize => length $object->data}; |
9032
|
0
|
|
|
|
|
0
|
$o->report($hashHex, $toStores, $depth, $o->{ui}->bold($o->{ui}->niceFileSize($size)), ' ', $o->{ui}->gray($object->hashesCount, ' hashes')); |
9033
|
|
|
|
|
|
|
|
9034
|
|
|
|
|
|
|
# Process all children |
9035
|
0
|
|
|
|
|
0
|
foreach my $hash ($object->hashes) { |
9036
|
0
|
|
0
|
|
|
0
|
$o->process($hash, $fromStore, $toStores, $depth + 1) // return; |
9037
|
|
|
|
|
|
|
} |
9038
|
|
|
|
|
|
|
|
9039
|
|
|
|
|
|
|
# Write the object to all active stores |
9040
|
0
|
|
|
|
|
0
|
for my $toStore (@$toStores) { |
9041
|
0
|
0
|
|
|
|
0
|
next if defined $toStore->{storeError}; |
9042
|
0
|
0
|
|
|
|
0
|
next if ! $toStore->{needed}->[$depth]; |
9043
|
0
|
|
|
|
|
0
|
my $putError = $toStore->{store}->put($hash, $object, $keyPair); |
9044
|
0
|
0
|
|
|
|
0
|
$toStore->{storeError} = $putError if $putError; |
9045
|
|
|
|
|
|
|
} |
9046
|
|
|
|
|
|
|
|
9047
|
0
|
|
|
|
|
0
|
return 1; |
9048
|
|
|
|
|
|
|
} |
9049
|
|
|
|
|
|
|
|
9050
|
|
|
|
|
|
|
sub report { |
9051
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9052
|
0
|
|
|
|
|
0
|
my $hashHex = shift; |
9053
|
0
|
|
|
|
|
0
|
my $toStores = shift; |
9054
|
0
|
|
|
|
|
0
|
my $depth = shift; |
9055
|
|
|
|
|
|
|
|
9056
|
0
|
|
|
|
|
0
|
my @text; |
9057
|
0
|
|
|
|
|
0
|
for my $toStore (@$toStores) { |
9058
|
0
|
0
|
|
|
|
0
|
if ($toStore->{storeError}) { |
|
|
0
|
|
|
|
|
|
9059
|
0
|
|
|
|
|
0
|
push @text, $o->{ui}->red(' ⨯'); |
9060
|
|
|
|
|
|
|
} elsif ($toStore->{needed}->[$depth]) { |
9061
|
0
|
|
|
|
|
0
|
push @text, $o->{ui}->green(' +'); |
9062
|
|
|
|
|
|
|
} else { |
9063
|
0
|
|
|
|
|
0
|
push @text, $o->{ui}->green(' ‒'); |
9064
|
|
|
|
|
|
|
} |
9065
|
|
|
|
|
|
|
} |
9066
|
|
|
|
|
|
|
|
9067
|
0
|
|
|
|
|
0
|
push @text, ' ', ' ' x ($depth - 1), $hashHex; |
9068
|
0
|
|
|
|
|
0
|
push @text, ' ', @_; |
9069
|
0
|
|
|
|
|
0
|
$o->{ui}->line(@text); |
9070
|
|
|
|
|
|
|
} |
9071
|
|
|
|
|
|
|
|
9072
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
9073
|
|
|
|
|
|
|
package CDS::Commands::UseCache; |
9074
|
|
|
|
|
|
|
|
9075
|
|
|
|
|
|
|
sub register { |
9076
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9077
|
0
|
|
|
|
|
0
|
my $cds = shift; |
9078
|
0
|
|
|
|
|
0
|
my $help = shift; |
9079
|
|
|
|
|
|
|
|
9080
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
9081
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
9082
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
9083
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
9084
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&useCache}); |
9085
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&dropCache}); |
9086
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&cache}); |
9087
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 1, 0, 'use'); |
9088
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'drop'); |
9089
|
0
|
|
|
|
|
0
|
$cds->addArrow($node006, 1, 0, 'cache'); |
9090
|
0
|
|
|
|
|
0
|
$help->addArrow($node003, 1, 0, 'cache'); |
9091
|
0
|
|
|
|
|
0
|
$node000->addArrow($node001, 1, 0, 'cache'); |
9092
|
0
|
|
|
|
|
0
|
$node001->addArrow($node004, 1, 0, 'STORE', \&collectStore); |
9093
|
0
|
|
|
|
|
0
|
$node002->addArrow($node005, 1, 0, 'cache'); |
9094
|
|
|
|
|
|
|
} |
9095
|
|
|
|
|
|
|
|
9096
|
|
|
|
|
|
|
sub collectStore { |
9097
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9098
|
0
|
|
|
|
|
0
|
my $label = shift; |
9099
|
0
|
|
|
|
|
0
|
my $value = shift; |
9100
|
|
|
|
|
|
|
|
9101
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
9102
|
|
|
|
|
|
|
} |
9103
|
|
|
|
|
|
|
|
9104
|
|
|
|
|
|
|
sub new { |
9105
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9106
|
0
|
|
|
|
|
0
|
my $actor = shift; |
9107
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
9108
|
|
|
|
|
|
|
|
9109
|
|
|
|
|
|
|
# END AUTOGENERATED |
9110
|
|
|
|
|
|
|
|
9111
|
|
|
|
|
|
|
# HTML FOLDER NAME use-cache |
9112
|
|
|
|
|
|
|
# HTML TITLE Using a cache store |
9113
|
|
|
|
|
|
|
sub help { |
9114
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9115
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9116
|
|
|
|
|
|
|
|
9117
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
9118
|
0
|
|
|
|
|
0
|
$ui->space; |
9119
|
0
|
|
|
|
|
0
|
$ui->command('cds use cache STORE'); |
9120
|
0
|
|
|
|
|
0
|
$ui->p('Uses STORE to cache objects, and speed up subsequent requests of the same object. This is particularly useful when working with (slow) remote stores. The cache store should be a fast store, such as a local folder store or an in-memory store.'); |
9121
|
0
|
|
|
|
|
0
|
$ui->p('Cached objects are not linked to any account, and may disappear with the next garbage collection. Most stores however keep objects for a least a few hours after their last use.'); |
9122
|
0
|
|
|
|
|
0
|
$ui->space; |
9123
|
0
|
|
|
|
|
0
|
$ui->command('cds drop cache'); |
9124
|
0
|
|
|
|
|
0
|
$ui->p('Stops using the cache.'); |
9125
|
0
|
|
|
|
|
0
|
$ui->space; |
9126
|
0
|
|
|
|
|
0
|
$ui->command('cds cache'); |
9127
|
0
|
|
|
|
|
0
|
$ui->p('Shows which cache store is used (if any).'); |
9128
|
0
|
|
|
|
|
0
|
$ui->space; |
9129
|
|
|
|
|
|
|
} |
9130
|
|
|
|
|
|
|
|
9131
|
|
|
|
|
|
|
sub useCache { |
9132
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9133
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9134
|
|
|
|
|
|
|
|
9135
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
9136
|
|
|
|
|
|
|
|
9137
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('use cache')->setText($o->{store}->url); |
9138
|
0
|
|
0
|
|
|
0
|
$o->{actor}->saveOrShowError // return; |
9139
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Using store "', $o->{store}->url, '" to cache objects.'); |
9140
|
|
|
|
|
|
|
} |
9141
|
|
|
|
|
|
|
|
9142
|
|
|
|
|
|
|
sub dropCache { |
9143
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9144
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9145
|
|
|
|
|
|
|
|
9146
|
0
|
|
|
|
|
0
|
$o->{actor}->sessionRoot->child('use cache')->clear; |
9147
|
0
|
|
0
|
|
|
0
|
$o->{actor}->saveOrShowError // return; |
9148
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('Not using any cache any more.'); |
9149
|
|
|
|
|
|
|
} |
9150
|
|
|
|
|
|
|
|
9151
|
|
|
|
|
|
|
sub cache { |
9152
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9153
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9154
|
|
|
|
|
|
|
|
9155
|
0
|
|
|
|
|
0
|
my $storeUrl = $o->{actor}->sessionRoot->child('use cache')->textValue; |
9156
|
0
|
0
|
|
|
|
0
|
return $o->{ui}->line('Not using any cache.') if ! length $storeUrl; |
9157
|
0
|
|
|
|
|
0
|
return $o->{ui}->line('Using store "', $storeUrl, '" to cache objects.'); |
9158
|
|
|
|
|
|
|
} |
9159
|
|
|
|
|
|
|
|
9160
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
9161
|
|
|
|
|
|
|
package CDS::Commands::UseStore; |
9162
|
|
|
|
|
|
|
|
9163
|
|
|
|
|
|
|
sub register { |
9164
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9165
|
0
|
|
|
|
|
0
|
my $cds = shift; |
9166
|
0
|
|
|
|
|
0
|
my $help = shift; |
9167
|
|
|
|
|
|
|
|
9168
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
9169
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
9170
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
9171
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
9172
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
9173
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&useStoreForMessaging}); |
9174
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'use'); |
9175
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'messaging'); |
9176
|
0
|
|
|
|
|
0
|
$node000->addArrow($node004, 1, 0, 'store'); |
9177
|
0
|
|
|
|
|
0
|
$node001->addArrow($node002, 1, 0, 'STORE', \&collectStore); |
9178
|
0
|
|
|
|
|
0
|
$node002->addArrow($node003, 1, 0, 'for'); |
9179
|
0
|
|
|
|
|
0
|
$node003->addArrow($node005, 1, 0, 'messaging'); |
9180
|
|
|
|
|
|
|
} |
9181
|
|
|
|
|
|
|
|
9182
|
|
|
|
|
|
|
sub collectStore { |
9183
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9184
|
0
|
|
|
|
|
0
|
my $label = shift; |
9185
|
0
|
|
|
|
|
0
|
my $value = shift; |
9186
|
|
|
|
|
|
|
|
9187
|
0
|
|
|
|
|
0
|
$o->{store} = $value; |
9188
|
|
|
|
|
|
|
} |
9189
|
|
|
|
|
|
|
|
9190
|
|
|
|
|
|
|
sub new { |
9191
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9192
|
0
|
|
|
|
|
0
|
my $actor = shift; |
9193
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
9194
|
|
|
|
|
|
|
|
9195
|
|
|
|
|
|
|
# END AUTOGENERATED |
9196
|
|
|
|
|
|
|
|
9197
|
|
|
|
|
|
|
# HTML FOLDER NAME use-store |
9198
|
|
|
|
|
|
|
# HTML TITLE Set the messaging store |
9199
|
|
|
|
|
|
|
sub help { |
9200
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9201
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9202
|
|
|
|
|
|
|
|
9203
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
9204
|
0
|
|
|
|
|
0
|
$ui->space; |
9205
|
0
|
|
|
|
|
0
|
$ui->command('cds use STORE for messaging'); |
9206
|
0
|
|
|
|
|
0
|
$ui->p('Uses STORE to send and receive messages.'); |
9207
|
0
|
|
|
|
|
0
|
$ui->space; |
9208
|
|
|
|
|
|
|
} |
9209
|
|
|
|
|
|
|
|
9210
|
|
|
|
|
|
|
sub useStoreForMessaging { |
9211
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9212
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9213
|
|
|
|
|
|
|
|
9214
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
9215
|
|
|
|
|
|
|
|
9216
|
0
|
|
|
|
|
0
|
$o->{actor}->{configuration}->setMessagingStoreUrl($o->{store}->url); |
9217
|
0
|
|
|
|
|
0
|
$o->{ui}->pGreen('The messaging store is now ', $o->{store}->url); |
9218
|
|
|
|
|
|
|
} |
9219
|
|
|
|
|
|
|
|
9220
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
9221
|
|
|
|
|
|
|
package CDS::Commands::Welcome; |
9222
|
|
|
|
|
|
|
|
9223
|
|
|
|
|
|
|
sub register { |
9224
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9225
|
0
|
|
|
|
|
0
|
my $cds = shift; |
9226
|
0
|
|
|
|
|
0
|
my $help = shift; |
9227
|
|
|
|
|
|
|
|
9228
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
9229
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
9230
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
9231
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(0); |
9232
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(0); |
9233
|
0
|
|
|
|
|
0
|
my $node005 = CDS::Parser::Node->new(0); |
9234
|
0
|
|
|
|
|
0
|
my $node006 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
9235
|
0
|
|
|
|
|
0
|
my $node007 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&suppress}); |
9236
|
0
|
|
|
|
|
0
|
my $node008 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&enable}); |
9237
|
0
|
|
|
|
|
0
|
my $node009 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&show}); |
9238
|
0
|
|
|
|
|
0
|
$cds->addArrow($node000, 1, 0, 'suppress'); |
9239
|
0
|
|
|
|
|
0
|
$cds->addArrow($node002, 1, 0, 'enable'); |
9240
|
0
|
|
|
|
|
0
|
$cds->addArrow($node004, 1, 0, 'show'); |
9241
|
0
|
|
|
|
|
0
|
$help->addArrow($node006, 1, 0, 'welcome'); |
9242
|
0
|
|
|
|
|
0
|
$node000->addArrow($node001, 1, 0, 'welcome'); |
9243
|
0
|
|
|
|
|
0
|
$node001->addArrow($node007, 1, 0, 'message'); |
9244
|
0
|
|
|
|
|
0
|
$node002->addArrow($node003, 1, 0, 'welcome'); |
9245
|
0
|
|
|
|
|
0
|
$node003->addArrow($node008, 1, 0, 'message'); |
9246
|
0
|
|
|
|
|
0
|
$node004->addArrow($node005, 1, 0, 'welcome'); |
9247
|
0
|
|
|
|
|
0
|
$node005->addArrow($node009, 1, 0, 'message'); |
9248
|
|
|
|
|
|
|
} |
9249
|
|
|
|
|
|
|
|
9250
|
|
|
|
|
|
|
sub new { |
9251
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9252
|
0
|
|
|
|
|
0
|
my $actor = shift; |
9253
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
9254
|
|
|
|
|
|
|
|
9255
|
|
|
|
|
|
|
# END AUTOGENERATED |
9256
|
|
|
|
|
|
|
|
9257
|
|
|
|
|
|
|
# HTML FOLDER NAME welcome |
9258
|
|
|
|
|
|
|
# HTML TITLE Welcome message |
9259
|
|
|
|
|
|
|
sub help { |
9260
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9261
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9262
|
|
|
|
|
|
|
|
9263
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
9264
|
0
|
|
|
|
|
0
|
$ui->space; |
9265
|
0
|
|
|
|
|
0
|
$ui->command('cds suppress welcome message'); |
9266
|
0
|
|
|
|
|
0
|
$ui->p('Suppresses the welcome message when typing "cds".'); |
9267
|
0
|
|
|
|
|
0
|
$ui->space; |
9268
|
0
|
|
|
|
|
0
|
$ui->command('cds enable welcome message'); |
9269
|
0
|
|
|
|
|
0
|
$ui->p('Enables the welcome message when typing "cds".'); |
9270
|
0
|
|
|
|
|
0
|
$ui->space; |
9271
|
0
|
|
|
|
|
0
|
$ui->command('cds show welcome message'); |
9272
|
0
|
|
|
|
|
0
|
$ui->p('Shows the welcome message.'); |
9273
|
0
|
|
|
|
|
0
|
$ui->space; |
9274
|
|
|
|
|
|
|
} |
9275
|
|
|
|
|
|
|
|
9276
|
|
|
|
|
|
|
sub suppress { |
9277
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9278
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9279
|
|
|
|
|
|
|
|
9280
|
0
|
|
|
|
|
0
|
$o->{actor}->localRoot->child('suppress welcome message')->setBoolean(1); |
9281
|
0
|
|
0
|
|
|
0
|
$o->{actor}->saveOrShowError // return; |
9282
|
|
|
|
|
|
|
|
9283
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
9284
|
0
|
|
|
|
|
0
|
$o->{ui}->p('The welcome message will not be shown any more.'); |
9285
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
9286
|
0
|
|
|
|
|
0
|
$o->{ui}->line('You can manually display the message by typing:'); |
9287
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->blue(' cds show welcome message')); |
9288
|
0
|
|
|
|
|
0
|
$o->{ui}->line('or re-enable it using:'); |
9289
|
0
|
|
|
|
|
0
|
$o->{ui}->line($o->{ui}->blue(' cds enable welcome message')); |
9290
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
9291
|
|
|
|
|
|
|
} |
9292
|
|
|
|
|
|
|
|
9293
|
|
|
|
|
|
|
sub enable { |
9294
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9295
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9296
|
|
|
|
|
|
|
|
9297
|
0
|
|
|
|
|
0
|
$o->{actor}->localRoot->child('suppress welcome message')->clear; |
9298
|
0
|
|
0
|
|
|
0
|
$o->{actor}->saveOrShowError // return; |
9299
|
|
|
|
|
|
|
|
9300
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
9301
|
0
|
|
|
|
|
0
|
$o->{ui}->p('The welcome message will be shown when you type "cds".'); |
9302
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
9303
|
|
|
|
|
|
|
} |
9304
|
|
|
|
|
|
|
|
9305
|
|
|
|
|
|
|
sub isEnabled { |
9306
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9307
|
0
|
|
|
|
|
0
|
! $o->{actor}->localRoot->child('suppress welcome message')->isSet } |
9308
|
|
|
|
|
|
|
|
9309
|
|
|
|
|
|
|
sub show { |
9310
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9311
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9312
|
|
|
|
|
|
|
|
9313
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
9314
|
0
|
|
|
|
|
0
|
$ui->space; |
9315
|
0
|
|
|
|
|
0
|
$ui->title('Hi there!'); |
9316
|
0
|
|
|
|
|
0
|
$ui->p('This is the command line interface (CLI) of Condensation ', $CDS::VERSION, ', ', $CDS::releaseDate, '. Condensation is a distributed data system with conflict-free forward merging and end-to-end security. More information is available on https://condensation.io.'); |
9317
|
0
|
|
|
|
|
0
|
$ui->space; |
9318
|
0
|
|
|
|
|
0
|
$ui->p('Commands resemble short english sentences. For example, the following "sentence" will show the record of an object:'); |
9319
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue(' cds show record e5cbfc282e1f3e6fd0f3e5fffd41964c645f44d7fae8ef5cb350c2dfd2196c9f \\')); |
9320
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue(' from http://examples.condensation.io')); |
9321
|
0
|
|
|
|
|
0
|
$ui->p('Type a "?" to explore possible commands, e.g.'); |
9322
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue(' cds show ?')); |
9323
|
0
|
|
|
|
|
0
|
$ui->p('or use TAB or TAB-TAB for command completion.'); |
9324
|
0
|
|
|
|
|
0
|
$ui->space; |
9325
|
0
|
|
|
|
|
0
|
$ui->p('To get help, type'); |
9326
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue(' cds help')); |
9327
|
0
|
|
|
|
|
0
|
$ui->space; |
9328
|
0
|
|
|
|
|
0
|
$ui->p('To suppress this welcome message, type'); |
9329
|
0
|
|
|
|
|
0
|
$ui->line($ui->blue(' cds suppress welcome message')); |
9330
|
0
|
|
|
|
|
0
|
$ui->space; |
9331
|
|
|
|
|
|
|
} |
9332
|
|
|
|
|
|
|
|
9333
|
|
|
|
|
|
|
package CDS::Commands::WhatIs; |
9334
|
|
|
|
|
|
|
|
9335
|
|
|
|
|
|
|
# BEGIN AUTOGENERATED |
9336
|
|
|
|
|
|
|
|
9337
|
|
|
|
|
|
|
sub register { |
9338
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9339
|
0
|
|
|
|
|
0
|
my $cds = shift; |
9340
|
0
|
|
|
|
|
0
|
my $help = shift; |
9341
|
|
|
|
|
|
|
|
9342
|
0
|
|
|
|
|
0
|
my $node000 = CDS::Parser::Node->new(0); |
9343
|
0
|
|
|
|
|
0
|
my $node001 = CDS::Parser::Node->new(0); |
9344
|
0
|
|
|
|
|
0
|
my $node002 = CDS::Parser::Node->new(0); |
9345
|
0
|
|
|
|
|
0
|
my $node003 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&help}); |
9346
|
0
|
|
|
|
|
0
|
my $node004 = CDS::Parser::Node->new(1, {constructor => \&new, function => \&whatIs}); |
9347
|
0
|
|
|
|
|
0
|
$cds->addArrow($node001, 1, 0, 'what'); |
9348
|
0
|
|
|
|
|
0
|
$help->addArrow($node000, 1, 0, 'what'); |
9349
|
0
|
|
|
|
|
0
|
$node000->addArrow($node003, 1, 0, 'is'); |
9350
|
0
|
|
|
|
|
0
|
$node001->addArrow($node002, 1, 0, 'is'); |
9351
|
0
|
|
|
|
|
0
|
$node002->addArrow($node004, 1, 0, 'TEXT', \&collectText); |
9352
|
|
|
|
|
|
|
} |
9353
|
|
|
|
|
|
|
|
9354
|
|
|
|
|
|
|
sub collectText { |
9355
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9356
|
0
|
|
|
|
|
0
|
my $label = shift; |
9357
|
0
|
|
|
|
|
0
|
my $value = shift; |
9358
|
|
|
|
|
|
|
|
9359
|
0
|
|
|
|
|
0
|
$o->{text} = $value; |
9360
|
|
|
|
|
|
|
} |
9361
|
|
|
|
|
|
|
|
9362
|
|
|
|
|
|
|
sub new { |
9363
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9364
|
0
|
|
|
|
|
0
|
my $actor = shift; |
9365
|
0
|
|
|
|
|
0
|
bless {actor => $actor, ui => $actor->ui} } |
9366
|
|
|
|
|
|
|
|
9367
|
|
|
|
|
|
|
# END AUTOGENERATED |
9368
|
|
|
|
|
|
|
|
9369
|
|
|
|
|
|
|
# HTML FOLDER NAME what-is |
9370
|
|
|
|
|
|
|
# HTML TITLE What is |
9371
|
|
|
|
|
|
|
sub help { |
9372
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9373
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9374
|
|
|
|
|
|
|
|
9375
|
0
|
|
|
|
|
0
|
my $ui = $o->{ui}; |
9376
|
0
|
|
|
|
|
0
|
$ui->space; |
9377
|
0
|
|
|
|
|
0
|
$ui->command('cds what is TEXT'); |
9378
|
0
|
|
|
|
|
0
|
$ui->p('Tells what TEXT could be under the current configuration.'); |
9379
|
0
|
|
|
|
|
0
|
$ui->space; |
9380
|
|
|
|
|
|
|
} |
9381
|
|
|
|
|
|
|
|
9382
|
|
|
|
|
|
|
sub whatIs { |
9383
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9384
|
0
|
|
|
|
|
0
|
my $cmd = shift; |
9385
|
|
|
|
|
|
|
|
9386
|
0
|
|
|
|
|
0
|
$cmd->collect($o); |
9387
|
0
|
|
|
|
|
0
|
$o->{butNot} = []; |
9388
|
|
|
|
|
|
|
|
9389
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
9390
|
0
|
|
|
|
|
0
|
$o->{ui}->title($o->{ui}->blue($o->{text}), ' may be …'); |
9391
|
|
|
|
|
|
|
|
9392
|
0
|
|
|
0
|
|
0
|
$o->test('ACCOUNT', 'an ACCOUNT', sub { shift->url }); |
|
0
|
|
|
|
|
0
|
|
9393
|
0
|
|
|
0
|
|
0
|
$o->test('AESKEY', 'an AESKEY', sub { unpack('H*', shift) }); |
|
0
|
|
|
|
|
0
|
|
9394
|
0
|
|
|
0
|
|
0
|
$o->test('BOX', 'a BOX', sub { shift->url }); |
|
0
|
|
|
|
|
0
|
|
9395
|
0
|
|
|
0
|
|
0
|
$o->test('BOXLABEL', 'a BOXLABEL', sub { shift }); |
|
0
|
|
|
|
|
0
|
|
9396
|
0
|
|
|
|
|
0
|
$o->test('FILE', 'a FILE', \&fileResult); |
9397
|
0
|
|
|
|
|
0
|
$o->test('FILENAME', 'a FILENAME', \&fileResult); |
9398
|
0
|
|
|
|
|
0
|
$o->test('FOLDER', 'a FOLDER', \&fileResult); |
9399
|
0
|
|
|
0
|
|
0
|
$o->test('GROUP', 'a GROUP on this system', sub { shift }); |
|
0
|
|
|
|
|
0
|
|
9400
|
0
|
|
|
0
|
|
0
|
$o->test('HASH', 'a HASH or ACTOR hash', sub { shift->hex }); |
|
0
|
|
|
|
|
0
|
|
9401
|
0
|
|
|
|
|
0
|
$o->test('KEYPAIR', 'a KEYPAIR', \&keyPairResult); |
9402
|
0
|
|
|
0
|
|
0
|
$o->test('LABEL', 'a remembered LABEL', sub { shift }); |
|
0
|
|
|
|
|
0
|
|
9403
|
0
|
|
|
0
|
|
0
|
$o->test('OBJECT', 'an OBJECT', sub { shift->url }); |
|
0
|
|
|
|
|
0
|
|
9404
|
0
|
|
|
|
|
0
|
$o->test('OBJECTFILE', 'an OBJECTFILE', \&objectFileResult); |
9405
|
0
|
|
|
0
|
|
0
|
$o->test('STORE', 'a STORE', sub { shift->url }); |
|
0
|
|
|
|
|
0
|
|
9406
|
0
|
|
|
0
|
|
0
|
$o->test('USER', 'a USER on this system', sub { shift }); |
|
0
|
|
|
|
|
0
|
|
9407
|
|
|
|
|
|
|
|
9408
|
0
|
|
|
|
|
0
|
for my $butNot (@{$o->{butNot}}) { |
|
0
|
|
|
|
|
0
|
|
9409
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
9410
|
0
|
|
|
|
|
0
|
$o->{ui}->line('… but not ', $butNot->{text}, ', because:'); |
9411
|
0
|
|
|
|
|
0
|
for my $warning (@{$butNot->{warnings}}) { |
|
0
|
|
|
|
|
0
|
|
9412
|
0
|
|
|
|
|
0
|
$o->{ui}->warning($warning); |
9413
|
|
|
|
|
|
|
} |
9414
|
|
|
|
|
|
|
} |
9415
|
|
|
|
|
|
|
|
9416
|
0
|
|
|
|
|
0
|
$o->{ui}->space; |
9417
|
|
|
|
|
|
|
} |
9418
|
|
|
|
|
|
|
|
9419
|
|
|
|
|
|
|
sub test { |
9420
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9421
|
0
|
|
|
|
|
0
|
my $expect = shift; |
9422
|
0
|
|
|
|
|
0
|
my $text = shift; |
9423
|
0
|
|
|
|
|
0
|
my $resultHandler = shift; |
9424
|
|
|
|
|
|
|
|
9425
|
0
|
|
|
|
|
0
|
my $token = CDS::Parser::Token->new($o->{actor}, $o->{text}); |
9426
|
0
|
|
|
|
|
0
|
my $result = $token->produce($expect); |
9427
|
0
|
0
|
|
|
|
0
|
if (defined $result) { |
|
|
0
|
|
|
|
|
|
9428
|
0
|
|
|
|
|
0
|
my $whichOne = &$resultHandler($result); |
9429
|
0
|
|
|
|
|
0
|
$o->{ui}->line('… ', $text, ' ', $o->{ui}->gray($whichOne)); |
9430
|
0
|
|
|
|
|
0
|
} elsif (scalar @{$token->{warnings}}) { |
9431
|
0
|
|
|
|
|
0
|
push @{$o->{butNot}}, {text => $text, warnings => $token->{warnings}}; |
|
0
|
|
|
|
|
0
|
|
9432
|
|
|
|
|
|
|
} |
9433
|
|
|
|
|
|
|
} |
9434
|
|
|
|
|
|
|
|
9435
|
|
|
|
|
|
|
sub keyPairResult { |
9436
|
0
|
|
|
0
|
|
0
|
my $keyPairToken = shift; |
9437
|
|
|
|
|
|
|
|
9438
|
0
|
|
|
|
|
0
|
return $keyPairToken->file.' ('.$keyPairToken->keyPair->publicKey->hash->hex.')'; |
9439
|
|
|
|
|
|
|
} |
9440
|
|
|
|
|
|
|
|
9441
|
|
|
|
|
|
|
sub objectFileResult { |
9442
|
0
|
|
|
0
|
|
0
|
my $objectFileToken = shift; |
9443
|
|
|
|
|
|
|
|
9444
|
0
|
0
|
|
|
|
0
|
return $objectFileToken->file if $objectFileToken->object->byteLength > 1024 * 1024; |
9445
|
0
|
|
|
|
|
0
|
return $objectFileToken->file.' ('.$objectFileToken->object->calculateHash->hex.')'; |
9446
|
|
|
|
|
|
|
} |
9447
|
|
|
|
|
|
|
|
9448
|
|
|
|
|
|
|
sub fileResult { |
9449
|
0
|
|
|
0
|
|
0
|
my $file = shift; |
9450
|
|
|
|
|
|
|
|
9451
|
0
|
|
|
|
|
0
|
my @s = stat $file; |
9452
|
0
|
0
|
|
|
|
0
|
my $label = |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
9453
|
|
|
|
|
|
|
! scalar @s ? ' (non-existing)' : |
9454
|
|
|
|
|
|
|
Fcntl::S_ISDIR($s[2]) ? ' (folder)' : |
9455
|
|
|
|
|
|
|
Fcntl::S_ISREG($s[2]) ? ' (file, '.$s[7].' bytes)' : |
9456
|
|
|
|
|
|
|
Fcntl::S_ISLNK($s[2]) ? ' (symbolic link)' : |
9457
|
|
|
|
|
|
|
Fcntl::S_ISBLK($s[2]) ? ' (block device)' : |
9458
|
|
|
|
|
|
|
Fcntl::S_ISCHR($s[2]) ? ' (char device)' : |
9459
|
|
|
|
|
|
|
Fcntl::S_ISSOCK($s[2]) ? ' (socket)' : |
9460
|
|
|
|
|
|
|
Fcntl::S_ISFIFO($s[2]) ? ' (pipe)' : ' (unknown type)'; |
9461
|
|
|
|
|
|
|
|
9462
|
0
|
|
|
|
|
0
|
return $file.$label; |
9463
|
|
|
|
|
|
|
} |
9464
|
|
|
|
|
|
|
|
9465
|
|
|
|
|
|
|
package CDS::Configuration; |
9466
|
|
|
|
|
|
|
|
9467
|
|
|
|
|
|
|
our $xdgConfigurationFolder = ($ENV{XDG_CONFIG_HOME} || $ENV{HOME}.'/.config').'/condensation'; |
9468
|
|
|
|
|
|
|
our $xdgDataFolder = ($ENV{XDG_DATA_HOME} || $ENV{HOME}.'/.local/share').'/condensation'; |
9469
|
|
|
|
|
|
|
|
9470
|
|
|
|
|
|
|
sub getOrCreateDefault { |
9471
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9472
|
0
|
|
|
|
|
0
|
my $ui = shift; |
9473
|
|
|
|
|
|
|
|
9474
|
0
|
|
|
|
|
0
|
my $configuration = $class->new($ui, $xdgConfigurationFolder, $xdgDataFolder); |
9475
|
0
|
|
|
|
|
0
|
$configuration->createIfNecessary(); |
9476
|
0
|
|
|
|
|
0
|
return $configuration; |
9477
|
|
|
|
|
|
|
} |
9478
|
|
|
|
|
|
|
|
9479
|
|
|
|
|
|
|
sub new { |
9480
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9481
|
0
|
|
|
|
|
0
|
my $ui = shift; |
9482
|
0
|
|
|
|
|
0
|
my $folder = shift; |
9483
|
0
|
|
|
|
|
0
|
my $defaultStoreFolder = shift; |
9484
|
|
|
|
|
|
|
|
9485
|
0
|
|
|
|
|
0
|
return bless {ui => $ui, folder => $folder, defaultStoreFolder => $defaultStoreFolder}; |
9486
|
|
|
|
|
|
|
} |
9487
|
|
|
|
|
|
|
|
9488
|
0
|
|
|
0
|
|
0
|
sub ui { shift->{ui} } |
9489
|
0
|
|
|
0
|
|
0
|
sub folder { shift->{folder} } |
9490
|
|
|
|
|
|
|
|
9491
|
|
|
|
|
|
|
sub createIfNecessary { |
9492
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9493
|
|
|
|
|
|
|
|
9494
|
0
|
|
|
|
|
0
|
my $keyPairFile = $o->{folder}.'/key-pair'; |
9495
|
0
|
0
|
|
|
|
0
|
return 1 if -f $keyPairFile; |
9496
|
|
|
|
|
|
|
|
9497
|
0
|
|
|
|
|
0
|
$o->{ui}->progress('Creating configuration folders …'); |
9498
|
0
|
|
0
|
|
|
0
|
$o->createFolder($o->{folder}) // return $o->{ui}->error('Failed to create the folder "', $o->{folder}, '".'); |
9499
|
0
|
|
0
|
|
|
0
|
$o->createFolder($o->{defaultStoreFolder}) // return $o->{ui}->error('Failed to create the folder "', $o->{defaultStoreFolder}, '".'); |
9500
|
0
|
|
|
|
|
0
|
CDS::FolderStore->new($o->{defaultStoreFolder})->createIfNecessary; |
9501
|
|
|
|
|
|
|
|
9502
|
0
|
|
|
|
|
0
|
$o->{ui}->progress('Generating key pair …'); |
9503
|
0
|
|
|
|
|
0
|
my $keyPair = CDS::KeyPair->generate; |
9504
|
0
|
|
0
|
|
|
0
|
$keyPair->writeToFile($keyPairFile) // return $o->{ui}->error('Failed to write the configuration file "', $keyPairFile, '". Make sure that this location is writable.'); |
9505
|
0
|
|
|
|
|
0
|
$o->{ui}->removeProgress; |
9506
|
0
|
|
|
|
|
0
|
return 1; |
9507
|
|
|
|
|
|
|
} |
9508
|
|
|
|
|
|
|
|
9509
|
|
|
|
|
|
|
sub createFolder { |
9510
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9511
|
0
|
|
|
|
|
0
|
my $folder = shift; |
9512
|
|
|
|
|
|
|
|
9513
|
0
|
|
|
|
|
0
|
for my $path (CDS->intermediateFolders($folder)) { |
9514
|
0
|
|
|
|
|
0
|
mkdir $path; |
9515
|
|
|
|
|
|
|
} |
9516
|
|
|
|
|
|
|
|
9517
|
0
|
|
|
|
|
0
|
return -d $folder; |
9518
|
|
|
|
|
|
|
} |
9519
|
|
|
|
|
|
|
|
9520
|
|
|
|
|
|
|
sub file { |
9521
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9522
|
0
|
|
|
|
|
0
|
my $filename = shift; |
9523
|
|
|
|
|
|
|
|
9524
|
0
|
|
|
|
|
0
|
return $o->{folder}.'/'.$filename; |
9525
|
|
|
|
|
|
|
} |
9526
|
|
|
|
|
|
|
|
9527
|
|
|
|
|
|
|
sub messagingStoreUrl { |
9528
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9529
|
|
|
|
|
|
|
|
9530
|
0
|
|
0
|
|
|
0
|
return $o->readFirstLine('messaging-store') // 'file://'.$o->{defaultStoreFolder}; |
9531
|
|
|
|
|
|
|
} |
9532
|
|
|
|
|
|
|
|
9533
|
|
|
|
|
|
|
sub storageStoreUrl { |
9534
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9535
|
|
|
|
|
|
|
|
9536
|
0
|
|
0
|
|
|
0
|
return $o->readFirstLine('store') // 'file://'.$o->{defaultStoreFolder}; |
9537
|
|
|
|
|
|
|
} |
9538
|
|
|
|
|
|
|
|
9539
|
|
|
|
|
|
|
sub setMessagingStoreUrl { |
9540
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9541
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
9542
|
|
|
|
|
|
|
|
9543
|
0
|
|
|
|
|
0
|
CDS->writeTextToFile($o->file('messaging-store'), $storeUrl); |
9544
|
|
|
|
|
|
|
} |
9545
|
|
|
|
|
|
|
|
9546
|
|
|
|
|
|
|
sub setStorageStoreUrl { |
9547
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9548
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
9549
|
|
|
|
|
|
|
|
9550
|
0
|
|
|
|
|
0
|
CDS->writeTextToFile($o->file('store'), $storeUrl); |
9551
|
|
|
|
|
|
|
} |
9552
|
|
|
|
|
|
|
|
9553
|
|
|
|
|
|
|
sub keyPair { |
9554
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9555
|
|
|
|
|
|
|
|
9556
|
0
|
|
|
|
|
0
|
return CDS::KeyPair->fromFile($o->file('key-pair')); |
9557
|
|
|
|
|
|
|
} |
9558
|
|
|
|
|
|
|
|
9559
|
|
|
|
|
|
|
sub setKeyPair { |
9560
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9561
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
9562
|
|
|
|
|
|
|
|
9563
|
0
|
|
|
|
|
0
|
$keyPair->writeToFile($o->file('key-pair')); |
9564
|
|
|
|
|
|
|
} |
9565
|
|
|
|
|
|
|
|
9566
|
|
|
|
|
|
|
sub readFirstLine { |
9567
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9568
|
0
|
|
|
|
|
0
|
my $file = shift; |
9569
|
|
|
|
|
|
|
|
9570
|
0
|
|
0
|
|
|
0
|
my $content = CDS->readTextFromFile($o->file($file)) // return; |
9571
|
0
|
0
|
|
|
|
0
|
$content = $1 if $content =~ /^(.*)\n/; |
9572
|
0
|
0
|
|
|
|
0
|
$content = $1 if $content =~ /^\s*(.*?)\s*$/; |
9573
|
0
|
|
|
|
|
0
|
return $content; |
9574
|
|
|
|
|
|
|
} |
9575
|
|
|
|
|
|
|
|
9576
|
|
|
|
|
|
|
package CDS::DetachedDocument; |
9577
|
|
|
|
|
|
|
|
9578
|
1
|
|
|
1
|
|
29680
|
use parent -norequire, 'CDS::Document'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
9579
|
|
|
|
|
|
|
|
9580
|
|
|
|
|
|
|
sub new { |
9581
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9582
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
9583
|
|
|
|
|
|
|
|
9584
|
0
|
|
|
|
|
0
|
return $class->SUPER::new($keyPair, CDS::InMemoryStore->create); |
9585
|
|
|
|
|
|
|
} |
9586
|
|
|
|
|
|
|
|
9587
|
|
|
|
|
|
|
sub savingDone { |
9588
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9589
|
0
|
|
|
|
|
0
|
my $revision = shift; |
9590
|
0
|
|
|
|
|
0
|
my $newPart = shift; |
9591
|
0
|
|
|
|
|
0
|
my $obsoleteParts = shift; |
9592
|
|
|
|
|
|
|
|
9593
|
|
|
|
|
|
|
# We don't do anything |
9594
|
0
|
|
|
|
|
0
|
$o->{unsaved}->savingDone; |
9595
|
|
|
|
|
|
|
} |
9596
|
|
|
|
|
|
|
|
9597
|
|
|
|
|
|
|
package CDS::DiscoverActorGroup; |
9598
|
|
|
|
|
|
|
|
9599
|
|
|
|
|
|
|
sub discover { |
9600
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9601
|
0
|
0
|
0
|
|
|
0
|
my $builder = shift; die 'wrong type '.ref($builder).' for $builder' if defined $builder && ref $builder ne 'CDS::ActorGroupBuilder'; |
|
0
|
|
|
|
|
0
|
|
9602
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
9603
|
0
|
|
|
|
|
0
|
my $delegate = shift; |
9604
|
|
|
|
|
|
|
|
9605
|
0
|
|
|
|
|
0
|
my $o = bless { |
9606
|
|
|
|
|
|
|
knownPublicKeys => $builder->knownPublicKeys, # A hashref of known public keys (e.g. from the existing actor group) |
9607
|
|
|
|
|
|
|
keyPair => $keyPair, |
9608
|
|
|
|
|
|
|
delegate => $delegate, # The delegate |
9609
|
|
|
|
|
|
|
nodesByUrl => {}, # Nodes on which this actor group is active, by URL |
9610
|
|
|
|
|
|
|
coverage => {}, # Hashes that belong to this actor group |
9611
|
|
|
|
|
|
|
}; |
9612
|
|
|
|
|
|
|
|
9613
|
|
|
|
|
|
|
# Add all active members |
9614
|
0
|
|
|
|
|
0
|
for my $member ($builder->members) { |
9615
|
0
|
0
|
|
|
|
0
|
next if $member->status ne 'active'; |
9616
|
0
|
|
|
|
|
0
|
my $node = $o->node($member->hash, $member->storeUrl); |
9617
|
0
|
0
|
|
|
|
0
|
if ($node->{revision} < $member->revision) { |
9618
|
0
|
|
|
|
|
0
|
$node->{revision} = $member->revision; |
9619
|
0
|
|
|
|
|
0
|
$node->{status} = 'active'; |
9620
|
|
|
|
|
|
|
} |
9621
|
|
|
|
|
|
|
|
9622
|
0
|
|
|
|
|
0
|
$o->{coverage}->{$member->hash->bytes} = 1; |
9623
|
|
|
|
|
|
|
} |
9624
|
|
|
|
|
|
|
|
9625
|
|
|
|
|
|
|
# Determine the revision at start |
9626
|
0
|
|
|
|
|
0
|
my $revisionAtStart = 0; |
9627
|
0
|
|
|
|
|
0
|
for my $node (values %{$o->{nodesByUrl}}) { |
|
0
|
|
|
|
|
0
|
|
9628
|
0
|
0
|
|
|
|
0
|
$revisionAtStart = $node->{revision} if $revisionAtStart < $node->{revision}; |
9629
|
|
|
|
|
|
|
} |
9630
|
|
|
|
|
|
|
|
9631
|
|
|
|
|
|
|
# Reload the cards of all known accounts |
9632
|
0
|
|
|
|
|
0
|
for my $node (values %{$o->{nodesByUrl}}) { |
|
0
|
|
|
|
|
0
|
|
9633
|
0
|
|
|
|
|
0
|
$node->discover; |
9634
|
|
|
|
|
|
|
} |
9635
|
|
|
|
|
|
|
|
9636
|
|
|
|
|
|
|
# From here, try extending to other accounts |
9637
|
0
|
|
|
|
|
0
|
while ($o->extend) {} |
9638
|
|
|
|
|
|
|
|
9639
|
|
|
|
|
|
|
# Compile the list of actors and cards |
9640
|
0
|
|
|
|
|
0
|
my @members; |
9641
|
|
|
|
|
|
|
my @cards; |
9642
|
0
|
|
|
|
|
0
|
for my $node (values %{$o->{nodesByUrl}}) { |
|
0
|
|
|
|
|
0
|
|
9643
|
0
|
0
|
|
|
|
0
|
next if ! $node->{reachable}; |
9644
|
0
|
0
|
|
|
|
0
|
next if ! $node->{attachedToUs}; |
9645
|
0
|
0
|
|
|
|
0
|
next if ! $node->{actorOnStore}; |
9646
|
0
|
0
|
|
|
|
0
|
next if ! $node->isActiveOrIdle; |
9647
|
|
|
|
|
|
|
#-- member ++ $node->{actorHash}->hex ++ $node->{cardsRead} ++ $node->{cards} // 'undef' ++ $node->{actorOnStore} // 'undef' |
9648
|
0
|
|
|
|
|
0
|
push @members, CDS::ActorGroup::Member->new($node->{actorOnStore}, $node->{storeUrl}, $node->{revision}, $node->isActive); |
9649
|
0
|
|
|
|
|
0
|
push @cards, @{$node->{cards}}; |
|
0
|
|
|
|
|
0
|
|
9650
|
|
|
|
|
|
|
} |
9651
|
|
|
|
|
|
|
|
9652
|
|
|
|
|
|
|
# Get the newest list of entrusted actors |
9653
|
0
|
|
|
|
|
0
|
my $parser = CDS::ActorGroupBuilder->new; |
9654
|
0
|
|
|
|
|
0
|
for my $card (@cards) { |
9655
|
0
|
|
|
|
|
0
|
$parser->parseEntrustedActors($card->card->child('entrusted actors'), 0); |
9656
|
|
|
|
|
|
|
} |
9657
|
|
|
|
|
|
|
|
9658
|
|
|
|
|
|
|
# Get the entrusted actors |
9659
|
0
|
|
|
|
|
0
|
my $entrustedActors = []; |
9660
|
0
|
|
|
|
|
0
|
for my $actor ($parser->entrustedActors) { |
9661
|
0
|
|
|
|
|
0
|
my $store = $o->{delegate}->onDiscoverActorGroupVerifyStore($actor->storeUrl); |
9662
|
0
|
0
|
|
|
|
0
|
next if ! $store; |
9663
|
|
|
|
|
|
|
|
9664
|
0
|
|
|
|
|
0
|
my $knownPublicKey = $o->{knownPublicKeys}->{$actor->hash->bytes}; |
9665
|
0
|
0
|
|
|
|
0
|
if ($knownPublicKey) { |
9666
|
0
|
|
|
|
|
0
|
push @$entrustedActors, CDS::ActorGroup::EntrustedActor->new(CDS::ActorOnStore->new($knownPublicKey, $store), $actor->storeUrl); |
9667
|
0
|
|
|
|
|
0
|
next; |
9668
|
|
|
|
|
|
|
} |
9669
|
|
|
|
|
|
|
|
9670
|
0
|
|
|
|
|
0
|
my ($publicKey, $invalidReason, $storeError) = $keyPair->getPublicKey($actor->hash, $store); |
9671
|
|
|
|
|
|
|
|
9672
|
0
|
0
|
|
|
|
0
|
if (defined $invalidReason) { |
9673
|
0
|
|
|
|
|
0
|
$o->{discoverer}->{delegate}->onDiscoverActorGroupInvalidPublicKey($actor->hash, $store, $invalidReason); |
9674
|
0
|
|
|
|
|
0
|
next; |
9675
|
|
|
|
|
|
|
} |
9676
|
|
|
|
|
|
|
|
9677
|
0
|
0
|
|
|
|
0
|
if (defined $storeError) { |
9678
|
0
|
|
|
|
|
0
|
$o->{discoverer}->{delegate}->onDiscoverActorGroupStoreError($store, $storeError); |
9679
|
0
|
|
|
|
|
0
|
next; |
9680
|
|
|
|
|
|
|
} |
9681
|
|
|
|
|
|
|
|
9682
|
0
|
|
|
|
|
0
|
push @$entrustedActors, CDS::ActorGroup::EntrustedActor->new(CDS::ActorOnStore->new($publicKey, $store), $actor->storeUrl); |
9683
|
|
|
|
|
|
|
} |
9684
|
|
|
|
|
|
|
|
9685
|
0
|
0
|
|
|
|
0
|
my $members = [sort { $b->{revision} <=> $a->{revision} || $b->{status} cmp $a->{status} } @members]; |
|
0
|
|
|
|
|
0
|
|
9686
|
0
|
|
|
|
|
0
|
return CDS::ActorGroup->new($members, $parser->entrustedActorsRevision, $entrustedActors), [@cards], [grep { $_->{attachedToUs} } values %{$o->{nodesByUrl}}]; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
9687
|
|
|
|
|
|
|
} |
9688
|
|
|
|
|
|
|
|
9689
|
|
|
|
|
|
|
sub node { |
9690
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9691
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
9692
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
9693
|
|
|
|
|
|
|
# private |
9694
|
0
|
|
|
|
|
0
|
my $url = $storeUrl.'/accounts/'.$actorHash->hex; |
9695
|
0
|
|
|
|
|
0
|
my $node = $o->{nodesByUrl}->{$url}; |
9696
|
0
|
0
|
|
|
|
0
|
return $node if $node; |
9697
|
0
|
|
|
|
|
0
|
return $o->{nodesByUrl}->{$url} = CDS::DiscoverActorGroup::Node->new($o, $actorHash, $storeUrl); |
9698
|
|
|
|
|
|
|
} |
9699
|
|
|
|
|
|
|
|
9700
|
|
|
|
|
|
|
sub covers { |
9701
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9702
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
9703
|
0
|
|
|
|
|
0
|
$o->{coverage}->{$hash->bytes} } |
9704
|
|
|
|
|
|
|
|
9705
|
|
|
|
|
|
|
sub extend { |
9706
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9707
|
|
|
|
|
|
|
|
9708
|
|
|
|
|
|
|
# Start with the newest node |
9709
|
0
|
|
|
|
|
0
|
my $mainNode; |
9710
|
0
|
|
|
|
|
0
|
my $mainRevision = -1; |
9711
|
0
|
|
|
|
|
0
|
for my $node (values %{$o->{nodesByUrl}}) { |
|
0
|
|
|
|
|
0
|
|
9712
|
0
|
0
|
|
|
|
0
|
next if ! $node->{attachedToUs}; |
9713
|
0
|
0
|
|
|
|
0
|
next if $node->{revision} <= $mainRevision; |
9714
|
0
|
|
|
|
|
0
|
$mainNode = $node; |
9715
|
0
|
|
|
|
|
0
|
$mainRevision = $node->{revision}; |
9716
|
|
|
|
|
|
|
} |
9717
|
|
|
|
|
|
|
|
9718
|
0
|
0
|
|
|
|
0
|
return 0 if ! $mainNode; |
9719
|
|
|
|
|
|
|
|
9720
|
|
|
|
|
|
|
# Reset the reachable flag |
9721
|
0
|
|
|
|
|
0
|
for my $node (values %{$o->{nodesByUrl}}) { |
|
0
|
|
|
|
|
0
|
|
9722
|
0
|
|
|
|
|
0
|
$node->{reachable} = 0; |
9723
|
|
|
|
|
|
|
} |
9724
|
0
|
|
|
|
|
0
|
$mainNode->{reachable} = 1; |
9725
|
|
|
|
|
|
|
|
9726
|
|
|
|
|
|
|
# Traverse the graph along active links to find accounts to discover. |
9727
|
0
|
|
|
|
|
0
|
my @toDiscover; |
9728
|
0
|
|
|
|
|
0
|
my @toCheck = ($mainNode); |
9729
|
0
|
|
|
|
|
0
|
while (1) { |
9730
|
0
|
|
0
|
|
|
0
|
my $currentNode = shift(@toCheck) // last; |
9731
|
0
|
|
|
|
|
0
|
for my $link (@{$currentNode->{links}}) { |
|
0
|
|
|
|
|
0
|
|
9732
|
0
|
|
|
|
|
0
|
my $node = $link->{node}; |
9733
|
0
|
0
|
|
|
|
0
|
next if $node->{reachable}; |
9734
|
0
|
0
|
|
|
|
0
|
my $prospectiveStatus = $link->{revision} > $node->{revision} ? $link->{status} : $node->{status}; |
9735
|
0
|
0
|
|
|
|
0
|
next if $prospectiveStatus ne 'active'; |
9736
|
0
|
|
|
|
|
0
|
$node->{reachable} = 1; |
9737
|
0
|
0
|
|
|
|
0
|
push @toCheck, $node if $node->{attachedToUs}; |
9738
|
0
|
0
|
|
|
|
0
|
push @toDiscover, $node if ! $node->{attachedToUs}; |
9739
|
|
|
|
|
|
|
} |
9740
|
|
|
|
|
|
|
} |
9741
|
|
|
|
|
|
|
|
9742
|
|
|
|
|
|
|
# Discover these accounts |
9743
|
0
|
|
|
|
|
0
|
my $hasChanges = 0; |
9744
|
0
|
|
|
|
|
0
|
for my $node (sort { $b->{revision} <=> $a->{revision} } @toDiscover) { |
|
0
|
|
|
|
|
0
|
|
9745
|
0
|
|
|
|
|
0
|
$node->discover; |
9746
|
0
|
0
|
|
|
|
0
|
next if ! $node->{attachedToUs}; |
9747
|
0
|
|
|
|
|
0
|
$hasChanges = 1; |
9748
|
|
|
|
|
|
|
} |
9749
|
|
|
|
|
|
|
|
9750
|
0
|
|
|
|
|
0
|
return $hasChanges; |
9751
|
|
|
|
|
|
|
} |
9752
|
|
|
|
|
|
|
|
9753
|
|
|
|
|
|
|
package CDS::DiscoverActorGroup::Card; |
9754
|
|
|
|
|
|
|
|
9755
|
|
|
|
|
|
|
sub new { |
9756
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9757
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
9758
|
0
|
0
|
0
|
|
|
0
|
my $actorOnStore = shift; die 'wrong type '.ref($actorOnStore).' for $actorOnStore' if defined $actorOnStore && ref $actorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
0
|
|
9759
|
0
|
0
|
0
|
|
|
0
|
my $envelopeHash = shift; die 'wrong type '.ref($envelopeHash).' for $envelopeHash' if defined $envelopeHash && ref $envelopeHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
9760
|
0
|
0
|
0
|
|
|
0
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
9761
|
0
|
0
|
0
|
|
|
0
|
my $cardHash = shift; die 'wrong type '.ref($cardHash).' for $cardHash' if defined $cardHash && ref $cardHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
9762
|
0
|
|
|
|
|
0
|
my $card = shift; |
9763
|
|
|
|
|
|
|
|
9764
|
0
|
|
|
|
|
0
|
return bless { |
9765
|
|
|
|
|
|
|
storeUrl => $storeUrl, |
9766
|
|
|
|
|
|
|
actorOnStore => $actorOnStore, |
9767
|
|
|
|
|
|
|
envelopeHash => $envelopeHash, |
9768
|
|
|
|
|
|
|
envelope => $envelope, |
9769
|
|
|
|
|
|
|
cardHash => $cardHash, |
9770
|
|
|
|
|
|
|
card => $card, |
9771
|
|
|
|
|
|
|
}; |
9772
|
|
|
|
|
|
|
} |
9773
|
|
|
|
|
|
|
|
9774
|
0
|
|
|
0
|
|
0
|
sub storeUrl { shift->{storeUrl} } |
9775
|
0
|
|
|
0
|
|
0
|
sub actorOnStore { shift->{actorOnStore} } |
9776
|
0
|
|
|
0
|
|
0
|
sub envelopeHash { shift->{envelopeHash} } |
9777
|
0
|
|
|
0
|
|
0
|
sub envelope { shift->{envelope} } |
9778
|
0
|
|
|
0
|
|
0
|
sub cardHash { shift->{cardHash} } |
9779
|
0
|
|
|
0
|
|
0
|
sub card { shift->{card} } |
9780
|
|
|
|
|
|
|
|
9781
|
|
|
|
|
|
|
package CDS::DiscoverActorGroup::Link; |
9782
|
|
|
|
|
|
|
|
9783
|
|
|
|
|
|
|
sub new { |
9784
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9785
|
0
|
|
|
|
|
0
|
my $node = shift; |
9786
|
0
|
|
|
|
|
0
|
my $revision = shift; |
9787
|
0
|
|
|
|
|
0
|
my $status = shift; |
9788
|
|
|
|
|
|
|
|
9789
|
0
|
|
|
|
|
0
|
bless { |
9790
|
|
|
|
|
|
|
node => $node, |
9791
|
|
|
|
|
|
|
revision => $revision, |
9792
|
|
|
|
|
|
|
status => $status, |
9793
|
|
|
|
|
|
|
}; |
9794
|
|
|
|
|
|
|
} |
9795
|
|
|
|
|
|
|
|
9796
|
0
|
|
|
0
|
|
0
|
sub node { shift->{node} } |
9797
|
0
|
|
|
0
|
|
0
|
sub revision { shift->{revision} } |
9798
|
0
|
|
|
0
|
|
0
|
sub status { shift->{status} } |
9799
|
|
|
|
|
|
|
|
9800
|
|
|
|
|
|
|
package CDS::DiscoverActorGroup::Node; |
9801
|
|
|
|
|
|
|
|
9802
|
|
|
|
|
|
|
sub new { |
9803
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9804
|
0
|
|
|
|
|
0
|
my $discoverer = shift; |
9805
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
9806
|
0
|
|
|
|
|
0
|
my $storeUrl = shift; |
9807
|
|
|
|
|
|
|
|
9808
|
0
|
|
|
|
|
0
|
return bless { |
9809
|
|
|
|
|
|
|
discoverer => $discoverer, |
9810
|
|
|
|
|
|
|
actorHash => $actorHash, |
9811
|
|
|
|
|
|
|
storeUrl => $storeUrl, |
9812
|
|
|
|
|
|
|
revision => -1, |
9813
|
|
|
|
|
|
|
status => 'idle', |
9814
|
|
|
|
|
|
|
reachable => 0, # whether this node is reachable from the main node |
9815
|
|
|
|
|
|
|
store => undef, |
9816
|
|
|
|
|
|
|
actorOnStore => undef, |
9817
|
|
|
|
|
|
|
links => [], # all links found in the cards |
9818
|
|
|
|
|
|
|
attachedToUs => 0, # whether the account belongs to us |
9819
|
|
|
|
|
|
|
cardsRead => 0, |
9820
|
|
|
|
|
|
|
cards => [], |
9821
|
|
|
|
|
|
|
}; |
9822
|
|
|
|
|
|
|
} |
9823
|
|
|
|
|
|
|
|
9824
|
|
|
|
|
|
|
sub cards { |
9825
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9826
|
0
|
|
|
|
|
0
|
@{$o->{cards}} } |
|
0
|
|
|
|
|
0
|
|
9827
|
|
|
|
|
|
|
sub isActive { |
9828
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9829
|
0
|
|
|
|
|
0
|
$o->{status} eq 'active' } |
9830
|
|
|
|
|
|
|
sub isActiveOrIdle { |
9831
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9832
|
0
|
0
|
|
|
|
0
|
$o->{status} eq 'active' || $o->{status} eq 'idle' } |
9833
|
|
|
|
|
|
|
|
9834
|
0
|
|
|
0
|
|
0
|
sub actorHash { shift->{actorHash} } |
9835
|
0
|
|
|
0
|
|
0
|
sub storeUrl { shift->{storeUrl} } |
9836
|
0
|
|
|
0
|
|
0
|
sub revision { shift->{revision} } |
9837
|
0
|
|
|
0
|
|
0
|
sub status { shift->{status} } |
9838
|
0
|
|
|
0
|
|
0
|
sub attachedToUs { shift->{attachedToUs} } |
9839
|
|
|
|
|
|
|
sub links { |
9840
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9841
|
0
|
|
|
|
|
0
|
@{$o->{links}} } |
|
0
|
|
|
|
|
0
|
|
9842
|
|
|
|
|
|
|
|
9843
|
|
|
|
|
|
|
sub discover { |
9844
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9845
|
|
|
|
|
|
|
|
9846
|
|
|
|
|
|
|
#-- discover ++ $o->{actorHash}->hex |
9847
|
0
|
|
|
|
|
0
|
$o->readCards; |
9848
|
0
|
|
|
|
|
0
|
$o->attach; |
9849
|
|
|
|
|
|
|
} |
9850
|
|
|
|
|
|
|
|
9851
|
|
|
|
|
|
|
sub readCards { |
9852
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9853
|
|
|
|
|
|
|
|
9854
|
0
|
0
|
|
|
|
0
|
return if $o->{cardsRead}; |
9855
|
0
|
|
|
|
|
0
|
$o->{cardsRead} = 1; |
9856
|
|
|
|
|
|
|
#-- read cards of ++ $o->{actorHash}->hex |
9857
|
|
|
|
|
|
|
|
9858
|
|
|
|
|
|
|
# Get the store |
9859
|
0
|
|
0
|
|
|
0
|
my $store = $o->{discoverer}->{delegate}->onDiscoverActorGroupVerifyStore($o->{storeUrl}, $o->{actorHash}) // return; |
9860
|
|
|
|
|
|
|
|
9861
|
|
|
|
|
|
|
# Get the public key if necessary |
9862
|
0
|
0
|
|
|
|
0
|
if (! $o->{actorOnStore}) { |
9863
|
0
|
|
|
|
|
0
|
my $publicKey = $o->{discoverer}->{knownPublicKeys}->{$o->{actorHash}->bytes}; |
9864
|
0
|
0
|
|
|
|
0
|
if (! $publicKey) { |
9865
|
0
|
|
|
|
|
0
|
my ($downloadedPublicKey, $invalidReason, $storeError) = $o->{discoverer}->{keyPair}->getPublicKey($o->{actorHash}, $store); |
9866
|
0
|
0
|
|
|
|
0
|
return $o->{discoverer}->{delegate}->onDiscoverActorGroupStoreError($store, $storeError) if defined $storeError; |
9867
|
0
|
0
|
|
|
|
0
|
return $o->{discoverer}->{delegate}->onDiscoverActorGroupInvalidPublicKey($o->{actorHash}, $store, $invalidReason) if defined $invalidReason; |
9868
|
0
|
|
|
|
|
0
|
$publicKey = $downloadedPublicKey; |
9869
|
|
|
|
|
|
|
} |
9870
|
|
|
|
|
|
|
|
9871
|
0
|
|
|
|
|
0
|
$o->{actorOnStore} = CDS::ActorOnStore->new($publicKey, $store); |
9872
|
|
|
|
|
|
|
} |
9873
|
|
|
|
|
|
|
|
9874
|
|
|
|
|
|
|
# List the public box |
9875
|
0
|
|
|
|
|
0
|
my ($hashes, $storeError) = $store->list($o->{actorHash}, 'public', 0, $o->{discoverer}->{keyPair}); |
9876
|
0
|
0
|
|
|
|
0
|
return $o->{discoverer}->{delegate}->onDiscoverActorGroupStoreError($store, $storeError) if defined $storeError; |
9877
|
|
|
|
|
|
|
|
9878
|
0
|
|
|
|
|
0
|
for my $envelopeHash (@$hashes) { |
9879
|
|
|
|
|
|
|
# Open the envelope |
9880
|
0
|
|
|
|
|
0
|
my ($object, $storeError) = $store->get($envelopeHash, $o->{discoverer}->{keyPair}); |
9881
|
0
|
0
|
|
|
|
0
|
return $o->{discoverer}->{delegate}->onDiscoverActorGroupStoreError($store, $storeError) if defined $storeError; |
9882
|
0
|
0
|
|
|
|
0
|
if (! $object) { |
9883
|
0
|
|
|
|
|
0
|
$o->{discoverer}->{delegate}->onDiscoverActorGroupInvalidCard($o->{actorOnStore}, $envelopeHash, 'Envelope object not found.'); |
9884
|
0
|
|
|
|
|
0
|
next; |
9885
|
|
|
|
|
|
|
} |
9886
|
|
|
|
|
|
|
|
9887
|
0
|
|
|
|
|
0
|
my $envelope = CDS::Record->fromObject($object); |
9888
|
0
|
0
|
|
|
|
0
|
if (! $envelope) { |
9889
|
0
|
|
|
|
|
0
|
$o->{discoverer}->{delegate}->onDiscoverActorGroupInvalidCard($o->{actorOnStore}, $envelopeHash, 'Envelope is not a record.'); |
9890
|
0
|
|
|
|
|
0
|
next; |
9891
|
|
|
|
|
|
|
} |
9892
|
|
|
|
|
|
|
|
9893
|
0
|
|
|
|
|
0
|
my $cardHash = $envelope->child('content')->hashValue; |
9894
|
0
|
0
|
|
|
|
0
|
if (! $cardHash) { |
9895
|
0
|
|
|
|
|
0
|
$o->{discoverer}->{delegate}->onDiscoverActorGroupInvalidCard($o->{actorOnStore}, $envelopeHash, 'Missing content hash.'); |
9896
|
0
|
|
|
|
|
0
|
next; |
9897
|
|
|
|
|
|
|
} |
9898
|
|
|
|
|
|
|
|
9899
|
0
|
0
|
|
|
|
0
|
if (! CDS->verifyEnvelopeSignature($envelope, $o->{actorOnStore}->publicKey, $cardHash)) { |
9900
|
0
|
|
|
|
|
0
|
$o->{discoverer}->{delegate}->onDiscoverActorGroupInvalidCard($o->{actorOnStore}, $envelopeHash, 'Invalid signature.'); |
9901
|
0
|
|
|
|
|
0
|
next; |
9902
|
|
|
|
|
|
|
} |
9903
|
|
|
|
|
|
|
|
9904
|
|
|
|
|
|
|
# Read the card |
9905
|
0
|
|
|
|
|
0
|
my ($cardObject, $storeError1) = $store->get($cardHash, $o->{discoverer}->{keyPair}); |
9906
|
0
|
0
|
|
|
|
0
|
return $o->{discoverer}->{delegate}->onDiscoverActorGroupStoreError($store, $storeError) if defined $storeError1; |
9907
|
0
|
0
|
|
|
|
0
|
if (! $cardObject) { |
9908
|
0
|
|
|
|
|
0
|
$o->{discoverer}->{delegate}->onDiscoverActorGroupInvalidCard($o->{actorOnStore}, $envelopeHash, 'Card object not found.'); |
9909
|
0
|
|
|
|
|
0
|
next; |
9910
|
|
|
|
|
|
|
} |
9911
|
|
|
|
|
|
|
|
9912
|
0
|
|
|
|
|
0
|
my $card = CDS::Record->fromObject($cardObject); |
9913
|
0
|
0
|
|
|
|
0
|
if (! $card) { |
9914
|
0
|
|
|
|
|
0
|
$o->{discoverer}->{delegate}->onDiscoverActorGroupInvalidCard($o->{actorOnStore}, $envelopeHash, 'Card is not a record.'); |
9915
|
0
|
|
|
|
|
0
|
next; |
9916
|
|
|
|
|
|
|
} |
9917
|
|
|
|
|
|
|
|
9918
|
|
|
|
|
|
|
# Add the card to the list of cards |
9919
|
0
|
|
|
|
|
0
|
push @{$o->{cards}}, CDS::DiscoverActorGroup::Card->new($o->{storeUrl}, $o->{actorOnStore}, $envelopeHash, $envelope, $cardHash, $card); |
|
0
|
|
|
|
|
0
|
|
9920
|
|
|
|
|
|
|
|
9921
|
|
|
|
|
|
|
# Parse the account list |
9922
|
0
|
|
|
|
|
0
|
my $builder = CDS::ActorGroupBuilder->new; |
9923
|
0
|
|
|
|
|
0
|
$builder->parseMembers($card->child('actor group'), 0); |
9924
|
0
|
|
|
|
|
0
|
for my $member ($builder->members) { |
9925
|
0
|
|
|
|
|
0
|
my $node = $o->{discoverer}->node($member->hash, $member->storeUrl); |
9926
|
|
|
|
|
|
|
#-- new link ++ $o->{actorHash}->hex ++ $status ++ $hash->hex |
9927
|
0
|
|
|
|
|
0
|
push @{$o->{links}}, CDS::DiscoverActorGroup::Link->new($node, $member->revision, $member->status); |
|
0
|
|
|
|
|
0
|
|
9928
|
|
|
|
|
|
|
} |
9929
|
|
|
|
|
|
|
} |
9930
|
|
|
|
|
|
|
} |
9931
|
|
|
|
|
|
|
|
9932
|
|
|
|
|
|
|
sub attach { |
9933
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9934
|
|
|
|
|
|
|
|
9935
|
0
|
0
|
|
|
|
0
|
return if $o->{attachedToUs}; |
9936
|
0
|
0
|
|
|
|
0
|
return if ! $o->hasLinkToUs; |
9937
|
|
|
|
|
|
|
|
9938
|
|
|
|
|
|
|
# Attach this node |
9939
|
0
|
|
|
|
|
0
|
$o->{attachedToUs} = 1; |
9940
|
|
|
|
|
|
|
|
9941
|
|
|
|
|
|
|
# Merge all links |
9942
|
0
|
|
|
|
|
0
|
for my $link (@{$o->{links}}) { |
|
0
|
|
|
|
|
0
|
|
9943
|
0
|
|
|
|
|
0
|
$link->{node}->merge($link->{revision}, $link->{status}); |
9944
|
|
|
|
|
|
|
} |
9945
|
|
|
|
|
|
|
|
9946
|
|
|
|
|
|
|
# Add the hash to the coverage |
9947
|
0
|
|
|
|
|
0
|
$o->{discoverer}->{coverage}->{$o->{actorHash}->bytes} = 1; |
9948
|
|
|
|
|
|
|
} |
9949
|
|
|
|
|
|
|
|
9950
|
|
|
|
|
|
|
sub merge { |
9951
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9952
|
0
|
|
|
|
|
0
|
my $revision = shift; |
9953
|
0
|
|
|
|
|
0
|
my $status = shift; |
9954
|
|
|
|
|
|
|
|
9955
|
0
|
0
|
|
|
|
0
|
return if $o->{revision} >= $revision; |
9956
|
0
|
|
|
|
|
0
|
$o->{revision} = $revision; |
9957
|
0
|
|
|
|
|
0
|
$o->{status} = $status; |
9958
|
|
|
|
|
|
|
} |
9959
|
|
|
|
|
|
|
|
9960
|
|
|
|
|
|
|
sub hasLinkToUs { |
9961
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9962
|
|
|
|
|
|
|
|
9963
|
0
|
0
|
|
|
|
0
|
return 1 if $o->{discoverer}->covers($o->{actorHash}); |
9964
|
0
|
|
|
|
|
0
|
for my $link (@{$o->{links}}) { |
|
0
|
|
|
|
|
0
|
|
9965
|
0
|
0
|
|
|
|
0
|
return 1 if $o->{discoverer}->covers($link->{node}->{actorHash}); |
9966
|
|
|
|
|
|
|
} |
9967
|
0
|
|
|
|
|
0
|
return; |
9968
|
|
|
|
|
|
|
} |
9969
|
|
|
|
|
|
|
|
9970
|
|
|
|
|
|
|
package CDS::Document; |
9971
|
|
|
|
|
|
|
|
9972
|
|
|
|
|
|
|
sub new { |
9973
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
9974
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
9975
|
0
|
|
|
|
|
0
|
my $store = shift; |
9976
|
|
|
|
|
|
|
|
9977
|
0
|
|
|
|
|
0
|
my $o = bless { |
9978
|
|
|
|
|
|
|
keyPair => $keyPair, |
9979
|
|
|
|
|
|
|
unsaved => CDS::Unsaved->new($store), |
9980
|
|
|
|
|
|
|
itemsBySelector => {}, |
9981
|
|
|
|
|
|
|
parts => {}, |
9982
|
|
|
|
|
|
|
hasPartsToMerge => 0, |
9983
|
|
|
|
|
|
|
}, $class; |
9984
|
|
|
|
|
|
|
|
9985
|
0
|
|
|
|
|
0
|
$o->{root} = CDS::Selector->root($o); |
9986
|
0
|
|
|
|
|
0
|
$o->{changes} = CDS::Document::Part->new; |
9987
|
0
|
|
|
|
|
0
|
return $o; |
9988
|
|
|
|
|
|
|
} |
9989
|
|
|
|
|
|
|
|
9990
|
0
|
|
|
0
|
|
0
|
sub keyPair { shift->{keyPair} } |
9991
|
0
|
|
|
0
|
|
0
|
sub unsaved { shift->{unsaved} } |
9992
|
|
|
|
|
|
|
sub parts { |
9993
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
9994
|
0
|
|
|
|
|
0
|
values %{$o->{parts}} } |
|
0
|
|
|
|
|
0
|
|
9995
|
0
|
|
|
0
|
|
0
|
sub hasPartsToMerge { shift->{hasPartsToMerge} } |
9996
|
|
|
|
|
|
|
|
9997
|
|
|
|
|
|
|
### Items |
9998
|
|
|
|
|
|
|
|
9999
|
0
|
|
|
0
|
|
0
|
sub root { shift->{root} } |
10000
|
|
|
|
|
|
|
sub rootItem { |
10001
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10002
|
0
|
|
|
|
|
0
|
$o->getOrCreate($o->{root}) } |
10003
|
|
|
|
|
|
|
|
10004
|
|
|
|
|
|
|
sub get { |
10005
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10006
|
0
|
0
|
0
|
|
|
0
|
my $selector = shift; die 'wrong type '.ref($selector).' for $selector' if defined $selector && ref $selector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
0
|
|
10007
|
0
|
|
|
|
|
0
|
$o->{itemsBySelector}->{$selector->{id}} } |
10008
|
|
|
|
|
|
|
|
10009
|
|
|
|
|
|
|
sub getOrCreate { |
10010
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10011
|
0
|
0
|
0
|
|
|
0
|
my $selector = shift; die 'wrong type '.ref($selector).' for $selector' if defined $selector && ref $selector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
0
|
|
10012
|
|
|
|
|
|
|
|
10013
|
0
|
|
|
|
|
0
|
my $item = $o->{itemsBySelector}->{$selector->{id}}; |
10014
|
0
|
0
|
|
|
|
0
|
$o->{itemsBySelector}->{$selector->{id}} = $item = CDS::Document::Item->new($selector) if ! $item; |
10015
|
0
|
|
|
|
|
0
|
return $item; |
10016
|
|
|
|
|
|
|
} |
10017
|
|
|
|
|
|
|
|
10018
|
|
|
|
|
|
|
sub prune { |
10019
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10020
|
0
|
|
|
|
|
0
|
$o->rootItem->pruneTree; } |
10021
|
|
|
|
|
|
|
|
10022
|
|
|
|
|
|
|
### Merging |
10023
|
|
|
|
|
|
|
|
10024
|
|
|
|
|
|
|
sub merge { |
10025
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10026
|
|
|
|
|
|
|
|
10027
|
0
|
|
|
|
|
0
|
for my $hashAndKey (@_) { |
10028
|
0
|
0
|
|
|
|
0
|
next if ! $hashAndKey; |
10029
|
0
|
0
|
|
|
|
0
|
next if $o->{parts}->{$hashAndKey->hash->bytes}; |
10030
|
0
|
|
|
|
|
0
|
my $part = CDS::Document::Part->new; |
10031
|
0
|
|
|
|
|
0
|
$part->{hashAndKey} = $hashAndKey; |
10032
|
0
|
|
|
|
|
0
|
$o->{parts}->{$hashAndKey->hash->bytes} = $part; |
10033
|
0
|
|
|
|
|
0
|
$o->{hasPartsToMerge} = 1; |
10034
|
|
|
|
|
|
|
} |
10035
|
|
|
|
|
|
|
} |
10036
|
|
|
|
|
|
|
|
10037
|
|
|
|
|
|
|
sub read { |
10038
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10039
|
|
|
|
|
|
|
|
10040
|
0
|
0
|
|
|
|
0
|
return 1 if ! $o->{hasPartsToMerge}; |
10041
|
|
|
|
|
|
|
|
10042
|
|
|
|
|
|
|
# Load the parts |
10043
|
0
|
|
|
|
|
0
|
for my $part (values %{$o->{parts}}) { |
|
0
|
|
|
|
|
0
|
|
10044
|
0
|
0
|
|
|
|
0
|
next if $part->{isMerged}; |
10045
|
0
|
0
|
|
|
|
0
|
next if $part->{loadedRecord}; |
10046
|
|
|
|
|
|
|
|
10047
|
0
|
|
|
|
|
0
|
my ($record, $object, $invalidReason, $storeError) = $o->{keyPair}->getAndDecryptRecord($part->{hashAndKey}, $o->{unsaved}); |
10048
|
0
|
0
|
|
|
|
0
|
return if defined $storeError; |
10049
|
|
|
|
|
|
|
|
10050
|
0
|
0
|
|
|
|
0
|
delete $o->{parts}->{$part->{hashAndKey}->hash->bytes} if defined $invalidReason; |
10051
|
0
|
|
|
|
|
0
|
$part->{loadedRecord} = $record; |
10052
|
|
|
|
|
|
|
} |
10053
|
|
|
|
|
|
|
|
10054
|
|
|
|
|
|
|
# Merge the loaded parts |
10055
|
0
|
|
|
|
|
0
|
for my $part (values %{$o->{parts}}) { |
|
0
|
|
|
|
|
0
|
|
10056
|
0
|
0
|
|
|
|
0
|
next if $part->{isMerged}; |
10057
|
0
|
0
|
|
|
|
0
|
next if ! $part->{loadedRecord}; |
10058
|
0
|
0
|
|
|
|
0
|
my $oldFormat = $part->{loadedRecord}->child('client')->textValue =~ /0.19/ ? 1 : 0; |
10059
|
0
|
|
|
|
|
0
|
$o->mergeNode($part, $o->{root}, $part->{loadedRecord}->child('root'), $oldFormat); |
10060
|
0
|
|
|
|
|
0
|
delete $part->{loadedRecord}; |
10061
|
0
|
|
|
|
|
0
|
$part->{isMerged} = 1; |
10062
|
|
|
|
|
|
|
} |
10063
|
|
|
|
|
|
|
|
10064
|
0
|
|
|
|
|
0
|
$o->{hasPartsToMerge} = 0; |
10065
|
0
|
|
|
|
|
0
|
return 1; |
10066
|
|
|
|
|
|
|
} |
10067
|
|
|
|
|
|
|
|
10068
|
|
|
|
|
|
|
sub mergeNode { |
10069
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10070
|
0
|
|
|
|
|
0
|
my $part = shift; |
10071
|
0
|
0
|
0
|
|
|
0
|
my $selector = shift; die 'wrong type '.ref($selector).' for $selector' if defined $selector && ref $selector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
0
|
|
10072
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
10073
|
0
|
|
|
|
|
0
|
my $oldFormat = shift; |
10074
|
|
|
|
|
|
|
|
10075
|
|
|
|
|
|
|
# Prepare |
10076
|
0
|
|
|
|
|
0
|
my @children = $record->children; |
10077
|
0
|
0
|
|
|
|
0
|
return if ! scalar @children; |
10078
|
0
|
|
|
|
|
0
|
my $item = $o->getOrCreate($selector); |
10079
|
|
|
|
|
|
|
|
10080
|
|
|
|
|
|
|
# Merge value |
10081
|
0
|
|
|
|
|
0
|
my $valueRecord = shift @children; |
10082
|
0
|
0
|
|
|
|
0
|
$valueRecord = $valueRecord->firstChild if $oldFormat; |
10083
|
0
|
|
|
|
|
0
|
$item->mergeValue($part, $valueRecord->asInteger, $valueRecord); |
10084
|
|
|
|
|
|
|
|
10085
|
|
|
|
|
|
|
# Merge children |
10086
|
0
|
|
|
|
|
0
|
for my $child (@children) { $o->mergeNode($part, $selector->child($child->bytes), $child, $oldFormat); } |
|
0
|
|
|
|
|
0
|
|
10087
|
|
|
|
|
|
|
} |
10088
|
|
|
|
|
|
|
|
10089
|
|
|
|
|
|
|
# *** Saving |
10090
|
|
|
|
|
|
|
# Call $document->save at any time to save the current state (if necessary). |
10091
|
|
|
|
|
|
|
|
10092
|
|
|
|
|
|
|
# This is called by the items whenever some data changes. |
10093
|
|
|
|
|
|
|
sub dataChanged { |
10094
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10095
|
|
|
|
|
|
|
} |
10096
|
|
|
|
|
|
|
|
10097
|
|
|
|
|
|
|
sub save { |
10098
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10099
|
|
|
|
|
|
|
|
10100
|
0
|
|
|
|
|
0
|
$o->{unsaved}->startSaving; |
10101
|
0
|
|
|
|
|
0
|
my $revision = CDS->now; |
10102
|
0
|
|
|
|
|
0
|
my $newPart = undef; |
10103
|
|
|
|
|
|
|
|
10104
|
|
|
|
|
|
|
#-- saving ++ $o->{changes}->{count} |
10105
|
0
|
0
|
|
|
|
0
|
if ($o->{changes}->{count}) { |
10106
|
|
|
|
|
|
|
# Take the changes |
10107
|
0
|
|
|
|
|
0
|
$newPart = $o->{changes}; |
10108
|
0
|
|
|
|
|
0
|
$o->{changes} = CDS::Document::Part->new; |
10109
|
|
|
|
|
|
|
|
10110
|
|
|
|
|
|
|
# Select all parts smaller than 2 * changes |
10111
|
0
|
|
|
|
|
0
|
$newPart->{selected} = 1; |
10112
|
0
|
|
|
|
|
0
|
my $count = $newPart->{count}; |
10113
|
0
|
|
|
|
|
0
|
while (1) { |
10114
|
0
|
|
|
|
|
0
|
my $addedPart = 0; |
10115
|
0
|
|
|
|
|
0
|
for my $part (values %{$o->{parts}}) { |
|
0
|
|
|
|
|
0
|
|
10116
|
|
|
|
|
|
|
#-- candidate ++ $part->{count} ++ $count |
10117
|
0
|
0
|
0
|
|
|
0
|
next if ! $part->{isMerged} || $part->{selected} || $part->{count} >= $count * 2; |
|
|
|
0
|
|
|
|
|
10118
|
0
|
|
|
|
|
0
|
$count += $part->{count}; |
10119
|
0
|
|
|
|
|
0
|
$part->{selected} = 1; |
10120
|
0
|
|
|
|
|
0
|
$addedPart = 1; |
10121
|
|
|
|
|
|
|
} |
10122
|
|
|
|
|
|
|
|
10123
|
0
|
0
|
|
|
|
0
|
last if ! $addedPart; |
10124
|
|
|
|
|
|
|
} |
10125
|
|
|
|
|
|
|
|
10126
|
|
|
|
|
|
|
# Include the selected items |
10127
|
0
|
|
|
|
|
0
|
for my $item (values %{$o->{itemsBySelector}}) { |
|
0
|
|
|
|
|
0
|
|
10128
|
0
|
0
|
|
|
|
0
|
next if ! $item->{part}->{selected}; |
10129
|
0
|
|
|
|
|
0
|
$item->setPart($newPart); |
10130
|
0
|
|
|
|
|
0
|
$item->createSaveRecord; |
10131
|
|
|
|
|
|
|
} |
10132
|
|
|
|
|
|
|
|
10133
|
0
|
|
|
|
|
0
|
my $record = CDS::Record->new; |
10134
|
0
|
|
|
|
|
0
|
$record->add('created')->addInteger($revision); |
10135
|
0
|
|
|
|
|
0
|
$record->add('client')->add(CDS->version); |
10136
|
0
|
|
|
|
|
0
|
$record->addRecord($o->rootItem->createSaveRecord); |
10137
|
|
|
|
|
|
|
|
10138
|
|
|
|
|
|
|
# Detach the save records |
10139
|
0
|
|
|
|
|
0
|
for my $item (values %{$o->{itemsBySelector}}) { |
|
0
|
|
|
|
|
0
|
|
10140
|
0
|
|
|
|
|
0
|
$item->detachSaveRecord; |
10141
|
|
|
|
|
|
|
} |
10142
|
|
|
|
|
|
|
|
10143
|
|
|
|
|
|
|
# Serialize and encrypt the record |
10144
|
0
|
|
|
|
|
0
|
my $key = CDS->randomKey; |
10145
|
0
|
|
|
|
|
0
|
my $newObject = $record->toObject->crypt($key); |
10146
|
0
|
|
|
|
|
0
|
$newPart->{hashAndKey} = CDS::HashAndKey->new($newObject->calculateHash, $key); |
10147
|
0
|
|
|
|
|
0
|
$newPart->{isMerged} = 1; |
10148
|
0
|
|
|
|
|
0
|
$newPart->{selected} = 0; |
10149
|
0
|
|
|
|
|
0
|
$o->{parts}->{$newPart->{hashAndKey}->hash->bytes} = $newPart; |
10150
|
|
|
|
|
|
|
#-- added ++ $o->{parts} ++ scalar keys %{$o->{parts}} ++ $newPart->{count} |
10151
|
0
|
|
|
|
|
0
|
$o->{unsaved}->{savingState}->addObject($newPart->{hashAndKey}->hash, $newObject); |
10152
|
|
|
|
|
|
|
} |
10153
|
|
|
|
|
|
|
|
10154
|
|
|
|
|
|
|
# Remove obsolete parts |
10155
|
0
|
|
|
|
|
0
|
my $obsoleteParts = []; |
10156
|
0
|
|
|
|
|
0
|
for my $part (values %{$o->{parts}}) { |
|
0
|
|
|
|
|
0
|
|
10157
|
0
|
0
|
|
|
|
0
|
next if ! $part->{isMerged}; |
10158
|
0
|
0
|
|
|
|
0
|
next if $part->{count}; |
10159
|
0
|
|
|
|
|
0
|
push @$obsoleteParts, $part; |
10160
|
0
|
|
|
|
|
0
|
delete $o->{parts}->{$part->{hashAndKey}->hash->bytes}; |
10161
|
|
|
|
|
|
|
} |
10162
|
|
|
|
|
|
|
|
10163
|
|
|
|
|
|
|
# Commit |
10164
|
|
|
|
|
|
|
#-- saving done ++ $revision ++ $newPart ++ $obsoleteParts |
10165
|
0
|
|
|
|
|
0
|
return $o->savingDone($revision, $newPart, $obsoleteParts); |
10166
|
|
|
|
|
|
|
} |
10167
|
|
|
|
|
|
|
|
10168
|
|
|
|
|
|
|
package CDS::Document::Item; |
10169
|
|
|
|
|
|
|
|
10170
|
|
|
|
|
|
|
sub new { |
10171
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10172
|
0
|
0
|
0
|
|
|
0
|
my $selector = shift; die 'wrong type '.ref($selector).' for $selector' if defined $selector && ref $selector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
0
|
|
10173
|
|
|
|
|
|
|
|
10174
|
0
|
|
|
|
|
0
|
my $parentSelector = $selector->parent; |
10175
|
0
|
0
|
|
|
|
0
|
my $parent = $parentSelector ? $selector->document->getOrCreate($parentSelector) : undef; |
10176
|
|
|
|
|
|
|
|
10177
|
0
|
|
|
|
|
0
|
my $o = bless { |
10178
|
|
|
|
|
|
|
document => $selector->document, |
10179
|
|
|
|
|
|
|
selector => $selector, |
10180
|
|
|
|
|
|
|
parent => $parent, |
10181
|
|
|
|
|
|
|
children => [], |
10182
|
|
|
|
|
|
|
part => undef, |
10183
|
|
|
|
|
|
|
revision => 0, |
10184
|
|
|
|
|
|
|
record => CDS::Record->new |
10185
|
|
|
|
|
|
|
}; |
10186
|
|
|
|
|
|
|
|
10187
|
0
|
0
|
|
|
|
0
|
push @{$parent->{children}}, $o if $parent; |
|
0
|
|
|
|
|
0
|
|
10188
|
0
|
|
|
|
|
0
|
return $o; |
10189
|
|
|
|
|
|
|
} |
10190
|
|
|
|
|
|
|
|
10191
|
|
|
|
|
|
|
sub pruneTree { |
10192
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10193
|
|
|
|
|
|
|
|
10194
|
|
|
|
|
|
|
# Try to remove children |
10195
|
0
|
|
|
|
|
0
|
for my $child (@{$o->{children}}) { $child->pruneTree; } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
10196
|
|
|
|
|
|
|
|
10197
|
|
|
|
|
|
|
# Don't remove the root item |
10198
|
0
|
0
|
|
|
|
0
|
return if ! $o->{parent}; |
10199
|
|
|
|
|
|
|
|
10200
|
|
|
|
|
|
|
# Don't remove if the item has children, or a value |
10201
|
0
|
0
|
|
|
|
0
|
return if scalar @{$o->{children}}; |
|
0
|
|
|
|
|
0
|
|
10202
|
0
|
0
|
|
|
|
0
|
return if $o->{revision} > 0; |
10203
|
|
|
|
|
|
|
|
10204
|
|
|
|
|
|
|
# Remove this from the tree |
10205
|
0
|
|
|
|
|
0
|
$o->{parent}->{children} = [grep { $_ != $o } @{$o->{parent}->{children}}]; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
10206
|
|
|
|
|
|
|
|
10207
|
|
|
|
|
|
|
# Remove this from the document hash |
10208
|
0
|
|
|
|
|
0
|
delete $o->{document}->{itemsBySelector}->{$o->{selector}->{id}}; |
10209
|
|
|
|
|
|
|
} |
10210
|
|
|
|
|
|
|
|
10211
|
|
|
|
|
|
|
# Low-level part change. |
10212
|
|
|
|
|
|
|
sub setPart { |
10213
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10214
|
0
|
|
|
|
|
0
|
my $part = shift; |
10215
|
|
|
|
|
|
|
|
10216
|
0
|
0
|
|
|
|
0
|
$o->{part}->{count} -= 1 if $o->{part}; |
10217
|
0
|
|
|
|
|
0
|
$o->{part} = $part; |
10218
|
0
|
0
|
|
|
|
0
|
$o->{part}->{count} += 1 if $o->{part}; |
10219
|
|
|
|
|
|
|
} |
10220
|
|
|
|
|
|
|
|
10221
|
|
|
|
|
|
|
# Merge a value |
10222
|
|
|
|
|
|
|
|
10223
|
|
|
|
|
|
|
sub mergeValue { |
10224
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10225
|
0
|
|
|
|
|
0
|
my $part = shift; |
10226
|
0
|
|
|
|
|
0
|
my $revision = shift; |
10227
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
10228
|
|
|
|
|
|
|
|
10229
|
0
|
0
|
|
|
|
0
|
return if $revision <= 0; |
10230
|
0
|
0
|
|
|
|
0
|
return if $revision < $o->{revision}; |
10231
|
0
|
0
|
0
|
|
|
0
|
return if $revision == $o->{revision} && $part->{size} < $o->{part}->{size}; |
10232
|
0
|
|
|
|
|
0
|
$o->setPart($part); |
10233
|
0
|
|
|
|
|
0
|
$o->{revision} = $revision; |
10234
|
0
|
|
|
|
|
0
|
$o->{record} = $record; |
10235
|
0
|
|
|
|
|
0
|
$o->{document}->dataChanged; |
10236
|
0
|
|
|
|
|
0
|
return 1; |
10237
|
|
|
|
|
|
|
} |
10238
|
|
|
|
|
|
|
|
10239
|
|
|
|
|
|
|
sub forget { |
10240
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10241
|
|
|
|
|
|
|
|
10242
|
0
|
0
|
|
|
|
0
|
return if $o->{revision} <= 0; |
10243
|
0
|
|
|
|
|
0
|
$o->{revision} = 0; |
10244
|
0
|
|
|
|
|
0
|
$o->{record} = CDS::Record->new; |
10245
|
0
|
|
|
|
|
0
|
$o->setPart; |
10246
|
|
|
|
|
|
|
} |
10247
|
|
|
|
|
|
|
|
10248
|
|
|
|
|
|
|
# Saving |
10249
|
|
|
|
|
|
|
|
10250
|
|
|
|
|
|
|
sub createSaveRecord { |
10251
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10252
|
|
|
|
|
|
|
|
10253
|
0
|
0
|
|
|
|
0
|
return $o->{saveRecord} if $o->{saveRecord}; |
10254
|
0
|
0
|
|
|
|
0
|
$o->{saveRecord} = $o->{parent} ? $o->{parent}->createSaveRecord->add($o->{selector}->{label}) : CDS::Record->new('root'); |
10255
|
0
|
0
|
|
|
|
0
|
if ($o->{part}->{selected}) { |
10256
|
0
|
0
|
|
|
|
0
|
CDS->log('Item saving zero revision of ', $o->{selector}->label) if $o->{revision} <= 0; |
10257
|
0
|
|
|
|
|
0
|
$o->{saveRecord}->addInteger($o->{revision})->addRecord($o->{record}->children); |
10258
|
|
|
|
|
|
|
} else { |
10259
|
0
|
|
|
|
|
0
|
$o->{saveRecord}->add(''); |
10260
|
|
|
|
|
|
|
} |
10261
|
0
|
|
|
|
|
0
|
return $o->{saveRecord}; |
10262
|
|
|
|
|
|
|
} |
10263
|
|
|
|
|
|
|
|
10264
|
|
|
|
|
|
|
sub detachSaveRecord { |
10265
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10266
|
|
|
|
|
|
|
|
10267
|
0
|
0
|
|
|
|
0
|
return if ! $o->{saveRecord}; |
10268
|
0
|
|
|
|
|
0
|
delete $o->{saveRecord}; |
10269
|
0
|
0
|
|
|
|
0
|
$o->{parent}->detachSaveRecord if $o->{parent}; |
10270
|
|
|
|
|
|
|
} |
10271
|
|
|
|
|
|
|
|
10272
|
|
|
|
|
|
|
package CDS::Document::Part; |
10273
|
|
|
|
|
|
|
|
10274
|
|
|
|
|
|
|
sub new { |
10275
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10276
|
|
|
|
|
|
|
|
10277
|
0
|
|
|
|
|
0
|
return bless { |
10278
|
|
|
|
|
|
|
isMerged => 0, |
10279
|
|
|
|
|
|
|
hashAndKey => undef, |
10280
|
|
|
|
|
|
|
size => 0, |
10281
|
|
|
|
|
|
|
count => 0, |
10282
|
|
|
|
|
|
|
selected => 0, |
10283
|
|
|
|
|
|
|
}; |
10284
|
|
|
|
|
|
|
} |
10285
|
|
|
|
|
|
|
|
10286
|
|
|
|
|
|
|
# In this implementation, we only keep track of the number of values of the list, but |
10287
|
|
|
|
|
|
|
# not of the corresponding items. This saves memory (~100 MiB for 1M items), but takes |
10288
|
|
|
|
|
|
|
# more time (0.5 s for 1M items) when saving. Since command line programs usually write |
10289
|
|
|
|
|
|
|
# the document only once, this is acceptable. Reading the tree anyway takes about 10 |
10290
|
|
|
|
|
|
|
# times more time. |
10291
|
|
|
|
|
|
|
|
10292
|
|
|
|
|
|
|
package CDS::ErrorHandlingStore; |
10293
|
|
|
|
|
|
|
|
10294
|
1
|
|
|
1
|
|
4477
|
use parent -norequire, 'CDS::Store'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
10295
|
|
|
|
|
|
|
|
10296
|
|
|
|
|
|
|
sub new { |
10297
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10298
|
0
|
|
|
|
|
0
|
my $store = shift; |
10299
|
0
|
|
|
|
|
0
|
my $url = shift; |
10300
|
0
|
|
|
|
|
0
|
my $errorHandler = shift; |
10301
|
|
|
|
|
|
|
|
10302
|
0
|
|
|
|
|
0
|
return bless { |
10303
|
|
|
|
|
|
|
store => $store, |
10304
|
|
|
|
|
|
|
url => $url, |
10305
|
|
|
|
|
|
|
errorHandler => $errorHandler, |
10306
|
|
|
|
|
|
|
} |
10307
|
|
|
|
|
|
|
} |
10308
|
|
|
|
|
|
|
|
10309
|
0
|
|
|
0
|
|
0
|
sub store { shift->{store} } |
10310
|
0
|
|
|
0
|
|
0
|
sub url { shift->{url} } |
10311
|
0
|
|
|
0
|
|
0
|
sub errorHandler { shift->{errorHandler} } |
10312
|
|
|
|
|
|
|
|
10313
|
|
|
|
|
|
|
sub id { |
10314
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10315
|
0
|
|
|
|
|
0
|
'Error handling'."\n ".$o->{store}->id } |
10316
|
|
|
|
|
|
|
|
10317
|
|
|
|
|
|
|
sub get { |
10318
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10319
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10320
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10321
|
|
|
|
|
|
|
|
10322
|
0
|
0
|
|
|
|
0
|
return undef, 'Store disabled.' if $o->{errorHandler}->hasStoreError($o, 'GET'); |
10323
|
|
|
|
|
|
|
|
10324
|
0
|
|
|
|
|
0
|
my ($object, $error) = $o->{store}->get($hash, $keyPair); |
10325
|
0
|
0
|
|
|
|
0
|
if (defined $error) { |
10326
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreError($o, 'GET', $error); |
10327
|
0
|
|
|
|
|
0
|
return undef, $error; |
10328
|
|
|
|
|
|
|
} |
10329
|
|
|
|
|
|
|
|
10330
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreSuccess($o, 'GET'); |
10331
|
0
|
|
|
|
|
0
|
return $object, $error; |
10332
|
|
|
|
|
|
|
} |
10333
|
|
|
|
|
|
|
|
10334
|
|
|
|
|
|
|
sub book { |
10335
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10336
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10337
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10338
|
|
|
|
|
|
|
|
10339
|
0
|
0
|
|
|
|
0
|
return undef, 'Store disabled.' if $o->{errorHandler}->hasStoreError($o, 'BOOK'); |
10340
|
|
|
|
|
|
|
|
10341
|
0
|
|
|
|
|
0
|
my ($booked, $error) = $o->{store}->book($hash, $keyPair); |
10342
|
0
|
0
|
|
|
|
0
|
if (defined $error) { |
10343
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreError($o, 'BOOK', $error); |
10344
|
0
|
|
|
|
|
0
|
return undef, $error; |
10345
|
|
|
|
|
|
|
} |
10346
|
|
|
|
|
|
|
|
10347
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreSuccess($o, 'BOOK'); |
10348
|
0
|
|
|
|
|
0
|
return $booked; |
10349
|
|
|
|
|
|
|
} |
10350
|
|
|
|
|
|
|
|
10351
|
|
|
|
|
|
|
sub put { |
10352
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10353
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10354
|
0
|
0
|
0
|
|
|
0
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
0
|
|
10355
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10356
|
|
|
|
|
|
|
|
10357
|
0
|
0
|
|
|
|
0
|
return 'Store disabled.' if $o->{errorHandler}->hasStoreError($o, 'PUT'); |
10358
|
|
|
|
|
|
|
|
10359
|
0
|
|
|
|
|
0
|
my $error = $o->{store}->put($hash, $object, $keyPair); |
10360
|
0
|
0
|
|
|
|
0
|
if (defined $error) { |
10361
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreError($o, 'PUT', $error); |
10362
|
0
|
|
|
|
|
0
|
return $error; |
10363
|
|
|
|
|
|
|
} |
10364
|
|
|
|
|
|
|
|
10365
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreSuccess($o, 'PUT'); |
10366
|
0
|
|
|
|
|
0
|
return; |
10367
|
|
|
|
|
|
|
} |
10368
|
|
|
|
|
|
|
|
10369
|
|
|
|
|
|
|
sub list { |
10370
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10371
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10372
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
10373
|
0
|
|
|
|
|
0
|
my $timeout = shift; |
10374
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10375
|
|
|
|
|
|
|
|
10376
|
0
|
0
|
|
|
|
0
|
return undef, 'Store disabled.' if $o->{errorHandler}->hasStoreError($o, 'LIST'); |
10377
|
|
|
|
|
|
|
|
10378
|
0
|
|
|
|
|
0
|
my ($hashes, $error) = $o->{store}->list($accountHash, $boxLabel, $timeout, $keyPair); |
10379
|
0
|
0
|
|
|
|
0
|
if (defined $error) { |
10380
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreError($o, 'LIST', $error); |
10381
|
0
|
|
|
|
|
0
|
return undef, $error; |
10382
|
|
|
|
|
|
|
} |
10383
|
|
|
|
|
|
|
|
10384
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreSuccess($o, 'LIST'); |
10385
|
0
|
|
|
|
|
0
|
return $hashes; |
10386
|
|
|
|
|
|
|
} |
10387
|
|
|
|
|
|
|
|
10388
|
|
|
|
|
|
|
sub add { |
10389
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10390
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10391
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
10392
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10393
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10394
|
|
|
|
|
|
|
|
10395
|
0
|
0
|
|
|
|
0
|
return 'Store disabled.' if $o->{errorHandler}->hasStoreError($o, 'ADD'); |
10396
|
|
|
|
|
|
|
|
10397
|
0
|
|
|
|
|
0
|
my $error = $o->{store}->add($accountHash, $boxLabel, $hash, $keyPair); |
10398
|
0
|
0
|
|
|
|
0
|
if (defined $error) { |
10399
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreError($o, 'ADD', $error); |
10400
|
0
|
|
|
|
|
0
|
return $error; |
10401
|
|
|
|
|
|
|
} |
10402
|
|
|
|
|
|
|
|
10403
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreSuccess($o, 'ADD'); |
10404
|
0
|
|
|
|
|
0
|
return; |
10405
|
|
|
|
|
|
|
} |
10406
|
|
|
|
|
|
|
|
10407
|
|
|
|
|
|
|
sub remove { |
10408
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10409
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10410
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
10411
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10412
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10413
|
|
|
|
|
|
|
|
10414
|
0
|
0
|
|
|
|
0
|
return 'Store disabled.' if $o->{errorHandler}->hasStoreError($o, 'REMOVE'); |
10415
|
|
|
|
|
|
|
|
10416
|
0
|
|
|
|
|
0
|
my $error = $o->{store}->remove($accountHash, $boxLabel, $hash, $keyPair); |
10417
|
0
|
0
|
|
|
|
0
|
if (defined $error) { |
10418
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreError($o, 'REMOVE', $error); |
10419
|
0
|
|
|
|
|
0
|
return $error; |
10420
|
|
|
|
|
|
|
} |
10421
|
|
|
|
|
|
|
|
10422
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreSuccess($o, 'REMOVE'); |
10423
|
0
|
|
|
|
|
0
|
return; |
10424
|
|
|
|
|
|
|
} |
10425
|
|
|
|
|
|
|
|
10426
|
|
|
|
|
|
|
sub modify { |
10427
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10428
|
0
|
|
|
|
|
0
|
my $modifications = shift; |
10429
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10430
|
|
|
|
|
|
|
|
10431
|
0
|
0
|
|
|
|
0
|
return 'Store disabled.' if $o->{errorHandler}->hasStoreError($o, 'MODIFY'); |
10432
|
|
|
|
|
|
|
|
10433
|
0
|
|
|
|
|
0
|
my $error = $o->{store}->modify($modifications, $keyPair); |
10434
|
0
|
0
|
|
|
|
0
|
if (defined $error) { |
10435
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreError($o, 'MODIFY', $error); |
10436
|
0
|
|
|
|
|
0
|
return $error; |
10437
|
|
|
|
|
|
|
} |
10438
|
|
|
|
|
|
|
|
10439
|
0
|
|
|
|
|
0
|
$o->{errorHandler}->onStoreSuccess($o, 'MODIFY'); |
10440
|
0
|
|
|
|
|
0
|
return; |
10441
|
|
|
|
|
|
|
} |
10442
|
|
|
|
|
|
|
|
10443
|
|
|
|
|
|
|
# A Condensation store on a local folder. |
10444
|
|
|
|
|
|
|
package CDS::FolderStore; |
10445
|
|
|
|
|
|
|
|
10446
|
1
|
|
|
1
|
|
1205
|
use parent -norequire, 'CDS::Store'; |
|
1
|
|
|
|
|
36
|
|
|
1
|
|
|
|
|
13
|
|
10447
|
|
|
|
|
|
|
|
10448
|
|
|
|
|
|
|
sub forUrl { |
10449
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10450
|
0
|
|
|
|
|
0
|
my $url = shift; |
10451
|
|
|
|
|
|
|
|
10452
|
0
|
0
|
|
|
|
0
|
return if substr($url, 0, 8) ne 'file:///'; |
10453
|
0
|
|
|
|
|
0
|
return $class->new(substr($url, 7)); |
10454
|
|
|
|
|
|
|
} |
10455
|
|
|
|
|
|
|
|
10456
|
|
|
|
|
|
|
sub new { |
10457
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10458
|
0
|
|
|
|
|
0
|
my $folder = shift; |
10459
|
|
|
|
|
|
|
|
10460
|
0
|
|
|
|
|
0
|
return bless { |
10461
|
|
|
|
|
|
|
folder => $folder, |
10462
|
|
|
|
|
|
|
permissions => CDS::FolderStore::PosixPermissions->forFolder($folder.'/accounts'), |
10463
|
|
|
|
|
|
|
}; |
10464
|
|
|
|
|
|
|
} |
10465
|
|
|
|
|
|
|
|
10466
|
|
|
|
|
|
|
sub id { |
10467
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10468
|
0
|
|
|
|
|
0
|
'file://'.$o->{folder} } |
10469
|
0
|
|
|
0
|
|
0
|
sub folder { shift->{folder} } |
10470
|
|
|
|
|
|
|
|
10471
|
0
|
|
|
0
|
|
0
|
sub permissions { shift->{permissions} } |
10472
|
|
|
|
|
|
|
sub setPermissions { |
10473
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10474
|
0
|
|
|
|
|
0
|
my $permissions = shift; |
10475
|
0
|
|
|
|
|
0
|
$o->{permissions} = $permissions; } |
10476
|
|
|
|
|
|
|
|
10477
|
|
|
|
|
|
|
sub get { |
10478
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10479
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10480
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10481
|
|
|
|
|
|
|
|
10482
|
0
|
|
|
|
|
0
|
my $hashHex = $hash->hex; |
10483
|
0
|
|
|
|
|
0
|
my $file = $o->{folder}.'/objects/'.substr($hashHex, 0, 2).'/'.substr($hashHex, 2); |
10484
|
0
|
|
|
|
|
0
|
return CDS::Object->fromBytes(CDS->readBytesFromFile($file)); |
10485
|
|
|
|
|
|
|
} |
10486
|
|
|
|
|
|
|
|
10487
|
|
|
|
|
|
|
sub book { |
10488
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10489
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10490
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10491
|
|
|
|
|
|
|
|
10492
|
|
|
|
|
|
|
# Book the object if it exists |
10493
|
0
|
|
|
|
|
0
|
my $hashHex = $hash->hex; |
10494
|
0
|
|
|
|
|
0
|
my $folder = $o->{folder}.'/objects/'.substr($hashHex, 0, 2); |
10495
|
0
|
|
|
|
|
0
|
my $file = $folder.'/'.substr($hashHex, 2); |
10496
|
0
|
0
|
0
|
|
|
0
|
return 1 if -e $file && utime(undef, undef, $file); |
10497
|
0
|
|
|
|
|
0
|
return; |
10498
|
|
|
|
|
|
|
} |
10499
|
|
|
|
|
|
|
|
10500
|
|
|
|
|
|
|
sub put { |
10501
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10502
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10503
|
0
|
0
|
0
|
|
|
0
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
0
|
|
10504
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10505
|
|
|
|
|
|
|
|
10506
|
|
|
|
|
|
|
# Book the object if it exists |
10507
|
0
|
|
|
|
|
0
|
my $hashHex = $hash->hex; |
10508
|
0
|
|
|
|
|
0
|
my $folder = $o->{folder}.'/objects/'.substr($hashHex, 0, 2); |
10509
|
0
|
|
|
|
|
0
|
my $file = $folder.'/'.substr($hashHex, 2); |
10510
|
0
|
0
|
0
|
|
|
0
|
return if -e $file && utime(undef, undef, $file); |
10511
|
|
|
|
|
|
|
|
10512
|
|
|
|
|
|
|
# Write the file, set the permissions, and move it to the right place |
10513
|
0
|
|
|
|
|
0
|
my $permissions = $o->{permissions}; |
10514
|
0
|
|
|
|
|
0
|
$permissions->mkdir($folder, $permissions->objectFolderMode); |
10515
|
0
|
|
0
|
|
|
0
|
my $temporaryFile = $permissions->writeTemporaryFile($folder, $permissions->objectFileMode, $object->bytes) // return 'Failed to write object'; |
10516
|
0
|
0
|
|
|
|
0
|
rename($temporaryFile, $file) || return 'Failed to rename object.'; |
10517
|
0
|
|
|
|
|
0
|
return; |
10518
|
|
|
|
|
|
|
} |
10519
|
|
|
|
|
|
|
|
10520
|
|
|
|
|
|
|
sub list { |
10521
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10522
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10523
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
10524
|
0
|
|
|
|
|
0
|
my $timeout = shift; |
10525
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10526
|
|
|
|
|
|
|
|
10527
|
0
|
0
|
|
|
|
0
|
return undef, 'Invalid box label.' if ! CDS->isValidBoxLabel($boxLabel); |
10528
|
|
|
|
|
|
|
|
10529
|
|
|
|
|
|
|
# Prepare |
10530
|
0
|
|
|
|
|
0
|
my $boxFolder = $o->{folder}.'/accounts/'.$accountHash->hex.'/'.$boxLabel; |
10531
|
|
|
|
|
|
|
|
10532
|
|
|
|
|
|
|
# List |
10533
|
0
|
0
|
|
|
|
0
|
return $o->listFolder($boxFolder) if ! $timeout; |
10534
|
|
|
|
|
|
|
|
10535
|
|
|
|
|
|
|
# Watch |
10536
|
0
|
|
|
|
|
0
|
my $hashes; |
10537
|
0
|
|
|
|
|
0
|
my $watcher = CDS::FolderStore::Watcher->new($boxFolder); |
10538
|
0
|
|
|
|
|
0
|
my $watchUntil = CDS->now + $timeout; |
10539
|
0
|
|
|
|
|
0
|
while (1) { |
10540
|
|
|
|
|
|
|
# List |
10541
|
0
|
|
|
|
|
0
|
$hashes = $o->listFolder($boxFolder); |
10542
|
0
|
0
|
|
|
|
0
|
last if scalar @$hashes; |
10543
|
|
|
|
|
|
|
|
10544
|
|
|
|
|
|
|
# Wait |
10545
|
0
|
|
0
|
|
|
0
|
$watcher->wait($watchUntil - CDS->now, $watchUntil) // last; |
10546
|
|
|
|
|
|
|
} |
10547
|
|
|
|
|
|
|
|
10548
|
0
|
|
|
|
|
0
|
$watcher->done; |
10549
|
0
|
|
|
|
|
0
|
return $hashes; |
10550
|
|
|
|
|
|
|
} |
10551
|
|
|
|
|
|
|
|
10552
|
|
|
|
|
|
|
sub listFolder { |
10553
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10554
|
0
|
|
|
|
|
0
|
my $boxFolder = shift; |
10555
|
|
|
|
|
|
|
# private |
10556
|
0
|
|
|
|
|
0
|
my $hashes = []; |
10557
|
0
|
|
|
|
|
0
|
for my $file (CDS->listFolder($boxFolder)) { |
10558
|
0
|
|
0
|
|
|
0
|
push @$hashes, CDS::Hash->fromHex($file) // next; |
10559
|
|
|
|
|
|
|
} |
10560
|
|
|
|
|
|
|
|
10561
|
0
|
|
|
|
|
0
|
return $hashes; |
10562
|
|
|
|
|
|
|
} |
10563
|
|
|
|
|
|
|
|
10564
|
|
|
|
|
|
|
sub add { |
10565
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10566
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10567
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
10568
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10569
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10570
|
|
|
|
|
|
|
|
10571
|
0
|
|
|
|
|
0
|
my $permissions = $o->{permissions}; |
10572
|
|
|
|
|
|
|
|
10573
|
0
|
0
|
|
|
|
0
|
next if ! CDS->isValidBoxLabel($boxLabel); |
10574
|
0
|
|
|
|
|
0
|
my $accountFolder = $o->{folder}.'/accounts/'.$accountHash->hex; |
10575
|
0
|
|
|
|
|
0
|
$permissions->mkdir($accountFolder, $permissions->accountFolderMode); |
10576
|
0
|
|
|
|
|
0
|
my $boxFolder = $accountFolder.'/'.$boxLabel; |
10577
|
0
|
|
|
|
|
0
|
$permissions->mkdir($boxFolder, $permissions->boxFolderMode($boxLabel)); |
10578
|
0
|
|
|
|
|
0
|
my $boxFileMode = $permissions->boxFileMode($boxLabel); |
10579
|
|
|
|
|
|
|
|
10580
|
0
|
|
0
|
|
|
0
|
my $temporaryFile = $permissions->writeTemporaryFile($boxFolder, $boxFileMode, '') // return 'Failed to write file.'; |
10581
|
0
|
0
|
|
|
|
0
|
rename($temporaryFile, $boxFolder.'/'.$hash->hex) || return 'Failed to rename file.'; |
10582
|
0
|
|
|
|
|
0
|
return; |
10583
|
|
|
|
|
|
|
} |
10584
|
|
|
|
|
|
|
|
10585
|
|
|
|
|
|
|
sub remove { |
10586
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10587
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10588
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
10589
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10590
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10591
|
|
|
|
|
|
|
|
10592
|
0
|
0
|
|
|
|
0
|
next if ! CDS->isValidBoxLabel($boxLabel); |
10593
|
0
|
|
|
|
|
0
|
my $accountFolder = $o->{folder}.'/accounts/'.$accountHash->hex; |
10594
|
0
|
|
|
|
|
0
|
my $boxFolder = $accountFolder.'/'.$boxLabel; |
10595
|
0
|
0
|
|
|
|
0
|
next if ! -d $boxFolder; |
10596
|
0
|
|
|
|
|
0
|
unlink $boxFolder.'/'.$hash->hex; |
10597
|
0
|
|
|
|
|
0
|
return; |
10598
|
|
|
|
|
|
|
} |
10599
|
|
|
|
|
|
|
|
10600
|
|
|
|
|
|
|
sub modify { |
10601
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10602
|
0
|
|
|
|
|
0
|
my $modifications = shift; |
10603
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
10604
|
|
|
|
|
|
|
|
10605
|
0
|
|
|
|
|
0
|
return $modifications->executeIndividually($o, $keyPair); |
10606
|
|
|
|
|
|
|
} |
10607
|
|
|
|
|
|
|
|
10608
|
|
|
|
|
|
|
# Store administration functions |
10609
|
|
|
|
|
|
|
|
10610
|
|
|
|
|
|
|
sub exists { |
10611
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10612
|
|
|
|
|
|
|
|
10613
|
0
|
|
0
|
|
|
0
|
return -d $o->{folder}.'/accounts' && -d $o->{folder}.'/objects'; |
10614
|
|
|
|
|
|
|
} |
10615
|
|
|
|
|
|
|
|
10616
|
|
|
|
|
|
|
# Creates the store if it does not exist. The store folder itself must exist. |
10617
|
|
|
|
|
|
|
sub createIfNecessary { |
10618
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10619
|
|
|
|
|
|
|
|
10620
|
0
|
|
|
|
|
0
|
my $accountsFolder = $o->{folder}.'/accounts'; |
10621
|
0
|
|
|
|
|
0
|
my $objectsFolder = $o->{folder}.'/objects'; |
10622
|
0
|
|
|
|
|
0
|
$o->{permissions}->mkdir($accountsFolder, $o->{permissions}->baseFolderMode); |
10623
|
0
|
|
|
|
|
0
|
$o->{permissions}->mkdir($objectsFolder, $o->{permissions}->baseFolderMode); |
10624
|
0
|
|
0
|
|
|
0
|
return -d $accountsFolder && -d $objectsFolder; |
10625
|
|
|
|
|
|
|
} |
10626
|
|
|
|
|
|
|
|
10627
|
|
|
|
|
|
|
# Lists accounts. This is a non-standard extension. |
10628
|
|
|
|
|
|
|
sub accounts { |
10629
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10630
|
|
|
|
|
|
|
|
10631
|
0
|
|
|
|
|
0
|
return grep { defined $_ } |
10632
|
0
|
|
|
|
|
0
|
map { CDS::Hash->fromHex($_) } |
10633
|
0
|
|
|
|
|
0
|
CDS->listFolder($o->{folder}.'/accounts'); |
10634
|
|
|
|
|
|
|
} |
10635
|
|
|
|
|
|
|
|
10636
|
|
|
|
|
|
|
# Adds an account. This is a non-standard extension. |
10637
|
|
|
|
|
|
|
sub addAccount { |
10638
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10639
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10640
|
|
|
|
|
|
|
|
10641
|
0
|
|
|
|
|
0
|
my $accountFolder = $o->{folder}.'/accounts/'.$accountHash->hex; |
10642
|
0
|
|
|
|
|
0
|
$o->{permissions}->mkdir($accountFolder, $o->{permissions}->accountFolderMode); |
10643
|
0
|
|
|
|
|
0
|
return -d $accountFolder; |
10644
|
|
|
|
|
|
|
} |
10645
|
|
|
|
|
|
|
|
10646
|
|
|
|
|
|
|
# Removes an account. This is a non-standard extension. |
10647
|
|
|
|
|
|
|
sub removeAccount { |
10648
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10649
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
10650
|
|
|
|
|
|
|
|
10651
|
0
|
|
|
|
|
0
|
my $accountFolder = $o->{folder}.'/accounts/'.$accountHash->hex; |
10652
|
0
|
|
|
|
|
0
|
my $trashFolder = $o->{folder}.'/accounts/.deleted-'.CDS->randomHex(16); |
10653
|
0
|
|
|
|
|
0
|
rename $accountFolder, $trashFolder; |
10654
|
0
|
|
|
|
|
0
|
system('rm', '-rf', $trashFolder); |
10655
|
0
|
|
|
|
|
0
|
return ! -d $accountFolder; |
10656
|
|
|
|
|
|
|
} |
10657
|
|
|
|
|
|
|
|
10658
|
|
|
|
|
|
|
# Checks (and optionally fixes) the POSIX permissions of all files and folders. This is a non-standard extension. |
10659
|
|
|
|
|
|
|
sub checkPermissions { |
10660
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10661
|
0
|
|
|
|
|
0
|
my $logger = shift; |
10662
|
|
|
|
|
|
|
|
10663
|
0
|
|
|
|
|
0
|
my $permissions = $o->{permissions}; |
10664
|
|
|
|
|
|
|
|
10665
|
|
|
|
|
|
|
# Check the accounts folder |
10666
|
0
|
|
|
|
|
0
|
my $accountsFolder = $o->{folder}.'/accounts'; |
10667
|
0
|
0
|
|
|
|
0
|
$permissions->checkPermissions($accountsFolder, $permissions->baseFolderMode, $logger) || return; |
10668
|
|
|
|
|
|
|
|
10669
|
|
|
|
|
|
|
# Check the account folders |
10670
|
0
|
|
|
|
|
0
|
for my $account (sort { $a cmp $b } CDS->listFolder($accountsFolder)) { |
|
0
|
|
|
|
|
0
|
|
10671
|
0
|
0
|
|
|
|
0
|
next if $account !~ /^[0-9a-f]{64}$/; |
10672
|
0
|
|
|
|
|
0
|
my $accountFolder = $accountsFolder.'/'.$account; |
10673
|
0
|
0
|
|
|
|
0
|
$permissions->checkPermissions($accountFolder, $permissions->accountFolderMode, $logger) || return; |
10674
|
|
|
|
|
|
|
|
10675
|
|
|
|
|
|
|
# Check the box folders |
10676
|
0
|
|
|
|
|
0
|
for my $boxLabel (sort { $a cmp $b } CDS->listFolder($accountFolder)) { |
|
0
|
|
|
|
|
0
|
|
10677
|
0
|
0
|
|
|
|
0
|
next if $boxLabel =~ /^\./; |
10678
|
0
|
|
|
|
|
0
|
my $boxFolder = $accountFolder.'/'.$boxLabel; |
10679
|
0
|
0
|
|
|
|
0
|
$permissions->checkPermissions($boxFolder, $permissions->boxFolderMode($boxLabel), $logger) || return; |
10680
|
|
|
|
|
|
|
|
10681
|
|
|
|
|
|
|
# Check each file |
10682
|
0
|
|
|
|
|
0
|
my $filePermissions = $permissions->boxFileMode($boxLabel); |
10683
|
0
|
|
|
|
|
0
|
for my $file (sort { $a cmp $b } CDS->listFolder($boxFolder)) { |
|
0
|
|
|
|
|
0
|
|
10684
|
0
|
0
|
|
|
|
0
|
next if $file !~ /^[0-9a-f]{64}/; |
10685
|
0
|
0
|
|
|
|
0
|
$permissions->checkPermissions($boxFolder.'/'.$file, $filePermissions, $logger) || return; |
10686
|
|
|
|
|
|
|
} |
10687
|
|
|
|
|
|
|
} |
10688
|
|
|
|
|
|
|
} |
10689
|
|
|
|
|
|
|
|
10690
|
|
|
|
|
|
|
# Check the objects folder |
10691
|
0
|
|
|
|
|
0
|
my $objectsFolder = $o->{folder}.'/objects'; |
10692
|
0
|
|
|
|
|
0
|
my $fileMode = $permissions->objectFileMode; |
10693
|
0
|
|
|
|
|
0
|
my $folderMode = $permissions->objectFolderMode; |
10694
|
0
|
0
|
|
|
|
0
|
$permissions->checkPermissions($objectsFolder, $folderMode, $logger) || return; |
10695
|
|
|
|
|
|
|
|
10696
|
|
|
|
|
|
|
# Check the 256 sub folders |
10697
|
0
|
|
|
|
|
0
|
for my $sub (sort { $a cmp $b } CDS->listFolder($objectsFolder)) { |
|
0
|
|
|
|
|
0
|
|
10698
|
0
|
0
|
|
|
|
0
|
next if $sub !~ /^[0-9a-f][0-9a-f]$/; |
10699
|
0
|
|
|
|
|
0
|
my $subFolder = $objectsFolder.'/'.$sub; |
10700
|
0
|
0
|
|
|
|
0
|
$permissions->checkPermissions($subFolder, $folderMode, $logger) || return; |
10701
|
|
|
|
|
|
|
|
10702
|
0
|
|
|
|
|
0
|
for my $file (sort { $a cmp $b } CDS->listFolder($subFolder)) { |
|
0
|
|
|
|
|
0
|
|
10703
|
0
|
0
|
|
|
|
0
|
next if $file !~ /^[0-9a-f]{62}/; |
10704
|
0
|
0
|
|
|
|
0
|
$permissions->checkPermissions($subFolder.'/'.$file, $fileMode, $logger) || return; |
10705
|
|
|
|
|
|
|
} |
10706
|
|
|
|
|
|
|
} |
10707
|
|
|
|
|
|
|
|
10708
|
0
|
|
|
|
|
0
|
return 1; |
10709
|
|
|
|
|
|
|
} |
10710
|
|
|
|
|
|
|
|
10711
|
|
|
|
|
|
|
# Handles POSIX permissions (user, group, and mode). |
10712
|
|
|
|
|
|
|
package CDS::FolderStore::PosixPermissions; |
10713
|
|
|
|
|
|
|
|
10714
|
|
|
|
|
|
|
# Returns the permissions set corresponding to the mode, uid, and gid of the base folder. |
10715
|
|
|
|
|
|
|
# If the permissions are ambiguous, the more restrictive set is chosen. |
10716
|
|
|
|
|
|
|
sub forFolder { |
10717
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10718
|
0
|
|
|
|
|
0
|
my $folder = shift; |
10719
|
|
|
|
|
|
|
|
10720
|
0
|
|
|
|
|
0
|
my @s = stat $folder; |
10721
|
0
|
|
0
|
|
|
0
|
my $mode = $s[2] // 0; |
10722
|
|
|
|
|
|
|
|
10723
|
|
|
|
|
|
|
return |
10724
|
0
|
0
|
|
|
|
0
|
($mode & 077) == 077 ? CDS::FolderStore::PosixPermissions::World->new : |
|
|
0
|
|
|
|
|
|
10725
|
|
|
|
|
|
|
($mode & 070) == 070 ? CDS::FolderStore::PosixPermissions::Group->new($s[5]) : |
10726
|
|
|
|
|
|
|
CDS::FolderStore::PosixPermissions::User->new($s[4]); |
10727
|
|
|
|
|
|
|
} |
10728
|
|
|
|
|
|
|
|
10729
|
0
|
|
|
0
|
|
0
|
sub uid { shift->{uid} } |
10730
|
0
|
|
|
0
|
|
0
|
sub gid { shift->{gid} } |
10731
|
|
|
|
|
|
|
|
10732
|
|
|
|
|
|
|
sub user { |
10733
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10734
|
|
|
|
|
|
|
|
10735
|
0
|
|
0
|
|
|
0
|
my $uid = $o->{uid} // return; |
10736
|
0
|
|
0
|
|
|
0
|
return getpwuid($uid) // $uid; |
10737
|
|
|
|
|
|
|
} |
10738
|
|
|
|
|
|
|
|
10739
|
|
|
|
|
|
|
sub group { |
10740
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10741
|
|
|
|
|
|
|
|
10742
|
0
|
|
0
|
|
|
0
|
my $gid = $o->{gid} // return; |
10743
|
0
|
|
0
|
|
|
0
|
return getgrgid($gid) // $gid; |
10744
|
|
|
|
|
|
|
} |
10745
|
|
|
|
|
|
|
|
10746
|
|
|
|
|
|
|
sub writeTemporaryFile { |
10747
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10748
|
0
|
|
|
|
|
0
|
my $folder = shift; |
10749
|
0
|
|
|
|
|
0
|
my $mode = shift; |
10750
|
|
|
|
|
|
|
|
10751
|
|
|
|
|
|
|
# Write the file |
10752
|
0
|
|
|
|
|
0
|
my $temporaryFile = $folder.'/.'.CDS->randomHex(16); |
10753
|
0
|
0
|
|
|
|
0
|
open(my $fh, '>:bytes', $temporaryFile) || return; |
10754
|
0
|
|
|
|
|
0
|
print $fh @_; |
10755
|
0
|
|
|
|
|
0
|
close $fh; |
10756
|
|
|
|
|
|
|
|
10757
|
|
|
|
|
|
|
# Set the permissions |
10758
|
0
|
|
|
|
|
0
|
chmod $mode, $temporaryFile; |
10759
|
0
|
|
|
|
|
0
|
my $uid = $o->uid; |
10760
|
0
|
|
|
|
|
0
|
my $gid = $o->gid; |
10761
|
0
|
0
|
0
|
|
|
0
|
chown $uid // -1, $gid // -1, $temporaryFile if defined $uid && $uid != $< || defined $gid && $gid != $(; |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
10762
|
0
|
|
|
|
|
0
|
return $temporaryFile; |
10763
|
|
|
|
|
|
|
} |
10764
|
|
|
|
|
|
|
|
10765
|
|
|
|
|
|
|
sub mkdir { |
10766
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10767
|
0
|
|
|
|
|
0
|
my $folder = shift; |
10768
|
0
|
|
|
|
|
0
|
my $mode = shift; |
10769
|
|
|
|
|
|
|
|
10770
|
0
|
0
|
|
|
|
0
|
return if -d $folder; |
10771
|
|
|
|
|
|
|
|
10772
|
|
|
|
|
|
|
# Create the folder (note: mode is altered by umask) |
10773
|
0
|
|
|
|
|
0
|
my $success = mkdir $folder, $mode; |
10774
|
|
|
|
|
|
|
|
10775
|
|
|
|
|
|
|
# Set the permissions |
10776
|
0
|
|
|
|
|
0
|
chmod $mode, $folder; |
10777
|
0
|
|
|
|
|
0
|
my $uid = $o->uid; |
10778
|
0
|
|
|
|
|
0
|
my $gid = $o->gid; |
10779
|
0
|
0
|
0
|
|
|
0
|
chown $uid // -1, $gid // -1, $folder if defined $uid && $uid != $< || defined $gid && $gid != $(; |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
10780
|
0
|
|
|
|
|
0
|
return $success; |
10781
|
|
|
|
|
|
|
} |
10782
|
|
|
|
|
|
|
|
10783
|
|
|
|
|
|
|
# Check the permissions of a file or folder, and fix them if desired. |
10784
|
|
|
|
|
|
|
# A logger object is called for the different cases (access error, correct permissions, wrong permissions, error fixing permissions). |
10785
|
|
|
|
|
|
|
sub checkPermissions { |
10786
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10787
|
0
|
|
|
|
|
0
|
my $item = shift; |
10788
|
0
|
|
|
|
|
0
|
my $expectedMode = shift; |
10789
|
0
|
|
|
|
|
0
|
my $logger = shift; |
10790
|
|
|
|
|
|
|
|
10791
|
0
|
|
|
|
|
0
|
my $expectedUid = $o->uid; |
10792
|
0
|
|
|
|
|
0
|
my $expectedGid = $o->gid; |
10793
|
|
|
|
|
|
|
|
10794
|
|
|
|
|
|
|
# Stat the item |
10795
|
0
|
|
|
|
|
0
|
my @s = stat $item; |
10796
|
0
|
0
|
|
|
|
0
|
return $logger->accessError($item) if ! scalar @s; |
10797
|
0
|
|
|
|
|
0
|
my $mode = $s[2] & 07777; |
10798
|
0
|
|
|
|
|
0
|
my $uid = $s[4]; |
10799
|
0
|
|
|
|
|
0
|
my $gid = $s[5]; |
10800
|
|
|
|
|
|
|
|
10801
|
|
|
|
|
|
|
# Check |
10802
|
0
|
|
0
|
|
|
0
|
my $wrongUid = defined $expectedUid && $uid != $expectedUid; |
10803
|
0
|
|
0
|
|
|
0
|
my $wrongGid = defined $expectedGid && $gid != $expectedGid; |
10804
|
0
|
|
|
|
|
0
|
my $wrongMode = $mode != $expectedMode; |
10805
|
0
|
0
|
0
|
|
|
0
|
if ($wrongUid || $wrongGid || $wrongMode) { |
|
|
|
0
|
|
|
|
|
10806
|
|
|
|
|
|
|
# Something is wrong |
10807
|
0
|
0
|
|
|
|
0
|
$logger->wrong($item, $uid, $gid, $mode, $expectedUid, $expectedGid, $expectedMode) || return 1; |
10808
|
|
|
|
|
|
|
|
10809
|
|
|
|
|
|
|
# Fix uid and gid |
10810
|
0
|
0
|
0
|
|
|
0
|
if ($wrongUid || $wrongGid) { |
10811
|
0
|
|
0
|
|
|
0
|
my $count = chown $expectedUid // -1, $expectedGid // -1, $item; |
|
|
|
0
|
|
|
|
|
10812
|
0
|
0
|
|
|
|
0
|
return $logger->setError($item) if $count < 1; |
10813
|
|
|
|
|
|
|
} |
10814
|
|
|
|
|
|
|
|
10815
|
|
|
|
|
|
|
# Fix mode |
10816
|
0
|
0
|
|
|
|
0
|
if ($wrongMode) { |
10817
|
0
|
|
|
|
|
0
|
my $count = chmod $expectedMode, $item; |
10818
|
0
|
0
|
|
|
|
0
|
return $logger->setError($item) if $count < 1; |
10819
|
|
|
|
|
|
|
} |
10820
|
|
|
|
|
|
|
} else { |
10821
|
|
|
|
|
|
|
# Everything is OK |
10822
|
0
|
|
|
|
|
0
|
$logger->correct($item, $mode, $uid, $gid); |
10823
|
|
|
|
|
|
|
} |
10824
|
|
|
|
|
|
|
|
10825
|
0
|
|
|
|
|
0
|
return 1; |
10826
|
|
|
|
|
|
|
} |
10827
|
|
|
|
|
|
|
|
10828
|
|
|
|
|
|
|
# The store belongs to a group. Every user belonging to the group is treated equivalent, and users are supposed to trust each other to some extent. |
10829
|
|
|
|
|
|
|
# The resulting store will have files belonging to multiple users, but the same group. |
10830
|
|
|
|
|
|
|
package CDS::FolderStore::PosixPermissions::Group; |
10831
|
|
|
|
|
|
|
|
10832
|
1
|
|
|
1
|
|
2684
|
use parent -norequire, 'CDS::FolderStore::PosixPermissions'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
10833
|
|
|
|
|
|
|
|
10834
|
|
|
|
|
|
|
sub new { |
10835
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10836
|
0
|
|
|
|
|
0
|
my $gid = shift; |
10837
|
|
|
|
|
|
|
|
10838
|
0
|
|
0
|
|
|
0
|
return bless {gid => $gid // $(}; |
10839
|
|
|
|
|
|
|
} |
10840
|
|
|
|
|
|
|
|
10841
|
|
|
|
|
|
|
sub target { |
10842
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10843
|
0
|
|
|
|
|
0
|
'members of the group '.$o->group } |
10844
|
0
|
|
|
0
|
|
0
|
sub baseFolderMode { 0771 } |
10845
|
0
|
|
|
0
|
|
0
|
sub objectFolderMode { 0771 } |
10846
|
0
|
|
|
0
|
|
0
|
sub objectFileMode { 0664 } |
10847
|
0
|
|
|
0
|
|
0
|
sub accountFolderMode { 0771 } |
10848
|
|
|
|
|
|
|
sub boxFolderMode { |
10849
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10850
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
10851
|
0
|
0
|
|
|
|
0
|
$boxLabel eq 'public' ? 0775 : 0770 } |
10852
|
|
|
|
|
|
|
sub boxFileMode { |
10853
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10854
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
10855
|
0
|
0
|
|
|
|
0
|
$boxLabel eq 'public' ? 0664 : 0660 } |
10856
|
|
|
|
|
|
|
|
10857
|
|
|
|
|
|
|
# The store belongs to a single user. Other users shall only be able to read objects and the public box, and post to the message box. |
10858
|
|
|
|
|
|
|
package CDS::FolderStore::PosixPermissions::User; |
10859
|
|
|
|
|
|
|
|
10860
|
1
|
|
|
1
|
|
249
|
use parent -norequire, 'CDS::FolderStore::PosixPermissions'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
5
|
|
10861
|
|
|
|
|
|
|
|
10862
|
|
|
|
|
|
|
sub new { |
10863
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10864
|
0
|
|
|
|
|
0
|
my $uid = shift; |
10865
|
|
|
|
|
|
|
|
10866
|
0
|
|
0
|
|
|
0
|
return bless {uid => $uid // $<}; |
10867
|
|
|
|
|
|
|
} |
10868
|
|
|
|
|
|
|
|
10869
|
|
|
|
|
|
|
sub target { |
10870
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10871
|
0
|
|
|
|
|
0
|
'user '.$o->user } |
10872
|
0
|
|
|
0
|
|
0
|
sub baseFolderMode { 0711 } |
10873
|
0
|
|
|
0
|
|
0
|
sub objectFolderMode { 0711 } |
10874
|
0
|
|
|
0
|
|
0
|
sub objectFileMode { 0644 } |
10875
|
0
|
|
|
0
|
|
0
|
sub accountFolderMode { 0711 } |
10876
|
|
|
|
|
|
|
sub boxFolderMode { |
10877
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10878
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
10879
|
0
|
0
|
|
|
|
0
|
$boxLabel eq 'public' ? 0755 : 0700 } |
10880
|
|
|
|
|
|
|
sub boxFileMode { |
10881
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10882
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
10883
|
0
|
0
|
|
|
|
0
|
$boxLabel eq 'public' ? 0644 : 0600 } |
10884
|
|
|
|
|
|
|
|
10885
|
|
|
|
|
|
|
# The store is open to everybody. This does not usually make sense, but is offered here for completeness. |
10886
|
|
|
|
|
|
|
# This is the simplest permission scheme. |
10887
|
|
|
|
|
|
|
package CDS::FolderStore::PosixPermissions::World; |
10888
|
|
|
|
|
|
|
|
10889
|
1
|
|
|
1
|
|
232
|
use parent -norequire, 'CDS::FolderStore::PosixPermissions'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
10890
|
|
|
|
|
|
|
|
10891
|
|
|
|
|
|
|
sub new { |
10892
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10893
|
|
|
|
|
|
|
|
10894
|
0
|
|
|
|
|
0
|
return bless {}; |
10895
|
|
|
|
|
|
|
} |
10896
|
|
|
|
|
|
|
|
10897
|
0
|
|
|
0
|
|
0
|
sub target { 'everybody' } |
10898
|
0
|
|
|
0
|
|
0
|
sub baseFolderMode { 0777 } |
10899
|
0
|
|
|
0
|
|
0
|
sub objectFolderMode { 0777 } |
10900
|
0
|
|
|
0
|
|
0
|
sub objectFileMode { 0666 } |
10901
|
0
|
|
|
0
|
|
0
|
sub accountFolderMode { 0777 } |
10902
|
0
|
|
|
0
|
|
0
|
sub boxFolderMode { 0777 } |
10903
|
0
|
|
|
0
|
|
0
|
sub boxFileMode { 0666 } |
10904
|
|
|
|
|
|
|
|
10905
|
|
|
|
|
|
|
package CDS::FolderStore::Watcher; |
10906
|
|
|
|
|
|
|
|
10907
|
|
|
|
|
|
|
sub new { |
10908
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10909
|
0
|
|
|
|
|
0
|
my $folder = shift; |
10910
|
|
|
|
|
|
|
|
10911
|
0
|
|
|
|
|
0
|
return bless {folder => $folder}; |
10912
|
|
|
|
|
|
|
} |
10913
|
|
|
|
|
|
|
|
10914
|
|
|
|
|
|
|
sub wait { |
10915
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10916
|
0
|
|
|
|
|
0
|
my $remaining = shift; |
10917
|
0
|
|
|
|
|
0
|
my $until = shift; |
10918
|
|
|
|
|
|
|
|
10919
|
0
|
0
|
|
|
|
0
|
return if $remaining <= 0; |
10920
|
0
|
|
|
|
|
0
|
sleep 1; |
10921
|
0
|
|
|
|
|
0
|
return 1; |
10922
|
|
|
|
|
|
|
} |
10923
|
|
|
|
|
|
|
|
10924
|
|
|
|
|
|
|
sub done { |
10925
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10926
|
|
|
|
|
|
|
} |
10927
|
|
|
|
|
|
|
|
10928
|
|
|
|
|
|
|
package CDS::GroupDataSharer; |
10929
|
|
|
|
|
|
|
|
10930
|
|
|
|
|
|
|
sub new { |
10931
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
10932
|
0
|
|
|
|
|
0
|
my $actor = shift; |
10933
|
|
|
|
|
|
|
|
10934
|
0
|
|
|
|
|
0
|
my $o = bless { |
10935
|
|
|
|
|
|
|
actor => $actor, |
10936
|
|
|
|
|
|
|
label => 'shared group data', |
10937
|
|
|
|
|
|
|
dataHandlers => {}, |
10938
|
|
|
|
|
|
|
messageChannel => CDS::MessageChannel->new($actor, 'group data', CDS->MONTH), |
10939
|
|
|
|
|
|
|
revision => 0, |
10940
|
|
|
|
|
|
|
version => '', |
10941
|
|
|
|
|
|
|
}, $class; |
10942
|
|
|
|
|
|
|
|
10943
|
0
|
|
|
|
|
0
|
$actor->storagePrivateRoot->addDataHandler($o->{label}, $o); |
10944
|
0
|
|
|
|
|
0
|
return $o; |
10945
|
|
|
|
|
|
|
} |
10946
|
|
|
|
|
|
|
|
10947
|
|
|
|
|
|
|
### Group data handlers |
10948
|
|
|
|
|
|
|
|
10949
|
|
|
|
|
|
|
sub addDataHandler { |
10950
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10951
|
0
|
|
|
|
|
0
|
my $label = shift; |
10952
|
0
|
|
|
|
|
0
|
my $dataHandler = shift; |
10953
|
|
|
|
|
|
|
|
10954
|
0
|
|
|
|
|
0
|
$o->{dataHandlers}->{$label} = $dataHandler; |
10955
|
|
|
|
|
|
|
} |
10956
|
|
|
|
|
|
|
|
10957
|
|
|
|
|
|
|
sub removeDataHandler { |
10958
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10959
|
0
|
|
|
|
|
0
|
my $label = shift; |
10960
|
0
|
|
|
|
|
0
|
my $dataHandler = shift; |
10961
|
|
|
|
|
|
|
|
10962
|
0
|
|
|
|
|
0
|
my $registered = $o->{dataHandlers}->{$label}; |
10963
|
0
|
0
|
|
|
|
0
|
return if $registered != $dataHandler; |
10964
|
0
|
|
|
|
|
0
|
delete $o->{dataHandlers}->{$label}; |
10965
|
|
|
|
|
|
|
} |
10966
|
|
|
|
|
|
|
|
10967
|
|
|
|
|
|
|
### MergeableData interface |
10968
|
|
|
|
|
|
|
|
10969
|
|
|
|
|
|
|
sub addDataTo { |
10970
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10971
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
10972
|
|
|
|
|
|
|
|
10973
|
0
|
0
|
|
|
|
0
|
return if ! $o->{revision}; |
10974
|
0
|
|
|
|
|
0
|
$record->addInteger($o->{revision})->add($o->{version}); |
10975
|
|
|
|
|
|
|
} |
10976
|
|
|
|
|
|
|
|
10977
|
|
|
|
|
|
|
sub mergeData { |
10978
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10979
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
10980
|
|
|
|
|
|
|
|
10981
|
0
|
|
|
|
|
0
|
for my $child ($record->children) { |
10982
|
0
|
|
|
|
|
0
|
my $revision = $child->asInteger; |
10983
|
0
|
0
|
|
|
|
0
|
next if $revision <= $o->{revision}; |
10984
|
|
|
|
|
|
|
|
10985
|
0
|
|
|
|
|
0
|
$o->{revision} = $revision; |
10986
|
0
|
|
|
|
|
0
|
$o->{version} = $child->bytesValue; |
10987
|
|
|
|
|
|
|
} |
10988
|
|
|
|
|
|
|
} |
10989
|
|
|
|
|
|
|
|
10990
|
|
|
|
|
|
|
sub mergeExternalData { |
10991
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
10992
|
0
|
|
|
|
|
0
|
my $store = shift; |
10993
|
0
|
0
|
0
|
|
|
0
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
0
|
|
10994
|
0
|
0
|
0
|
|
|
0
|
my $source = shift; die 'wrong type '.ref($source).' for $source' if defined $source && ref $source ne 'CDS::Source'; |
|
0
|
|
|
|
|
0
|
|
10995
|
|
|
|
|
|
|
|
10996
|
0
|
|
|
|
|
0
|
$o->mergeData($record); |
10997
|
0
|
0
|
|
|
|
0
|
return if ! $source; |
10998
|
0
|
|
|
|
|
0
|
$source->keep; |
10999
|
0
|
|
|
|
|
0
|
$o->{actor}->storagePrivateRoot->unsaved->state->addMergedSource($source); |
11000
|
|
|
|
|
|
|
} |
11001
|
|
|
|
|
|
|
|
11002
|
|
|
|
|
|
|
### Sending messages |
11003
|
|
|
|
|
|
|
|
11004
|
|
|
|
|
|
|
sub createMessage { |
11005
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11006
|
|
|
|
|
|
|
|
11007
|
0
|
|
|
|
|
0
|
my $message = CDS::Record->new; |
11008
|
0
|
|
|
|
|
0
|
my $data = $message->add('group data'); |
11009
|
0
|
|
|
|
|
0
|
for my $label (keys %{$o->{dataHandlers}}) { |
|
0
|
|
|
|
|
0
|
|
11010
|
0
|
|
|
|
|
0
|
my $dataHandler = $o->{dataHandlers}->{$label}; |
11011
|
0
|
|
|
|
|
0
|
$dataHandler->addDataTo($data->add($label)); |
11012
|
|
|
|
|
|
|
} |
11013
|
0
|
|
|
|
|
0
|
return $message; |
11014
|
|
|
|
|
|
|
} |
11015
|
|
|
|
|
|
|
|
11016
|
|
|
|
|
|
|
sub share { |
11017
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11018
|
|
|
|
|
|
|
|
11019
|
|
|
|
|
|
|
# Get the group data members |
11020
|
0
|
|
0
|
|
|
0
|
my $members = $o->{actor}->getGroupDataMembers // return; |
11021
|
0
|
0
|
|
|
|
0
|
return 1 if ! scalar @$members; |
11022
|
|
|
|
|
|
|
|
11023
|
|
|
|
|
|
|
# Create the group data message, and check if it changed |
11024
|
0
|
|
|
|
|
0
|
my $message = $o->createMessage; |
11025
|
0
|
|
|
|
|
0
|
my $versionHash = $message->toObject->calculateHash; |
11026
|
0
|
0
|
|
|
|
0
|
return if $versionHash->bytes eq $o->{version}; |
11027
|
|
|
|
|
|
|
|
11028
|
0
|
|
|
|
|
0
|
$o->{revision} = CDS->now; |
11029
|
0
|
|
|
|
|
0
|
$o->{version} = $versionHash->bytes; |
11030
|
0
|
|
|
|
|
0
|
$o->{actor}->storagePrivateRoot->dataChanged; |
11031
|
|
|
|
|
|
|
|
11032
|
|
|
|
|
|
|
# Procure the sent list |
11033
|
0
|
|
0
|
|
|
0
|
$o->{actor}->procureSentList // return; |
11034
|
|
|
|
|
|
|
|
11035
|
|
|
|
|
|
|
# Get the entrusted keys |
11036
|
0
|
|
0
|
|
|
0
|
my $entrustedKeys = $o->{actor}->getEntrustedKeys // return; |
11037
|
|
|
|
|
|
|
|
11038
|
|
|
|
|
|
|
# Transfer the data |
11039
|
0
|
|
|
|
|
0
|
$o->{messageChannel}->addTransfer([$message->dependentHashes], $o->{actor}->storagePrivateRoot->unsaved, 'group data message'); |
11040
|
|
|
|
|
|
|
|
11041
|
|
|
|
|
|
|
# Send the message |
11042
|
0
|
|
|
|
|
0
|
$o->{messageChannel}->setRecipients($members, $entrustedKeys); |
11043
|
0
|
|
|
|
|
0
|
my ($submission, $missingObject) = $o->{messageChannel}->submit($message, $o); |
11044
|
0
|
0
|
|
|
|
0
|
$o->{actor}->onMissingObject($missingObject) if $missingObject; |
11045
|
0
|
0
|
|
|
|
0
|
return if ! $submission; |
11046
|
0
|
|
|
|
|
0
|
return 1; |
11047
|
|
|
|
|
|
|
} |
11048
|
|
|
|
|
|
|
|
11049
|
|
|
|
|
|
|
sub onMessageChannelSubmissionCancelled { |
11050
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11051
|
|
|
|
|
|
|
} |
11052
|
|
|
|
|
|
|
|
11053
|
|
|
|
|
|
|
sub onMessageChannelSubmissionRecipientDone { |
11054
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11055
|
0
|
0
|
0
|
|
|
0
|
my $recipientActorOnStore = shift; die 'wrong type '.ref($recipientActorOnStore).' for $recipientActorOnStore' if defined $recipientActorOnStore && ref $recipientActorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
0
|
|
11056
|
|
|
|
|
|
|
} |
11057
|
|
|
|
|
|
|
|
11058
|
|
|
|
|
|
|
sub onMessageChannelSubmissionRecipientFailed { |
11059
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11060
|
0
|
0
|
0
|
|
|
0
|
my $recipientActorOnStore = shift; die 'wrong type '.ref($recipientActorOnStore).' for $recipientActorOnStore' if defined $recipientActorOnStore && ref $recipientActorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
0
|
|
11061
|
|
|
|
|
|
|
} |
11062
|
|
|
|
|
|
|
|
11063
|
|
|
|
|
|
|
sub onMessageChannelSubmissionDone { |
11064
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11065
|
0
|
|
|
|
|
0
|
my $succeeded = shift; |
11066
|
0
|
|
|
|
|
0
|
my $failed = shift; |
11067
|
|
|
|
|
|
|
} |
11068
|
|
|
|
|
|
|
|
11069
|
|
|
|
|
|
|
### Receiving messages |
11070
|
|
|
|
|
|
|
|
11071
|
|
|
|
|
|
|
sub processGroupDataMessage { |
11072
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11073
|
0
|
|
|
|
|
0
|
my $message = shift; |
11074
|
0
|
|
|
|
|
0
|
my $section = shift; |
11075
|
|
|
|
|
|
|
|
11076
|
0
|
0
|
|
|
|
0
|
if (! $o->{actor}->isGroupMember($message->sender->publicKey->hash)) { |
11077
|
|
|
|
|
|
|
# TODO: |
11078
|
|
|
|
|
|
|
# If the sender is not a known group member, we should run actor group discovery on the sender. He may be part of us, but we don't know that yet. |
11079
|
|
|
|
|
|
|
# At the very least, we should keep this message, and reconsider it if the actor group changes within the next few minutes (e.g. through another message). |
11080
|
0
|
|
|
|
|
0
|
return; |
11081
|
|
|
|
|
|
|
} |
11082
|
|
|
|
|
|
|
|
11083
|
0
|
|
|
|
|
0
|
for my $child ($section->children) { |
11084
|
0
|
|
0
|
|
|
0
|
my $dataHandler = $o->{dataHandlers}->{$child->bytes} // next; |
11085
|
0
|
|
|
|
|
0
|
$dataHandler->mergeExternalData($message->sender->store, $child, $message->source); |
11086
|
|
|
|
|
|
|
} |
11087
|
|
|
|
|
|
|
|
11088
|
0
|
|
|
|
|
0
|
return 1; |
11089
|
|
|
|
|
|
|
} |
11090
|
|
|
|
|
|
|
|
11091
|
|
|
|
|
|
|
package CDS::HTTPServer; |
11092
|
|
|
|
|
|
|
|
11093
|
1
|
|
|
1
|
|
1208
|
use parent -norequire, 'HTTP::Server::Simple'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
11094
|
|
|
|
|
|
|
|
11095
|
|
|
|
|
|
|
sub new { |
11096
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
11097
|
|
|
|
|
|
|
|
11098
|
0
|
|
|
|
|
0
|
my $o = $class->SUPER::new(@_); |
11099
|
0
|
|
|
|
|
0
|
$o->{logger} = CDS::HTTPServer::Logger->new(*STDERR); |
11100
|
0
|
|
|
|
|
0
|
$o->{handlers} = []; |
11101
|
0
|
|
|
|
|
0
|
return $o; |
11102
|
|
|
|
|
|
|
} |
11103
|
|
|
|
|
|
|
|
11104
|
|
|
|
|
|
|
sub addHandler { |
11105
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11106
|
0
|
|
|
|
|
0
|
my $handler = shift; |
11107
|
|
|
|
|
|
|
|
11108
|
0
|
|
|
|
|
0
|
push @{$o->{handlers}}, $handler; |
|
0
|
|
|
|
|
0
|
|
11109
|
|
|
|
|
|
|
} |
11110
|
|
|
|
|
|
|
|
11111
|
|
|
|
|
|
|
sub setLogger { |
11112
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11113
|
0
|
|
|
|
|
0
|
my $logger = shift; |
11114
|
|
|
|
|
|
|
|
11115
|
0
|
|
|
|
|
0
|
$o->{logger} = $logger; |
11116
|
|
|
|
|
|
|
} |
11117
|
|
|
|
|
|
|
|
11118
|
0
|
|
|
0
|
|
0
|
sub logger { shift->{logger} } |
11119
|
|
|
|
|
|
|
|
11120
|
|
|
|
|
|
|
sub setCorsAllowEverybody { |
11121
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11122
|
0
|
|
|
|
|
0
|
my $value = shift; |
11123
|
|
|
|
|
|
|
|
11124
|
0
|
|
|
|
|
0
|
$o->{corsAllowEverybody} = $value; |
11125
|
|
|
|
|
|
|
} |
11126
|
|
|
|
|
|
|
|
11127
|
0
|
|
|
0
|
|
0
|
sub corsAllowEverybody { shift->{corsAllowEverybody} } |
11128
|
|
|
|
|
|
|
|
11129
|
|
|
|
|
|
|
# *** HTTP::Server::Simple interface |
11130
|
|
|
|
|
|
|
|
11131
|
|
|
|
|
|
|
sub print_banner { |
11132
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11133
|
|
|
|
|
|
|
|
11134
|
0
|
|
|
|
|
0
|
$o->{logger}->onServerStarts($o->port); |
11135
|
|
|
|
|
|
|
} |
11136
|
|
|
|
|
|
|
|
11137
|
|
|
|
|
|
|
sub setup { |
11138
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11139
|
|
|
|
|
|
|
|
11140
|
0
|
|
|
|
|
0
|
my %parameters = @_; |
11141
|
|
|
|
|
|
|
$o->{request} = CDS::HTTPServer::Request->new({ |
11142
|
|
|
|
|
|
|
logger => $o->logger, |
11143
|
|
|
|
|
|
|
method => $parameters{method}, |
11144
|
|
|
|
|
|
|
path => $parameters{path}, |
11145
|
|
|
|
|
|
|
protocol => $parameters{protocol}, |
11146
|
|
|
|
|
|
|
queryString => $parameters{query_string}, |
11147
|
|
|
|
|
|
|
peerAddress => $parameters{peeraddr}, |
11148
|
|
|
|
|
|
|
peerPort => $parameters{peerport}, |
11149
|
0
|
|
|
|
|
0
|
headers => {}, |
11150
|
|
|
|
|
|
|
corsAllowEverybody => $o->corsAllowEverybody, |
11151
|
|
|
|
|
|
|
}); |
11152
|
|
|
|
|
|
|
} |
11153
|
|
|
|
|
|
|
|
11154
|
|
|
|
|
|
|
sub headers { |
11155
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11156
|
0
|
|
|
|
|
0
|
my $headers = shift; |
11157
|
|
|
|
|
|
|
|
11158
|
0
|
|
|
|
|
0
|
while (scalar @$headers) { |
11159
|
0
|
|
|
|
|
0
|
my $key = shift @$headers; |
11160
|
0
|
|
|
|
|
0
|
my $value = shift @$headers; |
11161
|
0
|
|
|
|
|
0
|
$o->{request}->setHeader($key, $value); |
11162
|
|
|
|
|
|
|
} |
11163
|
|
|
|
|
|
|
|
11164
|
|
|
|
|
|
|
# Read the content length |
11165
|
0
|
|
0
|
|
|
0
|
$o->{request}->setRemainingData($o->{request}->header('content-length') // 0); |
11166
|
|
|
|
|
|
|
} |
11167
|
|
|
|
|
|
|
|
11168
|
|
|
|
|
|
|
sub handler { |
11169
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11170
|
|
|
|
|
|
|
|
11171
|
|
|
|
|
|
|
# Start writing the log line |
11172
|
0
|
|
|
|
|
0
|
$o->{logger}->onRequestStarts($o->{request}); |
11173
|
|
|
|
|
|
|
|
11174
|
|
|
|
|
|
|
# Process the request |
11175
|
0
|
|
|
|
|
0
|
my $responseCode = $o->process; |
11176
|
0
|
|
|
|
|
0
|
$o->{logger}->onRequestDone($o->{request}, $responseCode); |
11177
|
|
|
|
|
|
|
|
11178
|
|
|
|
|
|
|
# Wrap up |
11179
|
0
|
|
|
|
|
0
|
$o->{request}->dropData; |
11180
|
0
|
|
|
|
|
0
|
$o->{request} = undef; |
11181
|
0
|
|
|
|
|
0
|
return; |
11182
|
|
|
|
|
|
|
} |
11183
|
|
|
|
|
|
|
|
11184
|
|
|
|
|
|
|
sub process { |
11185
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11186
|
|
|
|
|
|
|
|
11187
|
|
|
|
|
|
|
# Run the handler |
11188
|
0
|
|
|
|
|
0
|
for my $handler (@{$o->{handlers}}) { |
|
0
|
|
|
|
|
0
|
|
11189
|
0
|
|
0
|
|
|
0
|
my $responseCode = $handler->process($o->{request}) || next; |
11190
|
0
|
|
|
|
|
0
|
return $responseCode; |
11191
|
|
|
|
|
|
|
} |
11192
|
|
|
|
|
|
|
|
11193
|
|
|
|
|
|
|
# Default handler |
11194
|
0
|
|
|
|
|
0
|
return $o->{request}->reply404; |
11195
|
|
|
|
|
|
|
} |
11196
|
|
|
|
|
|
|
|
11197
|
|
|
|
|
|
|
sub bad_request { |
11198
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11199
|
|
|
|
|
|
|
|
11200
|
0
|
|
|
|
|
0
|
my $content = 'Bad Request'; |
11201
|
0
|
|
|
|
|
0
|
print 'HTTP/1.1 400 Bad Request', "\r\n"; |
11202
|
0
|
|
|
|
|
0
|
print 'Content-Length: ', length $content, "\r\n"; |
11203
|
0
|
|
|
|
|
0
|
print 'Content-Type: text/plain; charset=utf-8', "\r\n"; |
11204
|
0
|
|
|
|
|
0
|
print "\r\n"; |
11205
|
0
|
|
|
|
|
0
|
print $content; |
11206
|
0
|
|
|
|
|
0
|
$o->{request} = undef; |
11207
|
|
|
|
|
|
|
} |
11208
|
|
|
|
|
|
|
|
11209
|
|
|
|
|
|
|
package CDS::HTTPServer::IdentificationHandler; |
11210
|
|
|
|
|
|
|
|
11211
|
|
|
|
|
|
|
sub new { |
11212
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
11213
|
0
|
|
|
|
|
0
|
my $root = shift; |
11214
|
|
|
|
|
|
|
|
11215
|
0
|
|
|
|
|
0
|
return bless {root => $root}; |
11216
|
|
|
|
|
|
|
} |
11217
|
|
|
|
|
|
|
|
11218
|
|
|
|
|
|
|
sub process { |
11219
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11220
|
0
|
|
|
|
|
0
|
my $request = shift; |
11221
|
|
|
|
|
|
|
|
11222
|
0
|
|
0
|
|
|
0
|
my $path = $request->pathAbove($o->{root}) // return; |
11223
|
0
|
0
|
|
|
|
0
|
return if $path ne '/'; |
11224
|
|
|
|
|
|
|
|
11225
|
|
|
|
|
|
|
# Options |
11226
|
0
|
0
|
|
|
|
0
|
return $request->replyOptions('HEAD', 'GET') if $request->method eq 'OPTIONS'; |
11227
|
|
|
|
|
|
|
|
11228
|
|
|
|
|
|
|
# Get |
11229
|
0
|
0
|
0
|
|
|
0
|
return $request->reply200HTML('Condensation HTTP StoreThis is a Condensation HTTP Store server.') if $request->method eq 'HEAD' || $request->method eq 'GET'; |
11230
|
|
|
|
|
|
|
|
11231
|
0
|
|
|
|
|
0
|
return $request->reply405; |
11232
|
|
|
|
|
|
|
} |
11233
|
|
|
|
|
|
|
|
11234
|
|
|
|
|
|
|
package CDS::HTTPServer::Logger; |
11235
|
|
|
|
|
|
|
|
11236
|
|
|
|
|
|
|
sub new { |
11237
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
11238
|
0
|
|
|
|
|
0
|
my $fileHandle = shift; |
11239
|
|
|
|
|
|
|
|
11240
|
0
|
|
|
|
|
0
|
return bless { |
11241
|
|
|
|
|
|
|
fileHandle => $fileHandle, |
11242
|
|
|
|
|
|
|
lineStarted => 0, |
11243
|
|
|
|
|
|
|
}; |
11244
|
|
|
|
|
|
|
} |
11245
|
|
|
|
|
|
|
|
11246
|
|
|
|
|
|
|
sub onServerStarts { |
11247
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11248
|
0
|
|
|
|
|
0
|
my $port = shift; |
11249
|
|
|
|
|
|
|
|
11250
|
0
|
|
|
|
|
0
|
my $fh = $o->{fileHandle}; |
11251
|
0
|
|
|
|
|
0
|
my @t = localtime(time); |
11252
|
0
|
|
|
|
|
0
|
printf $fh '%04d-%02d-%02d %02d:%02d:%02d ', $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]; |
11253
|
0
|
|
|
|
|
0
|
print $fh 'Server ready at http://localhost:', $port, "\n"; |
11254
|
|
|
|
|
|
|
} |
11255
|
|
|
|
|
|
|
|
11256
|
|
|
|
|
|
|
sub onRequestStarts { |
11257
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11258
|
0
|
|
|
|
|
0
|
my $request = shift; |
11259
|
|
|
|
|
|
|
|
11260
|
0
|
|
|
|
|
0
|
my $fh = $o->{fileHandle}; |
11261
|
0
|
|
|
|
|
0
|
my @t = localtime(time); |
11262
|
0
|
|
|
|
|
0
|
printf $fh '%04d-%02d-%02d %02d:%02d:%02d ', $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]; |
11263
|
0
|
|
|
|
|
0
|
print $fh $request->peerAddress, ' ', $request->method, ' ', $request->path; |
11264
|
0
|
|
|
|
|
0
|
$o->{lineStarted} = 1; |
11265
|
|
|
|
|
|
|
} |
11266
|
|
|
|
|
|
|
|
11267
|
|
|
|
|
|
|
sub onRequestError { |
11268
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11269
|
0
|
|
|
|
|
0
|
my $request = shift; |
11270
|
|
|
|
|
|
|
|
11271
|
0
|
|
|
|
|
0
|
my $fh = $o->{fileHandle}; |
11272
|
0
|
0
|
|
|
|
0
|
print $fh "\n" if $o->{lineStarted}; |
11273
|
0
|
|
|
|
|
0
|
print $fh ' ', @_, "\n"; |
11274
|
0
|
|
|
|
|
0
|
$o->{lineStarted} = 0; |
11275
|
|
|
|
|
|
|
} |
11276
|
|
|
|
|
|
|
|
11277
|
|
|
|
|
|
|
sub onRequestDone { |
11278
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11279
|
0
|
|
|
|
|
0
|
my $request = shift; |
11280
|
0
|
|
|
|
|
0
|
my $responseCode = shift; |
11281
|
|
|
|
|
|
|
|
11282
|
0
|
|
|
|
|
0
|
my $fh = $o->{fileHandle}; |
11283
|
0
|
0
|
|
|
|
0
|
print $fh ' ===> ' if ! $o->{lineStarted}; |
11284
|
0
|
|
|
|
|
0
|
print $fh ' ', $responseCode, "\n"; |
11285
|
0
|
|
|
|
|
0
|
$o->{lineStarted} = 0; |
11286
|
|
|
|
|
|
|
} |
11287
|
|
|
|
|
|
|
|
11288
|
|
|
|
|
|
|
package CDS::HTTPServer::MessageGatewayHandler; |
11289
|
|
|
|
|
|
|
|
11290
|
|
|
|
|
|
|
sub new { |
11291
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
11292
|
0
|
|
|
|
|
0
|
my $root = shift; |
11293
|
0
|
|
|
|
|
0
|
my $actor = shift; |
11294
|
0
|
|
|
|
|
0
|
my $store = shift; |
11295
|
0
|
0
|
0
|
|
|
0
|
my $recipientHash = shift; die 'wrong type '.ref($recipientHash).' for $recipientHash' if defined $recipientHash && ref $recipientHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
11296
|
|
|
|
|
|
|
|
11297
|
0
|
|
|
|
|
0
|
return bless {root => $root, actor => $actor, store => $store, recipientHash => $recipientHash}; |
11298
|
|
|
|
|
|
|
} |
11299
|
|
|
|
|
|
|
|
11300
|
|
|
|
|
|
|
sub process { |
11301
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11302
|
0
|
|
|
|
|
0
|
my $request = shift; |
11303
|
|
|
|
|
|
|
|
11304
|
0
|
|
0
|
|
|
0
|
my $path = $request->pathAbove($o->{root}) // return; |
11305
|
0
|
0
|
|
|
|
0
|
return if $path ne '/'; |
11306
|
|
|
|
|
|
|
|
11307
|
|
|
|
|
|
|
# Options |
11308
|
0
|
0
|
|
|
|
0
|
return $request->replyOptions('HEAD', 'GET', 'PUT', 'POST', 'DELETE') if $request->method eq 'OPTIONS'; |
11309
|
|
|
|
|
|
|
|
11310
|
|
|
|
|
|
|
# Prepare a message |
11311
|
0
|
|
|
|
|
0
|
my $message = CDS::Record->new; |
11312
|
0
|
|
|
|
|
0
|
$message->add('time')->addInteger(CDS->now); |
11313
|
0
|
|
|
|
|
0
|
$message->add('ip')->add($request->peerAddress); |
11314
|
0
|
|
|
|
|
0
|
$message->add('method')->add($request->method); |
11315
|
0
|
|
|
|
|
0
|
$message->add('path')->add($request->path); |
11316
|
0
|
|
|
|
|
0
|
$message->add('query string')->add($request->queryString); |
11317
|
|
|
|
|
|
|
|
11318
|
0
|
|
|
|
|
0
|
my $headersRecord = $message->add('headers'); |
11319
|
0
|
|
|
|
|
0
|
my $headers = $request->headers; |
11320
|
0
|
|
|
|
|
0
|
for my $key (keys %$headers) { |
11321
|
0
|
|
|
|
|
0
|
$headersRecord->add($key)->add($headers->{$key}); |
11322
|
|
|
|
|
|
|
} |
11323
|
|
|
|
|
|
|
|
11324
|
|
|
|
|
|
|
# Prepare a channel |
11325
|
0
|
|
|
|
|
0
|
my $channel = CDS::MessageChannel->new($o->{actor}, CDS->randomBytes(8), CDS->WEEK); |
11326
|
0
|
|
|
|
|
0
|
$o->{messageChannel}->setRecipients([$o->{recipientHash}], []); |
11327
|
|
|
|
|
|
|
|
11328
|
|
|
|
|
|
|
# Add the data |
11329
|
0
|
0
|
|
|
|
0
|
if ($request->remainingData > 1024) { |
|
|
0
|
|
|
|
|
|
11330
|
|
|
|
|
|
|
# Store the data as a separate object |
11331
|
0
|
|
|
|
|
0
|
my $object = CDS::Object->create(CDS::Object->emptyHeader, $request->readData); |
11332
|
0
|
|
|
|
|
0
|
my $key = CDS->randomKey; |
11333
|
0
|
|
|
|
|
0
|
my $encryptedObject = $object->crypt($key); |
11334
|
0
|
|
|
|
|
0
|
my $hash = $encryptedObject->calculateHash; |
11335
|
0
|
|
|
|
|
0
|
$message->add('data')->addHash($hash); |
11336
|
0
|
|
|
|
|
0
|
$channel->addObject($hash, $encryptedObject); |
11337
|
|
|
|
|
|
|
} elsif ($request->remainingData) { |
11338
|
0
|
|
|
|
|
0
|
$message->add('data')->add($request->readData) |
11339
|
|
|
|
|
|
|
} |
11340
|
|
|
|
|
|
|
|
11341
|
|
|
|
|
|
|
# Submit |
11342
|
0
|
|
|
|
|
0
|
my ($submission, $missingObject) = $channel->submit($message, $o); |
11343
|
0
|
|
|
|
|
0
|
$o->{actor}->sendMessages; |
11344
|
|
|
|
|
|
|
|
11345
|
0
|
0
|
|
|
|
0
|
return $submission ? $request->reply200 : $request->reply500('Unable to send the message.'); |
11346
|
|
|
|
|
|
|
} |
11347
|
|
|
|
|
|
|
|
11348
|
|
|
|
|
|
|
sub onMessageChannelSubmissionCancelled { |
11349
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11350
|
|
|
|
|
|
|
} |
11351
|
|
|
|
|
|
|
|
11352
|
|
|
|
|
|
|
sub onMessageChannelSubmissionRecipientDone { |
11353
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11354
|
0
|
0
|
0
|
|
|
0
|
my $recipientActorOnStore = shift; die 'wrong type '.ref($recipientActorOnStore).' for $recipientActorOnStore' if defined $recipientActorOnStore && ref $recipientActorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
0
|
|
11355
|
|
|
|
|
|
|
} |
11356
|
|
|
|
|
|
|
|
11357
|
|
|
|
|
|
|
sub onMessageChannelSubmissionRecipientFailed { |
11358
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11359
|
0
|
0
|
0
|
|
|
0
|
my $recipientActorOnStore = shift; die 'wrong type '.ref($recipientActorOnStore).' for $recipientActorOnStore' if defined $recipientActorOnStore && ref $recipientActorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
0
|
|
11360
|
|
|
|
|
|
|
} |
11361
|
|
|
|
|
|
|
|
11362
|
|
|
|
|
|
|
sub onMessageChannelSubmissionDone { |
11363
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11364
|
0
|
|
|
|
|
0
|
my $succeeded = shift; |
11365
|
0
|
|
|
|
|
0
|
my $failed = shift; |
11366
|
|
|
|
|
|
|
} |
11367
|
|
|
|
|
|
|
|
11368
|
|
|
|
|
|
|
package CDS::HTTPServer::Request; |
11369
|
|
|
|
|
|
|
|
11370
|
|
|
|
|
|
|
sub new { |
11371
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
11372
|
0
|
|
|
|
|
0
|
my $parameters = shift; |
11373
|
|
|
|
|
|
|
|
11374
|
0
|
|
|
|
|
0
|
return bless $parameters; |
11375
|
|
|
|
|
|
|
} |
11376
|
|
|
|
|
|
|
|
11377
|
0
|
|
|
0
|
|
0
|
sub logger { shift->{logger} } |
11378
|
0
|
|
|
0
|
|
0
|
sub method { shift->{method} } |
11379
|
0
|
|
|
0
|
|
0
|
sub path { shift->{path} } |
11380
|
0
|
|
|
0
|
|
0
|
sub queryString { shift->{queryString} } |
11381
|
0
|
|
|
0
|
|
0
|
sub peerAddress { shift->{peerAddress} } |
11382
|
0
|
|
|
0
|
|
0
|
sub peerPort { shift->{peerPort} } |
11383
|
0
|
|
|
0
|
|
0
|
sub headers { shift->{headers} } |
11384
|
0
|
|
|
0
|
|
0
|
sub remainingData { shift->{remainingData} } |
11385
|
0
|
|
|
0
|
|
0
|
sub corsAllowEverybody { shift->{corsAllowEverybody} } |
11386
|
|
|
|
|
|
|
|
11387
|
|
|
|
|
|
|
# *** Path |
11388
|
|
|
|
|
|
|
|
11389
|
|
|
|
|
|
|
sub pathAbove { |
11390
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11391
|
0
|
|
|
|
|
0
|
my $root = shift; |
11392
|
|
|
|
|
|
|
|
11393
|
0
|
0
|
|
|
|
0
|
$root .= '/' if $root !~ /\/$/; |
11394
|
0
|
0
|
|
|
|
0
|
return if substr($o->{path}, 0, length $root) ne $root; |
11395
|
0
|
|
|
|
|
0
|
return substr($o->{path}, length($root) - 1); |
11396
|
|
|
|
|
|
|
} |
11397
|
|
|
|
|
|
|
|
11398
|
|
|
|
|
|
|
# *** Request data |
11399
|
|
|
|
|
|
|
|
11400
|
|
|
|
|
|
|
sub setRemainingData { |
11401
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11402
|
0
|
|
|
|
|
0
|
my $remainingData = shift; |
11403
|
|
|
|
|
|
|
|
11404
|
0
|
|
|
|
|
0
|
$o->{remainingData} = $remainingData; |
11405
|
|
|
|
|
|
|
} |
11406
|
|
|
|
|
|
|
|
11407
|
|
|
|
|
|
|
# Reads the request data |
11408
|
|
|
|
|
|
|
sub readData { |
11409
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11410
|
|
|
|
|
|
|
|
11411
|
0
|
|
|
|
|
0
|
my @buffers; |
11412
|
0
|
|
|
|
|
0
|
while ($o->{remainingData} > 0) { |
11413
|
0
|
|
0
|
|
|
0
|
my $read = sysread(STDIN, my $buffer, $o->{remainingData}) || return; |
11414
|
0
|
|
|
|
|
0
|
$o->{remainingData} -= $read; |
11415
|
0
|
|
|
|
|
0
|
push @buffers, $buffer; |
11416
|
|
|
|
|
|
|
} |
11417
|
|
|
|
|
|
|
|
11418
|
0
|
|
|
|
|
0
|
return join('', @buffers); |
11419
|
|
|
|
|
|
|
} |
11420
|
|
|
|
|
|
|
|
11421
|
|
|
|
|
|
|
# Read the request data and writes it directly to a file handle |
11422
|
|
|
|
|
|
|
sub copyDataAndCalculateHash { |
11423
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11424
|
0
|
|
|
|
|
0
|
my $fh = shift; |
11425
|
|
|
|
|
|
|
|
11426
|
0
|
|
|
|
|
0
|
my $sha = Digest::SHA->new(256); |
11427
|
0
|
|
|
|
|
0
|
while ($o->{remainingData} > 0) { |
11428
|
0
|
|
0
|
|
|
0
|
my $read = sysread(STDIN, my $buffer, $o->{remainingData}) || return; |
11429
|
0
|
|
|
|
|
0
|
$o->{remainingData} -= $read; |
11430
|
0
|
|
|
|
|
0
|
$sha->add($buffer); |
11431
|
0
|
|
|
|
|
0
|
print $fh $buffer; |
11432
|
|
|
|
|
|
|
} |
11433
|
|
|
|
|
|
|
|
11434
|
0
|
|
|
|
|
0
|
return $sha->digest; |
11435
|
|
|
|
|
|
|
} |
11436
|
|
|
|
|
|
|
|
11437
|
|
|
|
|
|
|
# Reads and drops the request data |
11438
|
|
|
|
|
|
|
sub dropData { |
11439
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11440
|
|
|
|
|
|
|
|
11441
|
0
|
|
|
|
|
0
|
while ($o->{remainingData} > 0) { |
11442
|
0
|
|
0
|
|
|
0
|
$o->{remainingData} -= read(STDIN, my $buffer, $o->{remainingData}) || return; |
11443
|
|
|
|
|
|
|
} |
11444
|
|
|
|
|
|
|
} |
11445
|
|
|
|
|
|
|
|
11446
|
|
|
|
|
|
|
# *** Headers |
11447
|
|
|
|
|
|
|
|
11448
|
|
|
|
|
|
|
sub setHeader { |
11449
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11450
|
0
|
|
|
|
|
0
|
my $key = shift; |
11451
|
0
|
|
|
|
|
0
|
my $value = shift; |
11452
|
|
|
|
|
|
|
|
11453
|
0
|
|
|
|
|
0
|
$o->{headers}->{lc($key)} = $value; |
11454
|
|
|
|
|
|
|
} |
11455
|
|
|
|
|
|
|
|
11456
|
|
|
|
|
|
|
sub header { |
11457
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11458
|
0
|
|
|
|
|
0
|
my $key = shift; |
11459
|
|
|
|
|
|
|
|
11460
|
0
|
|
|
|
|
0
|
return $o->{headers}->{lc($key)}; |
11461
|
|
|
|
|
|
|
} |
11462
|
|
|
|
|
|
|
|
11463
|
|
|
|
|
|
|
# *** Query string |
11464
|
|
|
|
|
|
|
|
11465
|
|
|
|
|
|
|
sub parseQueryString { |
11466
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11467
|
|
|
|
|
|
|
|
11468
|
0
|
0
|
|
|
|
0
|
return {} if ! defined $o->{queryString}; |
11469
|
|
|
|
|
|
|
|
11470
|
0
|
|
|
|
|
0
|
my $values = {}; |
11471
|
0
|
|
|
|
|
0
|
for my $pair (split /&/, $o->{queryString}) { |
11472
|
0
|
0
|
|
|
|
0
|
if ($pair =~ /^(.*?)=(.*)$/) { |
11473
|
0
|
|
|
|
|
0
|
my $key = $1; |
11474
|
0
|
|
|
|
|
0
|
my $value = $2; |
11475
|
0
|
|
|
|
|
0
|
$values->{&uri_decode($key)} = &uri_decode($value); |
11476
|
|
|
|
|
|
|
} else { |
11477
|
0
|
|
|
|
|
0
|
$values->{&uri_decode($pair)} = 1; |
11478
|
|
|
|
|
|
|
} |
11479
|
|
|
|
|
|
|
} |
11480
|
|
|
|
|
|
|
|
11481
|
0
|
|
|
|
|
0
|
return $values; |
11482
|
|
|
|
|
|
|
} |
11483
|
|
|
|
|
|
|
|
11484
|
|
|
|
|
|
|
sub uri_decode { |
11485
|
0
|
|
|
0
|
|
0
|
my $encoded = shift; |
11486
|
|
|
|
|
|
|
|
11487
|
0
|
|
|
|
|
0
|
$encoded =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; |
|
0
|
|
|
|
|
0
|
|
11488
|
0
|
|
|
|
|
0
|
return $encoded; |
11489
|
|
|
|
|
|
|
} |
11490
|
|
|
|
|
|
|
|
11491
|
|
|
|
|
|
|
# *** Condensation signature |
11492
|
|
|
|
|
|
|
|
11493
|
|
|
|
|
|
|
sub checkSignature { |
11494
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11495
|
0
|
|
|
|
|
0
|
my $store = shift; |
11496
|
0
|
|
|
|
|
0
|
my $contentBytesToSign = shift; |
11497
|
|
|
|
|
|
|
|
11498
|
|
|
|
|
|
|
# Check the date |
11499
|
0
|
|
0
|
|
|
0
|
my $dateString = $o->{headers}->{'condensation-date'} // $o->{headers}->{'date'} // return; |
|
|
|
0
|
|
|
|
|
11500
|
0
|
|
0
|
|
|
0
|
my $date = HTTP::Date::str2time($dateString) // return; |
11501
|
0
|
|
|
|
|
0
|
my $now = time; |
11502
|
0
|
0
|
0
|
|
|
0
|
return if $date < $now - 120 || $date > $now + 60; |
11503
|
|
|
|
|
|
|
|
11504
|
|
|
|
|
|
|
# Get and check the actor |
11505
|
0
|
|
0
|
|
|
0
|
my $actorHash = CDS::Hash->fromHex($o->{headers}->{'condensation-actor'}) // return; |
11506
|
0
|
|
|
|
|
0
|
my ($publicKeyObject, $error) = $store->get($actorHash); |
11507
|
0
|
0
|
|
|
|
0
|
return if ! $publicKeyObject; |
11508
|
0
|
0
|
|
|
|
0
|
return if ! $publicKeyObject->calculateHash->equals($actorHash); |
11509
|
0
|
|
0
|
|
|
0
|
my $publicKey = CDS::PublicKey->fromObject($publicKeyObject) // return; |
11510
|
|
|
|
|
|
|
|
11511
|
|
|
|
|
|
|
# Text to sign |
11512
|
0
|
|
|
|
|
0
|
my $bytesToSign = $dateString."\0".uc($o->{method})."\0".$o->{headers}->{'host'}.$o->{path}; |
11513
|
0
|
0
|
|
|
|
0
|
$bytesToSign .= "\0".$contentBytesToSign if defined $contentBytesToSign; |
11514
|
0
|
|
|
|
|
0
|
my $hashToSign = CDS::Hash->calculateFor($bytesToSign); |
11515
|
|
|
|
|
|
|
|
11516
|
|
|
|
|
|
|
# Check the signature |
11517
|
0
|
|
0
|
|
|
0
|
my $signatureString = $o->{headers}->{'condensation-signature'} // return; |
11518
|
0
|
|
0
|
|
|
0
|
$signatureString =~ /^\s*([0-9a-z]{512,512})\s*$/ // return; |
11519
|
0
|
|
|
|
|
0
|
my $signature = pack('H*', $1); |
11520
|
0
|
0
|
|
|
|
0
|
return if ! $publicKey->verifyHash($hashToSign, $signature); |
11521
|
|
|
|
|
|
|
|
11522
|
|
|
|
|
|
|
# Return the verified actor hash |
11523
|
0
|
|
|
|
|
0
|
return $actorHash; |
11524
|
|
|
|
|
|
|
} |
11525
|
|
|
|
|
|
|
|
11526
|
|
|
|
|
|
|
# *** Reply functions |
11527
|
|
|
|
|
|
|
|
11528
|
|
|
|
|
|
|
sub reply200 { |
11529
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11530
|
0
|
|
0
|
|
|
0
|
my $content = shift // ''; |
11531
|
|
|
|
|
|
|
|
11532
|
0
|
0
|
|
|
|
0
|
return length $content ? $o->reply(200, 'OK', &textContentType, $content) : $o->reply(204, 'No Content', {}); |
11533
|
|
|
|
|
|
|
} |
11534
|
|
|
|
|
|
|
|
11535
|
|
|
|
|
|
|
sub reply200Bytes { |
11536
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11537
|
0
|
|
0
|
|
|
0
|
my $content = shift // ''; |
11538
|
|
|
|
|
|
|
|
11539
|
0
|
0
|
|
|
|
0
|
return length $content ? $o->reply(200, 'OK', {'Content-Type' => 'application/octet-stream'}, $content) : $o->reply(204, 'No Content', {}); |
11540
|
|
|
|
|
|
|
} |
11541
|
|
|
|
|
|
|
|
11542
|
|
|
|
|
|
|
sub reply200HTML { |
11543
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11544
|
0
|
|
0
|
|
|
0
|
my $content = shift // ''; |
11545
|
|
|
|
|
|
|
|
11546
|
0
|
0
|
|
|
|
0
|
return length $content ? $o->reply(200, 'OK', {'Content-Type' => 'text/html; charset=utf-8'}, $content) : $o->reply(204, 'No Content', {}); |
11547
|
|
|
|
|
|
|
} |
11548
|
|
|
|
|
|
|
|
11549
|
|
|
|
|
|
|
sub replyOptions { |
11550
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11551
|
|
|
|
|
|
|
|
11552
|
0
|
|
|
|
|
0
|
my $headers = {}; |
11553
|
0
|
|
|
|
|
0
|
$headers->{'Allow'} = join(', ', @_, 'OPTIONS'); |
11554
|
0
|
0
|
0
|
|
|
0
|
$headers->{'Access-Control-Allow-Methods'} = join(', ', @_, 'OPTIONS') if $o->corsAllowEverybody && $o->{headers}->{'origin'}; |
11555
|
0
|
|
|
|
|
0
|
return $o->reply(200, 'OK', $headers); |
11556
|
|
|
|
|
|
|
} |
11557
|
|
|
|
|
|
|
|
11558
|
|
|
|
|
|
|
sub replyFatalError { |
11559
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11560
|
|
|
|
|
|
|
|
11561
|
0
|
|
|
|
|
0
|
$o->{logger}->onRequestError($o, @_); |
11562
|
0
|
|
|
|
|
0
|
return $o->reply500; |
11563
|
|
|
|
|
|
|
} |
11564
|
|
|
|
|
|
|
|
11565
|
|
|
|
|
|
|
sub reply303 { |
11566
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11567
|
0
|
|
|
|
|
0
|
my $location = shift; |
11568
|
0
|
|
|
|
|
0
|
$o->reply(303, 'See Other', {'Location' => $location}) } |
11569
|
0
|
|
|
0
|
|
0
|
sub reply400 { shift->reply(400, 'Bad Request', &textContentType, @_) } |
11570
|
0
|
|
|
0
|
|
0
|
sub reply403 { shift->reply(403, 'Forbidden', &textContentType, @_) } |
11571
|
0
|
|
|
0
|
|
0
|
sub reply404 { shift->reply(404, 'Not Found', &textContentType, @_) } |
11572
|
0
|
|
|
0
|
|
0
|
sub reply405 { shift->reply(405, 'Method Not Allowed', &textContentType, @_) } |
11573
|
0
|
|
|
0
|
|
0
|
sub reply500 { shift->reply(500, 'Internal Server Error', &textContentType, @_) } |
11574
|
0
|
|
|
0
|
|
0
|
sub reply503 { shift->reply(503, 'Service Not Available', &textContentType, @_) } |
11575
|
|
|
|
|
|
|
|
11576
|
|
|
|
|
|
|
sub reply { |
11577
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11578
|
0
|
|
|
|
|
0
|
my $responseCode = shift; |
11579
|
0
|
|
|
|
|
0
|
my $responseLabel = shift; |
11580
|
0
|
|
0
|
|
|
0
|
my $headers = shift // {}; |
11581
|
0
|
|
0
|
|
|
0
|
my $content = shift // ''; |
11582
|
|
|
|
|
|
|
|
11583
|
|
|
|
|
|
|
# Content-related headers |
11584
|
0
|
|
|
|
|
0
|
$headers->{'Content-Length'} = length($content); |
11585
|
|
|
|
|
|
|
|
11586
|
|
|
|
|
|
|
# Origin |
11587
|
0
|
0
|
0
|
|
|
0
|
if ($o->corsAllowEverybody && (my $origin = $o->{headers}->{'origin'})) { |
11588
|
0
|
|
|
|
|
0
|
$headers->{'Access-Control-Allow-Origin'} = $origin; |
11589
|
0
|
|
|
|
|
0
|
$headers->{'Access-Control-Allow-Headers'} = 'Content-Type'; |
11590
|
0
|
|
|
|
|
0
|
$headers->{'Access-Control-Max-Age'} = '86400'; |
11591
|
|
|
|
|
|
|
} |
11592
|
|
|
|
|
|
|
|
11593
|
|
|
|
|
|
|
# Write the reply |
11594
|
0
|
|
|
|
|
0
|
print 'HTTP/1.1 ', $responseCode, ' ', $responseLabel, "\r\n"; |
11595
|
0
|
|
|
|
|
0
|
for my $key (keys %$headers) { |
11596
|
0
|
|
|
|
|
0
|
print $key, ': ', $headers->{$key}, "\r\n"; |
11597
|
|
|
|
|
|
|
} |
11598
|
0
|
|
|
|
|
0
|
print "\r\n"; |
11599
|
0
|
0
|
|
|
|
0
|
print $content if $o->{method} ne 'HEAD'; |
11600
|
|
|
|
|
|
|
|
11601
|
|
|
|
|
|
|
# Return the response code |
11602
|
0
|
|
|
|
|
0
|
return $responseCode; |
11603
|
|
|
|
|
|
|
} |
11604
|
|
|
|
|
|
|
|
11605
|
0
|
|
|
0
|
|
0
|
sub textContentType { {'Content-Type' => 'text/plain; charset=utf-8'} } |
11606
|
|
|
|
|
|
|
|
11607
|
|
|
|
|
|
|
package CDS::HTTPServer::StaticContentHandler; |
11608
|
|
|
|
|
|
|
|
11609
|
|
|
|
|
|
|
sub new { |
11610
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
11611
|
0
|
|
|
|
|
0
|
my $path = shift; |
11612
|
0
|
|
|
|
|
0
|
my $content = shift; |
11613
|
0
|
|
|
|
|
0
|
my $contentType = shift; |
11614
|
|
|
|
|
|
|
|
11615
|
0
|
|
|
|
|
0
|
return bless { |
11616
|
|
|
|
|
|
|
path => $path, |
11617
|
|
|
|
|
|
|
content => $content, |
11618
|
|
|
|
|
|
|
contentType => $contentType, |
11619
|
|
|
|
|
|
|
}; |
11620
|
|
|
|
|
|
|
} |
11621
|
|
|
|
|
|
|
|
11622
|
|
|
|
|
|
|
sub process { |
11623
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11624
|
0
|
|
|
|
|
0
|
my $request = shift; |
11625
|
|
|
|
|
|
|
|
11626
|
0
|
0
|
|
|
|
0
|
return if $request->path ne $o->{path}; |
11627
|
|
|
|
|
|
|
|
11628
|
|
|
|
|
|
|
# Options |
11629
|
0
|
0
|
|
|
|
0
|
return $request->replyOptions('HEAD', 'GET') if $request->method eq 'OPTIONS'; |
11630
|
|
|
|
|
|
|
|
11631
|
|
|
|
|
|
|
# GET |
11632
|
0
|
0
|
|
|
|
0
|
return $request->reply(200, 'OK', {'Content-Type' => $o->{contentType}}, $o->{content}) if $request->method eq 'GET'; |
11633
|
|
|
|
|
|
|
|
11634
|
|
|
|
|
|
|
# Everything else |
11635
|
0
|
|
|
|
|
0
|
return $request->reply405; |
11636
|
|
|
|
|
|
|
} |
11637
|
|
|
|
|
|
|
|
11638
|
|
|
|
|
|
|
package CDS::HTTPServer::StaticFilesHandler; |
11639
|
|
|
|
|
|
|
|
11640
|
|
|
|
|
|
|
sub new { |
11641
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
11642
|
0
|
|
|
|
|
0
|
my $root = shift; |
11643
|
0
|
|
|
|
|
0
|
my $folder = shift; |
11644
|
0
|
|
0
|
|
|
0
|
my $defaultFile = shift // ''; |
11645
|
|
|
|
|
|
|
|
11646
|
0
|
|
|
|
|
0
|
return bless { |
11647
|
|
|
|
|
|
|
root => $root, |
11648
|
|
|
|
|
|
|
folder => $folder, |
11649
|
|
|
|
|
|
|
defaultFile => $defaultFile, |
11650
|
|
|
|
|
|
|
mimeTypesByExtension => { |
11651
|
|
|
|
|
|
|
'css' => 'text/css', |
11652
|
|
|
|
|
|
|
'gif' => 'image/gif', |
11653
|
|
|
|
|
|
|
'html' => 'text/html', |
11654
|
|
|
|
|
|
|
'jpg' => 'image/jpeg', |
11655
|
|
|
|
|
|
|
'jpeg' => 'image/jpeg', |
11656
|
|
|
|
|
|
|
'js' => 'application/javascript', |
11657
|
|
|
|
|
|
|
'mp4' => 'video/mp4', |
11658
|
|
|
|
|
|
|
'ogg' => 'video/ogg', |
11659
|
|
|
|
|
|
|
'pdf' => 'application/pdf', |
11660
|
|
|
|
|
|
|
'png' => 'image/png', |
11661
|
|
|
|
|
|
|
'svg' => 'image/svg+xml', |
11662
|
|
|
|
|
|
|
'txt' => 'text/plain', |
11663
|
|
|
|
|
|
|
'webm' => 'video/webm', |
11664
|
|
|
|
|
|
|
'zip' => 'application/zip', |
11665
|
|
|
|
|
|
|
}, |
11666
|
|
|
|
|
|
|
}; |
11667
|
|
|
|
|
|
|
} |
11668
|
|
|
|
|
|
|
|
11669
|
0
|
|
|
0
|
|
0
|
sub folder { shift->{folder} } |
11670
|
0
|
|
|
0
|
|
0
|
sub defaultFile { shift->{defaultFile} } |
11671
|
0
|
|
|
0
|
|
0
|
sub mimeTypesByExtension { shift->{mimeTypesByExtension} } |
11672
|
|
|
|
|
|
|
|
11673
|
|
|
|
|
|
|
sub setContentType { |
11674
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11675
|
0
|
|
|
|
|
0
|
my $extension = shift; |
11676
|
0
|
|
|
|
|
0
|
my $contentType = shift; |
11677
|
|
|
|
|
|
|
|
11678
|
0
|
|
|
|
|
0
|
$o->{mimeTypesByExtension}->{$extension} = $contentType; |
11679
|
|
|
|
|
|
|
} |
11680
|
|
|
|
|
|
|
|
11681
|
|
|
|
|
|
|
sub process { |
11682
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11683
|
0
|
|
|
|
|
0
|
my $request = shift; |
11684
|
|
|
|
|
|
|
|
11685
|
|
|
|
|
|
|
# Options |
11686
|
0
|
0
|
|
|
|
0
|
return $request->replyOptions('HEAD', 'GET') if $request->method eq 'OPTIONS'; |
11687
|
|
|
|
|
|
|
|
11688
|
|
|
|
|
|
|
# Get |
11689
|
0
|
0
|
0
|
|
|
0
|
return $o->get($request) if $request->method eq 'GET' || $request->method eq 'HEAD'; |
11690
|
|
|
|
|
|
|
|
11691
|
|
|
|
|
|
|
# Anything else |
11692
|
0
|
|
|
|
|
0
|
return $request->reply405; |
11693
|
|
|
|
|
|
|
} |
11694
|
|
|
|
|
|
|
|
11695
|
|
|
|
|
|
|
sub get { |
11696
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11697
|
0
|
|
|
|
|
0
|
my $request = shift; |
11698
|
|
|
|
|
|
|
|
11699
|
0
|
|
0
|
|
|
0
|
my $path = $request->pathAbove($o->{root}) // return; |
11700
|
0
|
|
|
|
|
0
|
return $o->deliverFileForPath($request, $path); |
11701
|
|
|
|
|
|
|
} |
11702
|
|
|
|
|
|
|
|
11703
|
|
|
|
|
|
|
sub deliverFileForPath { |
11704
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11705
|
0
|
|
|
|
|
0
|
my $request = shift; |
11706
|
0
|
|
|
|
|
0
|
my $path = shift; |
11707
|
|
|
|
|
|
|
|
11708
|
|
|
|
|
|
|
# Hidden files (starting with a dot), as well as "." and ".." never exist |
11709
|
0
|
|
|
|
|
0
|
for my $segment (split /\/+/, $path) { |
11710
|
0
|
0
|
|
|
|
0
|
return $request->reply404 if $segment =~ /^\./; |
11711
|
|
|
|
|
|
|
} |
11712
|
|
|
|
|
|
|
|
11713
|
|
|
|
|
|
|
# If a folder is requested, we serve the default file |
11714
|
0
|
|
|
|
|
0
|
my $file = $o->{folder}.$path; |
11715
|
0
|
0
|
|
|
|
0
|
if (-d $file) { |
11716
|
0
|
0
|
|
|
|
0
|
return $request->reply404 if ! length $o->{defaultFile}; |
11717
|
0
|
0
|
|
|
|
0
|
return $request->reply303($request->path.'/') if $file !~ /\/$/; |
11718
|
0
|
|
|
|
|
0
|
$file .= $o->{defaultFile}; |
11719
|
|
|
|
|
|
|
} |
11720
|
|
|
|
|
|
|
|
11721
|
0
|
|
|
|
|
0
|
return $o->deliverFile($request, $file); |
11722
|
|
|
|
|
|
|
} |
11723
|
|
|
|
|
|
|
|
11724
|
|
|
|
|
|
|
sub deliverFile { |
11725
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11726
|
0
|
|
|
|
|
0
|
my $request = shift; |
11727
|
0
|
|
|
|
|
0
|
my $file = shift; |
11728
|
0
|
|
0
|
|
|
0
|
my $contentType = shift // $o->guessContentType($file); |
11729
|
|
|
|
|
|
|
|
11730
|
0
|
|
0
|
|
|
0
|
my $bytes = $o->readFile($file) // return $request->reply404; |
11731
|
0
|
|
|
|
|
0
|
return $request->reply(200, 'OK', {'Content-Type' => $contentType}, $bytes); |
11732
|
|
|
|
|
|
|
} |
11733
|
|
|
|
|
|
|
|
11734
|
|
|
|
|
|
|
# Guesses the content type from the extension |
11735
|
|
|
|
|
|
|
sub guessContentType { |
11736
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11737
|
0
|
|
|
|
|
0
|
my $file = shift; |
11738
|
|
|
|
|
|
|
|
11739
|
0
|
0
|
|
|
|
0
|
my $extension = $file =~ /\.([A-Za-z0-9]*)$/ ? lc($1) : ''; |
11740
|
0
|
|
0
|
|
|
0
|
return $o->{mimeTypesByExtension}->{$extension} // 'application/octet-stream'; |
11741
|
|
|
|
|
|
|
} |
11742
|
|
|
|
|
|
|
|
11743
|
|
|
|
|
|
|
# Reads a file |
11744
|
|
|
|
|
|
|
sub readFile { |
11745
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11746
|
0
|
|
|
|
|
0
|
my $file = shift; |
11747
|
|
|
|
|
|
|
|
11748
|
0
|
0
|
|
|
|
0
|
open(my $fh, '<:bytes', $file) || return; |
11749
|
0
|
0
|
|
|
|
0
|
if (! -f $fh) { |
11750
|
0
|
|
|
|
|
0
|
close $fh; |
11751
|
0
|
|
|
|
|
0
|
return; |
11752
|
|
|
|
|
|
|
} |
11753
|
|
|
|
|
|
|
|
11754
|
0
|
|
|
|
|
0
|
local $/ = undef; |
11755
|
0
|
|
|
|
|
0
|
my $bytes = <$fh>; |
11756
|
0
|
|
|
|
|
0
|
close $fh; |
11757
|
0
|
|
|
|
|
0
|
return $bytes; |
11758
|
|
|
|
|
|
|
} |
11759
|
|
|
|
|
|
|
|
11760
|
|
|
|
|
|
|
package CDS::HTTPServer::StoreHandler; |
11761
|
|
|
|
|
|
|
|
11762
|
|
|
|
|
|
|
sub new { |
11763
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
11764
|
0
|
|
|
|
|
0
|
my $root = shift; |
11765
|
0
|
|
|
|
|
0
|
my $store = shift; |
11766
|
0
|
|
|
|
|
0
|
my $checkPutHash = shift; |
11767
|
0
|
|
0
|
|
|
0
|
my $checkSignatures = shift // 1; |
11768
|
|
|
|
|
|
|
|
11769
|
0
|
|
|
|
|
0
|
return bless { |
11770
|
|
|
|
|
|
|
root => $root, |
11771
|
|
|
|
|
|
|
store => $store, |
11772
|
|
|
|
|
|
|
checkPutHash => $checkPutHash, |
11773
|
|
|
|
|
|
|
checkEnvelopeHash => $checkPutHash, |
11774
|
|
|
|
|
|
|
checkSignatures => $checkSignatures, |
11775
|
|
|
|
|
|
|
maximumWatchTimeout => 0, |
11776
|
|
|
|
|
|
|
}; |
11777
|
|
|
|
|
|
|
} |
11778
|
|
|
|
|
|
|
|
11779
|
|
|
|
|
|
|
sub process { |
11780
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11781
|
0
|
|
|
|
|
0
|
my $request = shift; |
11782
|
|
|
|
|
|
|
|
11783
|
0
|
|
0
|
|
|
0
|
my $path = $request->pathAbove($o->{root}) // return; |
11784
|
|
|
|
|
|
|
|
11785
|
|
|
|
|
|
|
# Objects request |
11786
|
0
|
0
|
|
|
|
0
|
if ($request->path =~ /^\/objects\/([0-9a-f]{64})$/) { |
11787
|
0
|
|
|
|
|
0
|
my $hash = CDS::Hash->fromHex($1); |
11788
|
0
|
|
|
|
|
0
|
return $o->objects($request, $hash); |
11789
|
|
|
|
|
|
|
} |
11790
|
|
|
|
|
|
|
|
11791
|
|
|
|
|
|
|
# Box request |
11792
|
0
|
0
|
|
|
|
0
|
if ($request->path =~ /^\/accounts\/([0-9a-f]{64})\/(messages|private|public)$/) { |
11793
|
0
|
|
|
|
|
0
|
my $accountHash = CDS::Hash->fromHex($1); |
11794
|
0
|
|
|
|
|
0
|
my $boxLabel = $2; |
11795
|
0
|
|
|
|
|
0
|
return $o->box($request, $accountHash, $boxLabel); |
11796
|
|
|
|
|
|
|
} |
11797
|
|
|
|
|
|
|
|
11798
|
|
|
|
|
|
|
# Box entry request |
11799
|
0
|
0
|
|
|
|
0
|
if ($request->path =~ /^\/accounts\/([0-9a-f]{64})\/(messages|private|public)\/([0-9a-f]{64})$/) { |
11800
|
0
|
|
|
|
|
0
|
my $accountHash = CDS::Hash->fromHex($1); |
11801
|
0
|
|
|
|
|
0
|
my $boxLabel = $2; |
11802
|
0
|
|
|
|
|
0
|
my $hash = CDS::Hash->fromHex($3); |
11803
|
0
|
|
|
|
|
0
|
return $o->boxEntry($request, $accountHash, $boxLabel, $hash); |
11804
|
|
|
|
|
|
|
} |
11805
|
|
|
|
|
|
|
|
11806
|
|
|
|
|
|
|
# Account request |
11807
|
0
|
0
|
|
|
|
0
|
if ($request->path =~ /^\/accounts\/([0-9a-f]{64})$/) { |
11808
|
0
|
0
|
|
|
|
0
|
return $request->replyOptions if $request->method eq 'OPTIONS'; |
11809
|
0
|
|
|
|
|
0
|
return $request->reply405; |
11810
|
|
|
|
|
|
|
} |
11811
|
|
|
|
|
|
|
|
11812
|
|
|
|
|
|
|
# Accounts request |
11813
|
0
|
0
|
|
|
|
0
|
if ($request->path =~ /^\/accounts$/) { |
11814
|
0
|
|
|
|
|
0
|
return $o->accounts($request); |
11815
|
|
|
|
|
|
|
} |
11816
|
|
|
|
|
|
|
|
11817
|
|
|
|
|
|
|
# Other requests on /objects or /accounts |
11818
|
0
|
0
|
|
|
|
0
|
if ($request->path =~ /^\/(accounts|objects)(\/|$)/) { |
11819
|
0
|
|
|
|
|
0
|
return $request->reply404; |
11820
|
|
|
|
|
|
|
} |
11821
|
|
|
|
|
|
|
|
11822
|
|
|
|
|
|
|
# Nothing for us |
11823
|
0
|
|
|
|
|
0
|
return; |
11824
|
|
|
|
|
|
|
} |
11825
|
|
|
|
|
|
|
|
11826
|
|
|
|
|
|
|
sub objects { |
11827
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11828
|
0
|
|
|
|
|
0
|
my $request = shift; |
11829
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
11830
|
|
|
|
|
|
|
|
11831
|
|
|
|
|
|
|
# Options |
11832
|
0
|
0
|
|
|
|
0
|
if ($request->method eq 'OPTIONS') { |
11833
|
0
|
|
|
|
|
0
|
return $request->replyOptions('HEAD', 'GET', 'PUT', 'POST'); |
11834
|
|
|
|
|
|
|
} |
11835
|
|
|
|
|
|
|
|
11836
|
|
|
|
|
|
|
# Retrieve object |
11837
|
0
|
0
|
0
|
|
|
0
|
if ($request->method eq 'HEAD' || $request->method eq 'GET') { |
11838
|
0
|
|
|
|
|
0
|
my ($object, $error) = $o->{store}->get($hash); |
11839
|
0
|
0
|
|
|
|
0
|
return $request->replyFatalError($error) if defined $error; |
11840
|
0
|
0
|
|
|
|
0
|
return $request->reply404 if ! $object; |
11841
|
|
|
|
|
|
|
# We don't check the SHA256 sum here - this should be done by the client |
11842
|
0
|
|
|
|
|
0
|
return $request->reply200Bytes($object->bytes); |
11843
|
|
|
|
|
|
|
} |
11844
|
|
|
|
|
|
|
|
11845
|
|
|
|
|
|
|
# Put object |
11846
|
0
|
0
|
|
|
|
0
|
if ($request->method eq 'PUT') { |
11847
|
0
|
|
0
|
|
|
0
|
my $bytes = $request->readData // return $request->reply400('No data received.'); |
11848
|
0
|
|
0
|
|
|
0
|
my $object = CDS::Object->fromBytes($bytes) // return $request->reply400('Not a Condensation object.'); |
11849
|
0
|
0
|
0
|
|
|
0
|
return $request->reply400('SHA256 sum does not match hash.') if $o->{checkPutHash} && ! $object->calculateHash->equals($hash); |
11850
|
|
|
|
|
|
|
|
11851
|
0
|
0
|
|
|
|
0
|
if ($o->{checkSignatures}) { |
11852
|
0
|
|
|
|
|
0
|
my $checkSignatureStore = CDS::CheckSignatureStore->new($o->{store}); |
11853
|
0
|
|
|
|
|
0
|
$checkSignatureStore->put($hash, $object); |
11854
|
0
|
0
|
|
|
|
0
|
return $request->reply403 if ! $request->checkSignature($checkSignatureStore); |
11855
|
|
|
|
|
|
|
} |
11856
|
|
|
|
|
|
|
|
11857
|
0
|
|
|
|
|
0
|
my $error = $o->{store}->put($hash, $object); |
11858
|
0
|
0
|
|
|
|
0
|
return $request->replyFatalError($error) if defined $error; |
11859
|
0
|
|
|
|
|
0
|
return $request->reply200; |
11860
|
|
|
|
|
|
|
} |
11861
|
|
|
|
|
|
|
|
11862
|
|
|
|
|
|
|
# Book object |
11863
|
0
|
0
|
|
|
|
0
|
if ($request->method eq 'POST') { |
11864
|
0
|
0
|
0
|
|
|
0
|
return $request->reply403 if $o->{checkSignatures} && ! $request->checkSignature($o->{store}); |
11865
|
0
|
0
|
|
|
|
0
|
return $request->reply400('You cannot send data when booking an object.') if $request->remainingData; |
11866
|
0
|
|
|
|
|
0
|
my ($booked, $error) = $o->{store}->book($hash); |
11867
|
0
|
0
|
|
|
|
0
|
return $request->replyFatalError($error) if defined $error; |
11868
|
0
|
0
|
|
|
|
0
|
return $booked ? $request->reply200 : $request->reply404; |
11869
|
|
|
|
|
|
|
} |
11870
|
|
|
|
|
|
|
|
11871
|
0
|
|
|
|
|
0
|
return $request->reply405; |
11872
|
|
|
|
|
|
|
} |
11873
|
|
|
|
|
|
|
|
11874
|
|
|
|
|
|
|
sub box { |
11875
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11876
|
0
|
|
|
|
|
0
|
my $request = shift; |
11877
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
11878
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
11879
|
|
|
|
|
|
|
|
11880
|
|
|
|
|
|
|
# Options |
11881
|
0
|
0
|
|
|
|
0
|
if ($request->method eq 'OPTIONS') { |
11882
|
0
|
|
|
|
|
0
|
return $request->replyOptions('HEAD', 'GET', 'PUT', 'POST'); |
11883
|
|
|
|
|
|
|
} |
11884
|
|
|
|
|
|
|
|
11885
|
|
|
|
|
|
|
# List box |
11886
|
0
|
0
|
0
|
|
|
0
|
if ($request->method eq 'HEAD' || $request->method eq 'GET') { |
11887
|
0
|
|
0
|
|
|
0
|
my $watch = $request->headers->{'condensation-watch'} // ''; |
11888
|
0
|
0
|
|
|
|
0
|
my $timeout = $watch =~ /^(\d+)\s*ms$/ ? $1 + 0 : 0; |
11889
|
0
|
0
|
|
|
|
0
|
$timeout = $o->{maximumWatchTimeout} if $timeout > $o->{maximumWatchTimeout}; |
11890
|
0
|
|
|
|
|
0
|
my ($hashes, $error) = $o->{store}->list($accountHash, $boxLabel, $timeout); |
11891
|
0
|
0
|
|
|
|
0
|
return $request->replyFatalError($error) if defined $error; |
11892
|
0
|
|
|
|
|
0
|
return $request->reply200Bytes(join('', map { $_->bytes } @$hashes)); |
|
0
|
|
|
|
|
0
|
|
11893
|
|
|
|
|
|
|
} |
11894
|
|
|
|
|
|
|
|
11895
|
0
|
|
|
|
|
0
|
return $request->reply405; |
11896
|
|
|
|
|
|
|
} |
11897
|
|
|
|
|
|
|
|
11898
|
|
|
|
|
|
|
sub boxEntry { |
11899
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11900
|
0
|
|
|
|
|
0
|
my $request = shift; |
11901
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
11902
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
11903
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
11904
|
|
|
|
|
|
|
|
11905
|
|
|
|
|
|
|
# Options |
11906
|
0
|
0
|
|
|
|
0
|
if ($request->method eq 'OPTIONS') { |
11907
|
0
|
|
|
|
|
0
|
return $request->replyOptions('HEAD', 'PUT', 'DELETE'); |
11908
|
|
|
|
|
|
|
} |
11909
|
|
|
|
|
|
|
|
11910
|
|
|
|
|
|
|
# Add |
11911
|
0
|
0
|
|
|
|
0
|
if ($request->method eq 'PUT') { |
11912
|
0
|
0
|
|
|
|
0
|
if ($o->{checkSignatures}) { |
11913
|
0
|
|
|
|
|
0
|
my $actorHash = $request->checkSignature($o->{store}); |
11914
|
0
|
0
|
|
|
|
0
|
return $request->reply403 if ! $actorHash; |
11915
|
0
|
0
|
|
|
|
0
|
return $request->reply403 if ! $o->verifyAddition($actorHash, $accountHash, $boxLabel, $hash); |
11916
|
|
|
|
|
|
|
} |
11917
|
|
|
|
|
|
|
|
11918
|
0
|
|
|
|
|
0
|
my $error = $o->{store}->add($accountHash, $boxLabel, $hash); |
11919
|
0
|
0
|
|
|
|
0
|
return $request->replyFatalError($error) if defined $error; |
11920
|
0
|
|
|
|
|
0
|
return $request->reply200; |
11921
|
|
|
|
|
|
|
} |
11922
|
|
|
|
|
|
|
|
11923
|
|
|
|
|
|
|
# Remove |
11924
|
0
|
0
|
|
|
|
0
|
if ($request->method eq 'DELETE') { |
11925
|
0
|
0
|
|
|
|
0
|
if ($o->{checkSignatures}) { |
11926
|
0
|
|
|
|
|
0
|
my $actorHash = $request->checkSignature($o->{store}); |
11927
|
0
|
0
|
|
|
|
0
|
return $request->reply403 if ! $actorHash; |
11928
|
0
|
0
|
|
|
|
0
|
return $request->reply403 if ! $o->verifyRemoval($actorHash, $accountHash, $boxLabel, $hash); |
11929
|
|
|
|
|
|
|
} |
11930
|
|
|
|
|
|
|
|
11931
|
0
|
|
|
|
|
0
|
my ($booked, $error) = $o->{store}->remove($accountHash, $boxLabel, $hash); |
11932
|
0
|
0
|
|
|
|
0
|
return $request->replyFatalError($error) if defined $error; |
11933
|
0
|
|
|
|
|
0
|
return $request->reply200; |
11934
|
|
|
|
|
|
|
} |
11935
|
|
|
|
|
|
|
|
11936
|
0
|
|
|
|
|
0
|
return $request->reply405; |
11937
|
|
|
|
|
|
|
} |
11938
|
|
|
|
|
|
|
|
11939
|
|
|
|
|
|
|
sub accounts { |
11940
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11941
|
0
|
|
|
|
|
0
|
my $request = shift; |
11942
|
|
|
|
|
|
|
|
11943
|
|
|
|
|
|
|
# Options |
11944
|
0
|
0
|
|
|
|
0
|
if ($request->method eq 'OPTIONS') { |
11945
|
0
|
|
|
|
|
0
|
return $request->replyOptions('POST'); |
11946
|
|
|
|
|
|
|
} |
11947
|
|
|
|
|
|
|
|
11948
|
|
|
|
|
|
|
# Modify boxes |
11949
|
0
|
0
|
|
|
|
0
|
if ($request->method eq 'POST') { |
11950
|
0
|
|
0
|
|
|
0
|
my $bytes = $request->readData // return $request->reply400('No data received.'); |
11951
|
0
|
|
|
|
|
0
|
my $modifications = CDS::StoreModifications->fromBytes($bytes); |
11952
|
0
|
0
|
|
|
|
0
|
return $request->reply400('Invalid modifications.') if ! $modifications; |
11953
|
|
|
|
|
|
|
|
11954
|
0
|
0
|
|
|
|
0
|
if ($o->{checkSignatures}) { |
11955
|
0
|
|
|
|
|
0
|
my $actorHash = $request->checkSignature(CDS::CheckSignatureStore->new($o->{store}, $modifications->objects), $bytes); |
11956
|
0
|
0
|
|
|
|
0
|
return $request->reply403 if ! $actorHash; |
11957
|
0
|
0
|
|
|
|
0
|
return $request->reply403 if ! $o->verifyModifications($actorHash, $modifications); |
11958
|
|
|
|
|
|
|
} |
11959
|
|
|
|
|
|
|
|
11960
|
0
|
|
|
|
|
0
|
my $error = $o->{store}->modify($modifications); |
11961
|
0
|
0
|
|
|
|
0
|
return $request->replyFatalError($error) if defined $error; |
11962
|
0
|
|
|
|
|
0
|
return $request->reply200; |
11963
|
|
|
|
|
|
|
} |
11964
|
|
|
|
|
|
|
|
11965
|
0
|
|
|
|
|
0
|
return $request->reply405; |
11966
|
|
|
|
|
|
|
} |
11967
|
|
|
|
|
|
|
|
11968
|
|
|
|
|
|
|
sub verifyModifications { |
11969
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11970
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
11971
|
0
|
|
|
|
|
0
|
my $modifications = shift; |
11972
|
|
|
|
|
|
|
|
11973
|
0
|
|
|
|
|
0
|
for my $operation (@{$modifications->additions}) { |
|
0
|
|
|
|
|
0
|
|
11974
|
0
|
0
|
|
|
|
0
|
return if ! $o->verifyAddition($actorHash, $operation->{accountHash}, $operation->{boxLabel}, $operation->{hash}); |
11975
|
|
|
|
|
|
|
} |
11976
|
|
|
|
|
|
|
|
11977
|
0
|
|
|
|
|
0
|
for my $operation (@{$modifications->removals}) { |
|
0
|
|
|
|
|
0
|
|
11978
|
0
|
0
|
|
|
|
0
|
return if ! $o->verifyRemoval($actorHash, $operation->{accountHash}, $operation->{boxLabel}, $operation->{hash}); |
11979
|
|
|
|
|
|
|
} |
11980
|
|
|
|
|
|
|
|
11981
|
0
|
|
|
|
|
0
|
return 1; |
11982
|
|
|
|
|
|
|
} |
11983
|
|
|
|
|
|
|
|
11984
|
|
|
|
|
|
|
sub verifyAddition { |
11985
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11986
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
11987
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
11988
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
11989
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
11990
|
|
|
|
|
|
|
|
11991
|
0
|
0
|
|
|
|
0
|
return 1 if $accountHash->equals($actorHash); |
11992
|
0
|
0
|
|
|
|
0
|
return 1 if $boxLabel eq 'messages'; |
11993
|
0
|
|
|
|
|
0
|
return; |
11994
|
|
|
|
|
|
|
} |
11995
|
|
|
|
|
|
|
|
11996
|
|
|
|
|
|
|
sub verifyRemoval { |
11997
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
11998
|
0
|
0
|
0
|
|
|
0
|
my $actorHash = shift; die 'wrong type '.ref($actorHash).' for $actorHash' if defined $actorHash && ref $actorHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
11999
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
12000
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
12001
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
12002
|
|
|
|
|
|
|
|
12003
|
0
|
0
|
|
|
|
0
|
return 1 if $accountHash->equals($actorHash); |
12004
|
|
|
|
|
|
|
|
12005
|
|
|
|
|
|
|
# Get the envelope |
12006
|
0
|
|
|
|
|
0
|
my ($bytes, $error) = $o->{store}->get($hash); |
12007
|
0
|
0
|
|
|
|
0
|
return if defined $error; |
12008
|
0
|
0
|
|
|
|
0
|
return 1 if ! defined $bytes; |
12009
|
0
|
|
0
|
|
|
0
|
my $record = CDS::Record->fromObject(CDS::Object->fromBytes($bytes)) // return; |
12010
|
|
|
|
|
|
|
|
12011
|
|
|
|
|
|
|
# Allow anyone listed under "updated by" |
12012
|
0
|
|
|
|
|
0
|
my $actorHashBytes24 = substr($actorHash->bytes, 0, 24); |
12013
|
0
|
|
|
|
|
0
|
for my $child ($record->child('updated by')->children) { |
12014
|
0
|
|
|
|
|
0
|
my $hashBytes24 = $child->bytes; |
12015
|
0
|
0
|
|
|
|
0
|
next if length $hashBytes24 != 24; |
12016
|
0
|
0
|
|
|
|
0
|
return 1 if $hashBytes24 eq $actorHashBytes24; |
12017
|
|
|
|
|
|
|
} |
12018
|
|
|
|
|
|
|
|
12019
|
0
|
|
|
|
|
0
|
return; |
12020
|
|
|
|
|
|
|
} |
12021
|
|
|
|
|
|
|
|
12022
|
|
|
|
|
|
|
# A Condensation store accessed through HTTP or HTTPS. |
12023
|
|
|
|
|
|
|
package CDS::HTTPStore; |
12024
|
|
|
|
|
|
|
|
12025
|
1
|
|
|
1
|
|
6052
|
use parent -norequire, 'CDS::Store'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
12026
|
|
|
|
|
|
|
|
12027
|
|
|
|
|
|
|
sub forUrl { |
12028
|
1
|
|
|
1
|
|
3
|
my $class = shift; |
12029
|
1
|
|
|
|
|
2
|
my $url = shift; |
12030
|
|
|
|
|
|
|
|
12031
|
1
|
50
|
|
|
|
8
|
$url =~ /^(http|https):\/\// || return; |
12032
|
1
|
|
|
|
|
4
|
return $class->new($url); |
12033
|
|
|
|
|
|
|
} |
12034
|
|
|
|
|
|
|
|
12035
|
|
|
|
|
|
|
sub new { |
12036
|
1
|
|
|
1
|
|
1
|
my $class = shift; |
12037
|
1
|
|
|
|
|
2
|
my $url = shift; |
12038
|
|
|
|
|
|
|
|
12039
|
1
|
|
|
|
|
6
|
return bless {url => $url}; |
12040
|
|
|
|
|
|
|
} |
12041
|
|
|
|
|
|
|
|
12042
|
|
|
|
|
|
|
sub id { |
12043
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
12044
|
0
|
|
|
|
|
0
|
$o->{url} } |
12045
|
|
|
|
|
|
|
|
12046
|
|
|
|
|
|
|
sub get { |
12047
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
12048
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
12049
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
12050
|
|
|
|
|
|
|
|
12051
|
0
|
|
|
|
|
0
|
my $response = $o->request('GET', $o->{url}.'/objects/'.$hash->hex, HTTP::Headers->new); |
12052
|
0
|
0
|
|
|
|
0
|
return if $response->code == 404; |
12053
|
0
|
0
|
|
|
|
0
|
return undef, 'get ==> HTTP '.$response->status_line if ! $response->is_success; |
12054
|
0
|
|
|
|
|
0
|
return CDS::Object->fromBytes($response->decoded_content(charset => 'none')); |
12055
|
|
|
|
|
|
|
} |
12056
|
|
|
|
|
|
|
|
12057
|
|
|
|
|
|
|
sub put { |
12058
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
12059
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
12060
|
0
|
0
|
0
|
|
|
0
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
0
|
|
12061
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
12062
|
|
|
|
|
|
|
|
12063
|
0
|
|
|
|
|
0
|
my $headers = HTTP::Headers->new; |
12064
|
0
|
|
|
|
|
0
|
$headers->header('Content-Type' => 'application/condensation-object'); |
12065
|
0
|
|
|
|
|
0
|
my $response = $o->request('PUT', $o->{url}.'/objects/'.$hash->hex, $headers, $keyPair, $object->bytes); |
12066
|
0
|
0
|
|
|
|
0
|
return if $response->is_success; |
12067
|
0
|
|
|
|
|
0
|
return 'put ==> HTTP '.$response->status_line; |
12068
|
|
|
|
|
|
|
} |
12069
|
|
|
|
|
|
|
|
12070
|
|
|
|
|
|
|
sub book { |
12071
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
12072
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
12073
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
12074
|
|
|
|
|
|
|
|
12075
|
0
|
|
|
|
|
0
|
my $response = $o->request('POST', $o->{url}.'/objects/'.$hash->hex, HTTP::Headers->new, $keyPair); |
12076
|
0
|
0
|
|
|
|
0
|
return if $response->code == 404; |
12077
|
0
|
0
|
|
|
|
0
|
return 1 if $response->is_success; |
12078
|
0
|
|
|
|
|
0
|
return undef, 'book ==> HTTP '.$response->status_line; |
12079
|
|
|
|
|
|
|
} |
12080
|
|
|
|
|
|
|
|
12081
|
|
|
|
|
|
|
sub list { |
12082
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
12083
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
12084
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
12085
|
0
|
|
|
|
|
0
|
my $timeout = shift; |
12086
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
12087
|
|
|
|
|
|
|
|
12088
|
0
|
|
|
|
|
0
|
my $boxUrl = $o->{url}.'/accounts/'.$accountHash->hex.'/'.$boxLabel; |
12089
|
0
|
|
|
|
|
0
|
my $headers = HTTP::Headers->new; |
12090
|
0
|
0
|
|
|
|
0
|
$headers->header('Condensation-Watch' => $timeout.' ms') if $timeout > 0; |
12091
|
0
|
|
|
|
|
0
|
my $response = $o->request('GET', $boxUrl, $headers); |
12092
|
0
|
0
|
|
|
|
0
|
return undef, 'list ==> HTTP '.$response->status_line if ! $response->is_success; |
12093
|
0
|
|
|
|
|
0
|
my $bytes = $response->decoded_content(charset => 'none'); |
12094
|
|
|
|
|
|
|
|
12095
|
0
|
0
|
|
|
|
0
|
if (length($bytes) % 32 != 0) { |
12096
|
0
|
|
|
|
|
0
|
print STDERR 'old procotol', "\n"; |
12097
|
0
|
|
|
|
|
0
|
my $hashes = []; |
12098
|
0
|
|
|
|
|
0
|
for my $line (split /\n/, $bytes) { |
12099
|
0
|
|
0
|
|
|
0
|
push @$hashes, CDS::Hash->fromHex($line) // next; |
12100
|
|
|
|
|
|
|
} |
12101
|
0
|
|
|
|
|
0
|
return $hashes; |
12102
|
|
|
|
|
|
|
} |
12103
|
|
|
|
|
|
|
|
12104
|
0
|
|
|
|
|
0
|
my $countHashes = int(length($bytes) / 32); |
12105
|
0
|
|
|
|
|
0
|
return [map { CDS::Hash->fromBytes(substr($bytes, $_ * 32, 32)) } 0 .. $countHashes - 1]; |
|
0
|
|
|
|
|
0
|
|
12106
|
|
|
|
|
|
|
} |
12107
|
|
|
|
|
|
|
|
12108
|
|
|
|
|
|
|
sub add { |
12109
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
12110
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
12111
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
12112
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
12113
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
12114
|
|
|
|
|
|
|
|
12115
|
0
|
|
|
|
|
0
|
my $headers = HTTP::Headers->new; |
12116
|
0
|
|
|
|
|
0
|
my $response = $o->request('PUT', $o->{url}.'/accounts/'.$accountHash->hex.'/'.$boxLabel.'/'.$hash->hex, $headers, $keyPair); |
12117
|
0
|
0
|
|
|
|
0
|
return if $response->is_success; |
12118
|
0
|
|
|
|
|
0
|
return 'add ==> HTTP '.$response->status_line; |
12119
|
|
|
|
|
|
|
} |
12120
|
|
|
|
|
|
|
|
12121
|
|
|
|
|
|
|
sub remove { |
12122
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
12123
|
0
|
0
|
0
|
|
|
0
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
12124
|
0
|
|
|
|
|
0
|
my $boxLabel = shift; |
12125
|
0
|
0
|
0
|
|
|
0
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
0
|
|
12126
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
12127
|
|
|
|
|
|
|
|
12128
|
0
|
|
|
|
|
0
|
my $headers = HTTP::Headers->new; |
12129
|
0
|
|
|
|
|
0
|
my $response = $o->request('DELETE', $o->{url}.'/accounts/'.$accountHash->hex.'/'.$boxLabel.'/'.$hash->hex, $headers, $keyPair); |
12130
|
0
|
0
|
|
|
|
0
|
return if $response->is_success; |
12131
|
0
|
|
|
|
|
0
|
return 'remove ==> HTTP '.$response->status_line; |
12132
|
|
|
|
|
|
|
} |
12133
|
|
|
|
|
|
|
|
12134
|
|
|
|
|
|
|
sub modify { |
12135
|
0
|
|
|
0
|
|
0
|
my $o = shift; |
12136
|
0
|
|
|
|
|
0
|
my $modifications = shift; |
12137
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
12138
|
|
|
|
|
|
|
|
12139
|
0
|
|
|
|
|
0
|
my $bytes = $modifications->toRecord->toObject->bytes; |
12140
|
0
|
|
|
|
|
0
|
my $headers = HTTP::Headers->new; |
12141
|
0
|
|
|
|
|
0
|
$headers->header('Content-Type' => 'application/condensation-modifications'); |
12142
|
0
|
|
|
|
|
0
|
my $response = $o->request('POST', $o->{url}.'/accounts', $headers, $keyPair, $bytes, 1); |
12143
|
0
|
0
|
|
|
|
0
|
return if $response->is_success; |
12144
|
0
|
|
|
|
|
0
|
return 'modify ==> HTTP '.$response->status_line; |
12145
|
|
|
|
|
|
|
} |
12146
|
|
|
|
|
|
|
|
12147
|
|
|
|
|
|
|
# Executes a HTTP request. |
12148
|
|
|
|
|
|
|
sub request { |
12149
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
12150
|
0
|
|
|
|
|
0
|
my $method = shift; |
12151
|
0
|
|
|
|
|
0
|
my $url = shift; |
12152
|
0
|
|
|
|
|
0
|
my $headers = shift; |
12153
|
0
|
0
|
0
|
|
|
0
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
0
|
|
12154
|
0
|
|
|
|
|
0
|
my $data = shift; |
12155
|
0
|
|
|
|
|
0
|
my $signData = shift; |
12156
|
|
|
|
|
|
|
# private |
12157
|
0
|
|
|
|
|
0
|
$headers->date(time); |
12158
|
0
|
|
|
|
|
0
|
$headers->header('User-Agent' => CDS->version); |
12159
|
|
|
|
|
|
|
|
12160
|
0
|
0
|
|
|
|
0
|
if ($keyPair) { |
12161
|
0
|
0
|
|
|
|
0
|
my $hostAndPath = $url =~ /^https?:\/\/(.*)$/ ? $1 : $url; |
12162
|
0
|
|
|
|
|
0
|
my $date = CDS::ISODate->millisecondString; |
12163
|
0
|
|
|
|
|
0
|
my $bytesToSign = $date."\0".uc($method)."\0".$hostAndPath; |
12164
|
0
|
0
|
|
|
|
0
|
$bytesToSign .= "\0".$data if $signData; |
12165
|
0
|
|
|
|
|
0
|
my $hashBytesToSign = Digest::SHA::sha256($bytesToSign); |
12166
|
0
|
|
|
|
|
0
|
my $signature = $keyPair->sign($hashBytesToSign); |
12167
|
0
|
|
|
|
|
0
|
$headers->header('Condensation-Date' => $date); |
12168
|
0
|
|
|
|
|
0
|
$headers->header('Condensation-Actor' => $keyPair->publicKey->hash->hex); |
12169
|
0
|
|
|
|
|
0
|
$headers->header('Condensation-Signature' => unpack('H*', $signature)); |
12170
|
|
|
|
|
|
|
} |
12171
|
|
|
|
|
|
|
|
12172
|
0
|
|
|
|
|
0
|
return LWP::UserAgent->new->request(HTTP::Request->new($method, $url, $headers, $data)); |
12173
|
|
|
|
|
|
|
} |
12174
|
|
|
|
|
|
|
|
12175
|
|
|
|
|
|
|
# Models a hash, and offers binary and hexadecimal representation. |
12176
|
|
|
|
|
|
|
package CDS::Hash; |
12177
|
|
|
|
|
|
|
|
12178
|
|
|
|
|
|
|
sub fromBytes { |
12179
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
12180
|
0
|
|
0
|
|
|
0
|
my $hashBytes = shift // return; |
12181
|
|
|
|
|
|
|
|
12182
|
0
|
0
|
|
|
|
0
|
return if length $hashBytes != 32; |
12183
|
0
|
|
|
|
|
0
|
return bless \$hashBytes; |
12184
|
|
|
|
|
|
|
} |
12185
|
|
|
|
|
|
|
|
12186
|
|
|
|
|
|
|
sub fromHex { |
12187
|
4
|
|
|
4
|
|
85
|
my $class = shift; |
12188
|
4
|
|
50
|
|
|
12
|
my $hashHex = shift // return; |
12189
|
|
|
|
|
|
|
|
12190
|
4
|
100
|
|
|
|
28
|
$hashHex =~ /^\s*([a-fA-F0-9]{64,64})\s*$/ || return; |
12191
|
2
|
|
|
|
|
16
|
my $hashBytes = pack('H*', $hashHex); |
12192
|
2
|
|
|
|
|
10
|
return bless \$hashBytes; |
12193
|
|
|
|
|
|
|
} |
12194
|
|
|
|
|
|
|
|
12195
|
|
|
|
|
|
|
sub calculateFor { |
12196
|
0
|
|
|
0
|
|
|
my $class = shift; |
12197
|
0
|
|
|
|
|
|
my $bytes = shift; |
12198
|
|
|
|
|
|
|
|
12199
|
|
|
|
|
|
|
# The Perl built-in SHA256 implementation is a tad faster than our SHA256 implementation. |
12200
|
|
|
|
|
|
|
#return $class->fromBytes(CDS::C::sha256($bytes)); |
12201
|
0
|
|
|
|
|
|
return $class->fromBytes(Digest::SHA::sha256($bytes)); |
12202
|
|
|
|
|
|
|
} |
12203
|
|
|
|
|
|
|
|
12204
|
|
|
|
|
|
|
sub hex { |
12205
|
0
|
|
|
0
|
|
|
my $o = shift; |
12206
|
|
|
|
|
|
|
|
12207
|
0
|
|
|
|
|
|
return unpack('H*', $$o); |
12208
|
|
|
|
|
|
|
} |
12209
|
|
|
|
|
|
|
|
12210
|
|
|
|
|
|
|
sub shortHex { |
12211
|
0
|
|
|
0
|
|
|
my $o = shift; |
12212
|
|
|
|
|
|
|
|
12213
|
0
|
|
|
|
|
|
return unpack('H*', substr($$o, 0, 8)) . '…'; |
12214
|
|
|
|
|
|
|
} |
12215
|
|
|
|
|
|
|
|
12216
|
|
|
|
|
|
|
sub bytes { |
12217
|
0
|
|
|
0
|
|
|
my $o = shift; |
12218
|
0
|
|
|
|
|
|
$$o } |
12219
|
|
|
|
|
|
|
|
12220
|
|
|
|
|
|
|
sub equals { |
12221
|
0
|
|
|
0
|
|
|
my $this = shift; |
12222
|
0
|
|
|
|
|
|
my $that = shift; |
12223
|
|
|
|
|
|
|
|
12224
|
0
|
0
|
0
|
|
|
|
return 1 if ! defined $this && ! defined $that; |
12225
|
0
|
0
|
0
|
|
|
|
return if ! defined $this || ! defined $that; |
12226
|
0
|
|
|
|
|
|
return $$this eq $$that; |
12227
|
|
|
|
|
|
|
} |
12228
|
|
|
|
|
|
|
|
12229
|
|
|
|
|
|
|
sub cmp { |
12230
|
0
|
|
|
0
|
|
|
my $this = shift; |
12231
|
0
|
|
|
|
|
|
my $that = shift; |
12232
|
0
|
|
|
|
|
|
$$this cmp $$that } |
12233
|
|
|
|
|
|
|
|
12234
|
|
|
|
|
|
|
# A hash with an AES key. |
12235
|
|
|
|
|
|
|
package CDS::HashAndKey; |
12236
|
|
|
|
|
|
|
|
12237
|
|
|
|
|
|
|
sub new { |
12238
|
0
|
|
|
0
|
|
|
my $class = shift; |
12239
|
0
|
0
|
0
|
|
|
|
my $hash = shift // return; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
0
|
|
|
|
|
12240
|
0
|
|
0
|
|
|
|
my $key = shift // return; |
12241
|
|
|
|
|
|
|
|
12242
|
0
|
|
|
|
|
|
return bless { |
12243
|
|
|
|
|
|
|
hash => $hash, |
12244
|
|
|
|
|
|
|
key => $key, |
12245
|
|
|
|
|
|
|
}; |
12246
|
|
|
|
|
|
|
} |
12247
|
|
|
|
|
|
|
|
12248
|
0
|
|
|
0
|
|
|
sub hash { shift->{hash} } |
12249
|
0
|
|
|
0
|
|
|
sub key { shift->{key} } |
12250
|
|
|
|
|
|
|
|
12251
|
|
|
|
|
|
|
package CDS::ISODate; |
12252
|
|
|
|
|
|
|
|
12253
|
|
|
|
|
|
|
# Parses a date accepting various ISO variants, and calculates the timestamp using Time::Local |
12254
|
|
|
|
|
|
|
sub parse { |
12255
|
0
|
|
|
0
|
|
|
my $class = shift; |
12256
|
0
|
|
0
|
|
|
|
my $dateString = shift // return; |
12257
|
|
|
|
|
|
|
|
12258
|
0
|
0
|
|
|
|
|
if ($dateString =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
12259
|
0
|
|
|
|
|
|
return (timegm(0, 0, 0, $3, $2 - 1, $1 - 1900) + 86400 - 30) * 1000; |
12260
|
|
|
|
|
|
|
} elsif ($dateString =~ /^(\d\d\d\d)-(\d\d)-(\d\d)(T|\s+)(\d\d):(\d\d):(\d\d|\d\d\.\d*)$/) { |
12261
|
0
|
|
|
|
|
|
return (timelocal(0, $6, $5, $3, $2 - 1, $1 - 1900) + $7) * 1000; |
12262
|
|
|
|
|
|
|
} elsif ($dateString =~ /^(\d\d\d\d)-(\d\d)-(\d\d)(T|\s+)(\d\d):(\d\d):(\d\d|\d\d\.\d*)Z$/) { |
12263
|
0
|
|
|
|
|
|
return (timegm(0, $6, $5, $3, $2 - 1, $1 - 1900) + $7) * 1000; |
12264
|
|
|
|
|
|
|
} elsif ($dateString =~ /^(\d\d\d\d)-(\d\d)-(\d\d)(T|\s+)(\d\d):(\d\d):(\d\d|\d\d\.\d*)+(\d\d):(\d\d)$/) { |
12265
|
0
|
|
|
|
|
|
return (timegm(0, $6, $5, $3, $2 - 1, $1 - 1900) + $7 - $8 * 3600 - $9 * 60) * 1000; |
12266
|
|
|
|
|
|
|
} elsif ($dateString =~ /^(\d\d\d\d)-(\d\d)-(\d\d)(T|\s+)(\d\d):(\d\d):(\d\d|\d\d\.\d*)-(\d\d):(\d\d)$/) { |
12267
|
0
|
|
|
|
|
|
return (timegm(0, $6, $5, $3, $2 - 1, $1 - 1900) + $7 + $8 * 3600 + $9 * 60) * 1000; |
12268
|
|
|
|
|
|
|
} elsif ($dateString =~ /^\s*(\d+)\s*$/) { |
12269
|
0
|
|
|
|
|
|
return $1; |
12270
|
|
|
|
|
|
|
} else { |
12271
|
0
|
|
|
|
|
|
return; |
12272
|
|
|
|
|
|
|
} |
12273
|
|
|
|
|
|
|
} |
12274
|
|
|
|
|
|
|
|
12275
|
|
|
|
|
|
|
# Returns a properly formatted string with a precision of 1 day (i.e., the "date" only) |
12276
|
|
|
|
|
|
|
sub dayString { |
12277
|
0
|
|
|
0
|
|
|
my $class = shift; |
12278
|
0
|
|
0
|
|
|
|
my $time = shift // 1000 * time; |
12279
|
|
|
|
|
|
|
|
12280
|
0
|
|
|
|
|
|
my @t = gmtime($time / 1000); |
12281
|
0
|
|
|
|
|
|
return sprintf('%04d-%02d-%02d', $t[5] + 1900, $t[4] + 1, $t[3]); |
12282
|
|
|
|
|
|
|
} |
12283
|
|
|
|
|
|
|
|
12284
|
|
|
|
|
|
|
# Returns a properly formatted string with a precision of 1 second (i.e., "time of day" and "date") using UTC |
12285
|
|
|
|
|
|
|
sub secondString { |
12286
|
0
|
|
|
0
|
|
|
my $class = shift; |
12287
|
0
|
|
0
|
|
|
|
my $time = shift // 1000 * time; |
12288
|
|
|
|
|
|
|
|
12289
|
0
|
|
|
|
|
|
my @t = gmtime($time / 1000); |
12290
|
0
|
|
|
|
|
|
return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ', $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]); |
12291
|
|
|
|
|
|
|
} |
12292
|
|
|
|
|
|
|
|
12293
|
|
|
|
|
|
|
# Returns a properly formatted string with a precision of 1 second (i.e., "time of day" and "date") using UTC |
12294
|
|
|
|
|
|
|
sub millisecondString { |
12295
|
0
|
|
|
0
|
|
|
my $class = shift; |
12296
|
0
|
|
0
|
|
|
|
my $time = shift // 1000 * time; |
12297
|
|
|
|
|
|
|
|
12298
|
0
|
|
|
|
|
|
my @t = gmtime($time / 1000); |
12299
|
0
|
|
|
|
|
|
return sprintf('%04d-%02d-%02dT%02d:%02d:%02d.%03dZ', $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0], int($time) % 1000); |
12300
|
|
|
|
|
|
|
} |
12301
|
|
|
|
|
|
|
|
12302
|
|
|
|
|
|
|
# Returns a properly formatted string with a precision of 1 second (i.e., "time of day" and "date") using local time |
12303
|
|
|
|
|
|
|
sub localSecondString { |
12304
|
0
|
|
|
0
|
|
|
my $class = shift; |
12305
|
0
|
|
0
|
|
|
|
my $time = shift // 1000 * time; |
12306
|
|
|
|
|
|
|
|
12307
|
0
|
|
|
|
|
|
my @t = localtime($time / 1000); |
12308
|
0
|
|
|
|
|
|
return sprintf('%04d-%02d-%02dT%02d:%02d:%02d', $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]); |
12309
|
|
|
|
|
|
|
} |
12310
|
|
|
|
|
|
|
|
12311
|
|
|
|
|
|
|
package CDS::InMemoryStore; |
12312
|
|
|
|
|
|
|
|
12313
|
|
|
|
|
|
|
sub create { |
12314
|
0
|
|
|
0
|
|
|
my $class = shift; |
12315
|
|
|
|
|
|
|
|
12316
|
0
|
|
|
|
|
|
return CDS::InMemoryStore->new('inMemoryStore:'.unpack('H*', CDS->randomBytes(16))); |
12317
|
|
|
|
|
|
|
} |
12318
|
|
|
|
|
|
|
|
12319
|
|
|
|
|
|
|
sub new { |
12320
|
0
|
|
|
0
|
|
|
my $o = shift; |
12321
|
0
|
|
|
|
|
|
my $id = shift; |
12322
|
|
|
|
|
|
|
|
12323
|
0
|
|
|
|
|
|
return bless { |
12324
|
|
|
|
|
|
|
id => $id, |
12325
|
|
|
|
|
|
|
objects => {}, |
12326
|
|
|
|
|
|
|
accounts => {}, |
12327
|
|
|
|
|
|
|
}; |
12328
|
|
|
|
|
|
|
} |
12329
|
|
|
|
|
|
|
|
12330
|
0
|
|
|
0
|
|
|
sub id { shift->{id} } |
12331
|
|
|
|
|
|
|
|
12332
|
|
|
|
|
|
|
sub accountForWriting { |
12333
|
0
|
|
|
0
|
|
|
my $o = shift; |
12334
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12335
|
|
|
|
|
|
|
|
12336
|
0
|
|
|
|
|
|
my $account = $o->{accounts}->{$hash->bytes}; |
12337
|
0
|
0
|
|
|
|
|
return $account if $account; |
12338
|
0
|
|
|
|
|
|
return $o->{accounts}->{$hash->bytes} = {messages => {}, private => {}, public => {}}; |
12339
|
|
|
|
|
|
|
} |
12340
|
|
|
|
|
|
|
|
12341
|
|
|
|
|
|
|
# *** Store interface |
12342
|
|
|
|
|
|
|
|
12343
|
|
|
|
|
|
|
sub get { |
12344
|
0
|
|
|
0
|
|
|
my $o = shift; |
12345
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12346
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12347
|
|
|
|
|
|
|
|
12348
|
0
|
|
0
|
|
|
|
my $entry = $o->{objects}->{$hash->bytes} // return; |
12349
|
0
|
|
|
|
|
|
return $entry->{object}; |
12350
|
|
|
|
|
|
|
} |
12351
|
|
|
|
|
|
|
|
12352
|
|
|
|
|
|
|
sub book { |
12353
|
0
|
|
|
0
|
|
|
my $o = shift; |
12354
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12355
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12356
|
|
|
|
|
|
|
|
12357
|
0
|
|
0
|
|
|
|
my $entry = $o->{objects}->{$hash->bytes} // return; |
12358
|
0
|
|
|
|
|
|
$entry->{booked} = CDS->now; |
12359
|
0
|
|
|
|
|
|
return 1; |
12360
|
|
|
|
|
|
|
} |
12361
|
|
|
|
|
|
|
|
12362
|
|
|
|
|
|
|
sub put { |
12363
|
0
|
|
|
0
|
|
|
my $o = shift; |
12364
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12365
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
12366
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12367
|
|
|
|
|
|
|
|
12368
|
0
|
|
|
|
|
|
$o->{objects}->{$hash->bytes} = {object => $object, booked => CDS->now}; |
12369
|
0
|
|
|
|
|
|
return; |
12370
|
|
|
|
|
|
|
} |
12371
|
|
|
|
|
|
|
|
12372
|
|
|
|
|
|
|
sub list { |
12373
|
0
|
|
|
0
|
|
|
my $o = shift; |
12374
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12375
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
12376
|
0
|
|
|
|
|
|
my $timeout = shift; |
12377
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12378
|
|
|
|
|
|
|
|
12379
|
0
|
|
0
|
|
|
|
my $account = $o->{accounts}->{$accountHash->bytes} // return []; |
12380
|
0
|
|
0
|
|
|
|
my $box = $account->{$boxLabel} // return undef, 'Invalid box label.'; |
12381
|
0
|
|
|
|
|
|
return values %$box; |
12382
|
|
|
|
|
|
|
} |
12383
|
|
|
|
|
|
|
|
12384
|
|
|
|
|
|
|
sub add { |
12385
|
0
|
|
|
0
|
|
|
my $o = shift; |
12386
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12387
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
12388
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12389
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12390
|
|
|
|
|
|
|
|
12391
|
0
|
|
0
|
|
|
|
my $box = $o->accountForWriting($accountHash)->{$boxLabel} // return; |
12392
|
0
|
|
|
|
|
|
$box->{$hash->bytes} = $hash; |
12393
|
|
|
|
|
|
|
} |
12394
|
|
|
|
|
|
|
|
12395
|
|
|
|
|
|
|
sub remove { |
12396
|
0
|
|
|
0
|
|
|
my $o = shift; |
12397
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12398
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
12399
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12400
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12401
|
|
|
|
|
|
|
|
12402
|
0
|
|
0
|
|
|
|
my $box = $o->accountForWriting($accountHash)->{$boxLabel} // return; |
12403
|
0
|
|
|
|
|
|
delete $box->{$hash->bytes}; |
12404
|
|
|
|
|
|
|
} |
12405
|
|
|
|
|
|
|
|
12406
|
|
|
|
|
|
|
sub modify { |
12407
|
0
|
|
|
0
|
|
|
my $o = shift; |
12408
|
0
|
|
|
|
|
|
my $modifications = shift; |
12409
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12410
|
|
|
|
|
|
|
|
12411
|
0
|
|
|
|
|
|
return $modifications->executeIndividually($o, $keyPair); |
12412
|
|
|
|
|
|
|
} |
12413
|
|
|
|
|
|
|
|
12414
|
|
|
|
|
|
|
# Garbage collection |
12415
|
|
|
|
|
|
|
|
12416
|
|
|
|
|
|
|
sub collectGarbage { |
12417
|
0
|
|
|
0
|
|
|
my $o = shift; |
12418
|
0
|
|
|
|
|
|
my $graceTime = shift; |
12419
|
|
|
|
|
|
|
|
12420
|
|
|
|
|
|
|
# Mark all objects as not used |
12421
|
0
|
|
|
|
|
|
for my $entry (values %{$o->{objects}}) { |
|
0
|
|
|
|
|
|
|
12422
|
0
|
|
|
|
|
|
$entry->{inUse} = 0; |
12423
|
|
|
|
|
|
|
} |
12424
|
|
|
|
|
|
|
|
12425
|
|
|
|
|
|
|
# Mark all objects newer than the grace time |
12426
|
0
|
|
|
|
|
|
for my $entry (values %{$o->{objects}}) { |
|
0
|
|
|
|
|
|
|
12427
|
0
|
0
|
|
|
|
|
$o->markEntry($entry) if $entry->{booked} > $graceTime; |
12428
|
|
|
|
|
|
|
} |
12429
|
|
|
|
|
|
|
|
12430
|
|
|
|
|
|
|
# Mark all objects referenced from a box |
12431
|
0
|
|
|
|
|
|
for my $account (values %{$o->{accounts}}) { |
|
0
|
|
|
|
|
|
|
12432
|
0
|
|
|
|
|
|
for my $hash (values %{$account->{messages}}) { $o->markHash($hash); } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
12433
|
0
|
|
|
|
|
|
for my $hash (values %{$account->{private}}) { $o->markHash($hash); } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
12434
|
0
|
|
|
|
|
|
for my $hash (values %{$account->{public}}) { $o->markHash($hash); } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
12435
|
|
|
|
|
|
|
} |
12436
|
|
|
|
|
|
|
|
12437
|
|
|
|
|
|
|
# Remove empty accounts |
12438
|
0
|
|
|
|
|
|
while (my ($key, $account) = each %{$o->{accounts}}) { |
|
0
|
|
|
|
|
|
|
12439
|
0
|
0
|
|
|
|
|
next if scalar keys %{$account->{messages}}; |
|
0
|
|
|
|
|
|
|
12440
|
0
|
0
|
|
|
|
|
next if scalar keys %{$account->{private}}; |
|
0
|
|
|
|
|
|
|
12441
|
0
|
0
|
|
|
|
|
next if scalar keys %{$account->{public}}; |
|
0
|
|
|
|
|
|
|
12442
|
0
|
|
|
|
|
|
delete $o->{accounts}->{$key}; |
12443
|
|
|
|
|
|
|
} |
12444
|
|
|
|
|
|
|
|
12445
|
|
|
|
|
|
|
# Remove obsolete objects |
12446
|
0
|
|
|
|
|
|
while (my ($key, $entry) = each %{$o->{objects}}) { |
|
0
|
|
|
|
|
|
|
12447
|
0
|
0
|
|
|
|
|
next if $entry->{inUse}; |
12448
|
0
|
|
|
|
|
|
delete $o->{objects}->{$key}; |
12449
|
|
|
|
|
|
|
} |
12450
|
|
|
|
|
|
|
} |
12451
|
|
|
|
|
|
|
|
12452
|
|
|
|
|
|
|
sub markHash { |
12453
|
0
|
|
|
0
|
|
|
my $o = shift; |
12454
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12455
|
|
|
|
|
|
|
# private |
12456
|
0
|
|
0
|
|
|
|
my $child = $o->{objects}->{$hash->bytes} // return; |
12457
|
0
|
|
|
|
|
|
$o->mark($child); |
12458
|
|
|
|
|
|
|
} |
12459
|
|
|
|
|
|
|
|
12460
|
|
|
|
|
|
|
sub markEntry { |
12461
|
0
|
|
|
0
|
|
|
my $o = shift; |
12462
|
0
|
|
|
|
|
|
my $entry = shift; |
12463
|
|
|
|
|
|
|
# private |
12464
|
0
|
0
|
|
|
|
|
return if $entry->{inUse}; |
12465
|
0
|
|
|
|
|
|
$entry->{inUse} = 1; |
12466
|
|
|
|
|
|
|
|
12467
|
|
|
|
|
|
|
# Mark all children |
12468
|
0
|
|
|
|
|
|
for my $hash ($entry->{object}->hashes) { |
12469
|
0
|
|
|
|
|
|
$o->markHash($hash); |
12470
|
|
|
|
|
|
|
} |
12471
|
|
|
|
|
|
|
} |
12472
|
|
|
|
|
|
|
|
12473
|
|
|
|
|
|
|
package CDS::KeyPair; |
12474
|
|
|
|
|
|
|
|
12475
|
|
|
|
|
|
|
sub transfer { |
12476
|
0
|
|
|
0
|
|
|
my $o = shift; |
12477
|
0
|
|
|
|
|
|
my $hashes = shift; |
12478
|
0
|
|
|
|
|
|
my $sourceStore = shift; |
12479
|
0
|
|
|
|
|
|
my $destinationStore = shift; |
12480
|
|
|
|
|
|
|
|
12481
|
0
|
|
|
|
|
|
for my $hash (@$hashes) { |
12482
|
0
|
|
|
|
|
|
my ($missing, $store, $storeError) = $o->recursiveTransfer($hash, $sourceStore, $destinationStore, {}); |
12483
|
0
|
0
|
|
|
|
|
return $missing if $missing; |
12484
|
0
|
0
|
|
|
|
|
return undef, $store, $storeError if defined $storeError; |
12485
|
|
|
|
|
|
|
} |
12486
|
|
|
|
|
|
|
|
12487
|
0
|
|
|
|
|
|
return; |
12488
|
|
|
|
|
|
|
} |
12489
|
|
|
|
|
|
|
|
12490
|
|
|
|
|
|
|
sub recursiveTransfer { |
12491
|
0
|
|
|
0
|
|
|
my $o = shift; |
12492
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12493
|
0
|
|
|
|
|
|
my $sourceStore = shift; |
12494
|
0
|
|
|
|
|
|
my $destinationStore = shift; |
12495
|
0
|
|
|
|
|
|
my $done = shift; |
12496
|
|
|
|
|
|
|
# private |
12497
|
0
|
0
|
|
|
|
|
return if $done->{$hash->bytes}; |
12498
|
0
|
|
|
|
|
|
$done->{$hash->bytes} = 1; |
12499
|
|
|
|
|
|
|
|
12500
|
|
|
|
|
|
|
# Book |
12501
|
0
|
|
|
|
|
|
my ($booked, $bookError) = $destinationStore->book($hash, $o); |
12502
|
0
|
0
|
|
|
|
|
return undef, $destinationStore, $bookError if defined $bookError; |
12503
|
0
|
0
|
|
|
|
|
return if $booked; |
12504
|
|
|
|
|
|
|
|
12505
|
|
|
|
|
|
|
# Get |
12506
|
0
|
|
|
|
|
|
my ($object, $getError) = $sourceStore->get($hash, $o); |
12507
|
0
|
0
|
|
|
|
|
return undef, $sourceStore, $getError if defined $getError; |
12508
|
0
|
0
|
|
|
|
|
return CDS::MissingObject->new($hash, $sourceStore) if ! defined $object; |
12509
|
|
|
|
|
|
|
|
12510
|
|
|
|
|
|
|
# Process children |
12511
|
0
|
|
|
|
|
|
for my $child ($object->hashes) { |
12512
|
0
|
|
|
|
|
|
my ($missing, $store, $error) = $o->recursiveTransfer($child, $sourceStore, $destinationStore, $done); |
12513
|
0
|
0
|
|
|
|
|
return undef, $store, $error if defined $error; |
12514
|
0
|
0
|
|
|
|
|
if (defined $missing) { |
12515
|
0
|
|
|
|
|
|
push @{$missing->{path}}, $child; |
|
0
|
|
|
|
|
|
|
12516
|
0
|
|
|
|
|
|
return $missing; |
12517
|
|
|
|
|
|
|
} |
12518
|
|
|
|
|
|
|
} |
12519
|
|
|
|
|
|
|
|
12520
|
|
|
|
|
|
|
# Put |
12521
|
0
|
|
|
|
|
|
my $putError = $destinationStore->put($hash, $object, $o); |
12522
|
0
|
0
|
|
|
|
|
return undef, $destinationStore, $putError if defined $putError; |
12523
|
0
|
|
|
|
|
|
return; |
12524
|
|
|
|
|
|
|
} |
12525
|
|
|
|
|
|
|
|
12526
|
|
|
|
|
|
|
sub createPublicEnvelope { |
12527
|
0
|
|
|
0
|
|
|
my $o = shift; |
12528
|
0
|
0
|
0
|
|
|
|
my $contentHash = shift; die 'wrong type '.ref($contentHash).' for $contentHash' if defined $contentHash && ref $contentHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12529
|
|
|
|
|
|
|
|
12530
|
0
|
|
|
|
|
|
my $envelope = CDS::Record->new; |
12531
|
0
|
|
|
|
|
|
$envelope->add('content')->addHash($contentHash); |
12532
|
0
|
|
|
|
|
|
$envelope->add('signature')->add($o->signHash($contentHash)); |
12533
|
0
|
|
|
|
|
|
return $envelope; |
12534
|
|
|
|
|
|
|
} |
12535
|
|
|
|
|
|
|
|
12536
|
|
|
|
|
|
|
sub createPrivateEnvelope { |
12537
|
0
|
|
|
0
|
|
|
my $o = shift; |
12538
|
0
|
|
|
|
|
|
my $contentHashAndKey = shift; |
12539
|
0
|
|
|
|
|
|
my $recipientPublicKeys = shift; |
12540
|
|
|
|
|
|
|
|
12541
|
0
|
|
|
|
|
|
my $envelope = CDS::Record->new; |
12542
|
0
|
|
|
|
|
|
$envelope->add('content')->addHash($contentHashAndKey->hash); |
12543
|
0
|
|
|
|
|
|
$o->addRecipientsToEnvelope($envelope, $contentHashAndKey->key, $recipientPublicKeys); |
12544
|
0
|
|
|
|
|
|
$envelope->add('signature')->add($o->signHash($contentHashAndKey->hash)); |
12545
|
0
|
|
|
|
|
|
return $envelope; |
12546
|
|
|
|
|
|
|
} |
12547
|
|
|
|
|
|
|
|
12548
|
|
|
|
|
|
|
sub createMessageEnvelope { |
12549
|
0
|
|
|
0
|
|
|
my $o = shift; |
12550
|
0
|
|
|
|
|
|
my $storeUrl = shift; |
12551
|
0
|
0
|
0
|
|
|
|
my $messageRecord = shift; die 'wrong type '.ref($messageRecord).' for $messageRecord' if defined $messageRecord && ref $messageRecord ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
12552
|
0
|
|
|
|
|
|
my $recipientPublicKeys = shift; |
12553
|
0
|
|
|
|
|
|
my $expires = shift; |
12554
|
|
|
|
|
|
|
|
12555
|
0
|
|
|
|
|
|
my $contentRecord = CDS::Record->new; |
12556
|
0
|
|
|
|
|
|
$contentRecord->add('store')->addText($storeUrl); |
12557
|
0
|
|
|
|
|
|
$contentRecord->add('sender')->addHash($o->publicKey->hash); |
12558
|
0
|
|
|
|
|
|
$contentRecord->addRecord($messageRecord->children); |
12559
|
0
|
|
|
|
|
|
my $contentObject = $contentRecord->toObject; |
12560
|
0
|
|
|
|
|
|
my $contentKey = CDS->randomKey; |
12561
|
0
|
|
|
|
|
|
my $encryptedContent = CDS::C::aesCrypt($contentObject->bytes, $contentKey, CDS->zeroCTR); |
12562
|
|
|
|
|
|
|
#my $hashToSign = $contentObject->calculateHash; # prior to 2020-05-05 |
12563
|
0
|
|
|
|
|
|
my $hashToSign = CDS::Hash->calculateFor($encryptedContent); |
12564
|
|
|
|
|
|
|
|
12565
|
0
|
|
|
|
|
|
my $envelope = CDS::Record->new; |
12566
|
0
|
|
|
|
|
|
$envelope->add('content')->add($encryptedContent); |
12567
|
0
|
|
|
|
|
|
$o->addRecipientsToEnvelope($envelope, $contentKey, $recipientPublicKeys); |
12568
|
0
|
|
|
|
|
|
$envelope->add('updated by')->add(substr($o->publicKey->hash->bytes, 0, 24)); |
12569
|
0
|
0
|
|
|
|
|
$envelope->add('expires')->addInteger($expires) if defined $expires; |
12570
|
0
|
|
|
|
|
|
$envelope->add('signature')->add($o->signHash($hashToSign)); |
12571
|
0
|
|
|
|
|
|
return $envelope; |
12572
|
|
|
|
|
|
|
} |
12573
|
|
|
|
|
|
|
|
12574
|
|
|
|
|
|
|
sub addRecipientsToEnvelope { |
12575
|
0
|
|
|
0
|
|
|
my $o = shift; |
12576
|
0
|
0
|
0
|
|
|
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
12577
|
0
|
|
|
|
|
|
my $key = shift; |
12578
|
0
|
|
|
|
|
|
my $recipientPublicKeys = shift; |
12579
|
|
|
|
|
|
|
# private |
12580
|
0
|
|
|
|
|
|
my $encryptedKeyRecord = $envelope->add('encrypted for'); |
12581
|
0
|
|
|
|
|
|
my $myHashBytes24 = substr($o->{publicKey}->hash->bytes, 0, 24); |
12582
|
0
|
|
|
|
|
|
$encryptedKeyRecord->add($myHashBytes24)->add($o->{publicKey}->encrypt($key)); |
12583
|
0
|
|
|
|
|
|
for my $publicKey (@$recipientPublicKeys) { |
12584
|
0
|
0
|
|
|
|
|
next if $publicKey->hash->equals($o->{publicKey}->hash); |
12585
|
0
|
|
|
|
|
|
my $hashBytes24 = substr($publicKey->hash->bytes, 0, 24); |
12586
|
0
|
|
|
|
|
|
$encryptedKeyRecord->add($hashBytes24)->add($publicKey->encrypt($key)); |
12587
|
|
|
|
|
|
|
} |
12588
|
|
|
|
|
|
|
} |
12589
|
|
|
|
|
|
|
|
12590
|
|
|
|
|
|
|
sub generate { |
12591
|
0
|
|
|
0
|
|
|
my $class = shift; |
12592
|
|
|
|
|
|
|
|
12593
|
|
|
|
|
|
|
# Generate a new private key |
12594
|
0
|
|
|
|
|
|
my $rsaPrivateKey = CDS::C::privateKeyGenerate(); |
12595
|
|
|
|
|
|
|
|
12596
|
|
|
|
|
|
|
# Serialize the public key |
12597
|
0
|
|
|
|
|
|
my $rsaPublicKey = CDS::C::publicKeyFromPrivateKey($rsaPrivateKey); |
12598
|
0
|
|
|
|
|
|
my $record = CDS::Record->new; |
12599
|
0
|
|
|
|
|
|
$record->add('e')->add(CDS::C::publicKeyE($rsaPublicKey)); |
12600
|
0
|
|
|
|
|
|
$record->add('n')->add(CDS::C::publicKeyN($rsaPublicKey)); |
12601
|
0
|
|
|
|
|
|
my $publicKey = CDS::PublicKey->fromObject($record->toObject); |
12602
|
|
|
|
|
|
|
|
12603
|
|
|
|
|
|
|
# Return a new CDS::KeyPair instance |
12604
|
0
|
|
|
|
|
|
return CDS::KeyPair->new($publicKey, $rsaPrivateKey); |
12605
|
|
|
|
|
|
|
} |
12606
|
|
|
|
|
|
|
|
12607
|
|
|
|
|
|
|
sub fromFile { |
12608
|
0
|
|
|
0
|
|
|
my $class = shift; |
12609
|
0
|
|
|
|
|
|
my $file = shift; |
12610
|
|
|
|
|
|
|
|
12611
|
0
|
|
0
|
|
|
|
my $bytes = CDS->readBytesFromFile($file) // return; |
12612
|
0
|
|
|
|
|
|
my $record = CDS::Record->fromObject(CDS::Object->fromBytes($bytes)); |
12613
|
0
|
|
|
|
|
|
return $class->fromRecord($record); |
12614
|
|
|
|
|
|
|
} |
12615
|
|
|
|
|
|
|
|
12616
|
|
|
|
|
|
|
sub fromHex { |
12617
|
0
|
|
|
0
|
|
|
my $class = shift; |
12618
|
0
|
|
|
|
|
|
my $hex = shift; |
12619
|
|
|
|
|
|
|
|
12620
|
0
|
|
|
|
|
|
return $class->fromRecord(CDS::Record->fromObject(CDS::Object->fromBytes(pack 'H*', $hex))); |
12621
|
|
|
|
|
|
|
} |
12622
|
|
|
|
|
|
|
|
12623
|
|
|
|
|
|
|
sub fromRecord { |
12624
|
0
|
|
|
0
|
|
|
my $class = shift; |
12625
|
0
|
0
|
0
|
|
|
|
my $record = shift // return; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
0
|
|
|
|
|
12626
|
|
|
|
|
|
|
|
12627
|
0
|
|
0
|
|
|
|
my $publicKey = CDS::PublicKey->fromObject(CDS::Object->fromBytes($record->child('public key object')->bytesValue)) // return; |
12628
|
0
|
|
|
|
|
|
my $rsaKey = $record->child('rsa key'); |
12629
|
0
|
|
|
|
|
|
my $e = $rsaKey->child('e')->bytesValue; |
12630
|
0
|
|
|
|
|
|
my $p = $rsaKey->child('p')->bytesValue; |
12631
|
0
|
|
|
|
|
|
my $q = $rsaKey->child('q')->bytesValue; |
12632
|
0
|
|
0
|
|
|
|
return $class->new($publicKey, CDS::C::privateKeyNew($e, $p, $q) // return); |
12633
|
|
|
|
|
|
|
} |
12634
|
|
|
|
|
|
|
|
12635
|
|
|
|
|
|
|
sub new { |
12636
|
0
|
|
|
0
|
|
|
my $class = shift; |
12637
|
0
|
0
|
0
|
|
|
|
my $publicKey = shift; die 'wrong type '.ref($publicKey).' for $publicKey' if defined $publicKey && ref $publicKey ne 'CDS::PublicKey'; |
|
0
|
|
|
|
|
|
|
12638
|
0
|
|
|
|
|
|
my $rsaPrivateKey = shift; |
12639
|
|
|
|
|
|
|
|
12640
|
0
|
|
|
|
|
|
return bless { |
12641
|
|
|
|
|
|
|
publicKey => $publicKey, # The public key |
12642
|
|
|
|
|
|
|
rsaPrivateKey => $rsaPrivateKey, # The private key |
12643
|
|
|
|
|
|
|
}; |
12644
|
|
|
|
|
|
|
} |
12645
|
|
|
|
|
|
|
|
12646
|
0
|
|
|
0
|
|
|
sub publicKey { shift->{publicKey} } |
12647
|
0
|
|
|
0
|
|
|
sub rsaPrivateKey { shift->{rsaPrivateKey} } |
12648
|
|
|
|
|
|
|
|
12649
|
|
|
|
|
|
|
### Serialization ### |
12650
|
|
|
|
|
|
|
|
12651
|
|
|
|
|
|
|
sub toRecord { |
12652
|
0
|
|
|
0
|
|
|
my $o = shift; |
12653
|
|
|
|
|
|
|
|
12654
|
0
|
|
|
|
|
|
my $record = CDS::Record->new; |
12655
|
0
|
|
|
|
|
|
$record->add('public key object')->add($o->{publicKey}->object->bytes); |
12656
|
0
|
|
|
|
|
|
my $rsaKeyRecord = $record->add('rsa key'); |
12657
|
0
|
|
|
|
|
|
$rsaKeyRecord->add('e')->add(CDS::C::privateKeyE($o->{rsaPrivateKey})); |
12658
|
0
|
|
|
|
|
|
$rsaKeyRecord->add('p')->add(CDS::C::privateKeyP($o->{rsaPrivateKey})); |
12659
|
0
|
|
|
|
|
|
$rsaKeyRecord->add('q')->add(CDS::C::privateKeyQ($o->{rsaPrivateKey})); |
12660
|
0
|
|
|
|
|
|
return $record; |
12661
|
|
|
|
|
|
|
} |
12662
|
|
|
|
|
|
|
|
12663
|
|
|
|
|
|
|
sub toHex { |
12664
|
0
|
|
|
0
|
|
|
my $o = shift; |
12665
|
|
|
|
|
|
|
|
12666
|
0
|
|
|
|
|
|
my $object = $o->toRecord->toObject; |
12667
|
0
|
|
|
|
|
|
return unpack('H*', $object->header).unpack('H*', $object->data); |
12668
|
|
|
|
|
|
|
} |
12669
|
|
|
|
|
|
|
|
12670
|
|
|
|
|
|
|
sub writeToFile { |
12671
|
0
|
|
|
0
|
|
|
my $o = shift; |
12672
|
0
|
|
|
|
|
|
my $file = shift; |
12673
|
|
|
|
|
|
|
|
12674
|
0
|
|
|
|
|
|
my $object = $o->toRecord->toObject; |
12675
|
0
|
|
|
|
|
|
return CDS->writeBytesToFile($file, $object->bytes); |
12676
|
|
|
|
|
|
|
} |
12677
|
|
|
|
|
|
|
|
12678
|
|
|
|
|
|
|
### Private key interface ### |
12679
|
|
|
|
|
|
|
|
12680
|
|
|
|
|
|
|
sub decrypt { |
12681
|
0
|
|
|
0
|
|
|
my $o = shift; |
12682
|
0
|
|
|
|
|
|
my $bytes = shift; |
12683
|
|
|
|
|
|
|
# decrypt(bytes) -> bytes |
12684
|
0
|
|
|
|
|
|
return CDS::C::privateKeyDecrypt($o->{rsaPrivateKey}, $bytes); |
12685
|
|
|
|
|
|
|
} |
12686
|
|
|
|
|
|
|
|
12687
|
|
|
|
|
|
|
sub sign { |
12688
|
0
|
|
|
0
|
|
|
my $o = shift; |
12689
|
0
|
|
|
|
|
|
my $digest = shift; |
12690
|
|
|
|
|
|
|
# sign(bytes) -> bytes |
12691
|
0
|
|
|
|
|
|
return CDS::C::privateKeySign($o->{rsaPrivateKey}, $digest); |
12692
|
|
|
|
|
|
|
} |
12693
|
|
|
|
|
|
|
|
12694
|
|
|
|
|
|
|
sub signHash { |
12695
|
0
|
|
|
0
|
|
|
my $o = shift; |
12696
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12697
|
|
|
|
|
|
|
# signHash(hash) -> bytes |
12698
|
0
|
|
|
|
|
|
return CDS::C::privateKeySign($o->{rsaPrivateKey}, $hash->bytes); |
12699
|
|
|
|
|
|
|
} |
12700
|
|
|
|
|
|
|
|
12701
|
|
|
|
|
|
|
### Retrieval ### |
12702
|
|
|
|
|
|
|
|
12703
|
|
|
|
|
|
|
# Retrieves an object from one of the stores, and decrypts it. |
12704
|
|
|
|
|
|
|
sub getAndDecrypt { |
12705
|
0
|
|
|
0
|
|
|
my $o = shift; |
12706
|
0
|
0
|
0
|
|
|
|
my $hashAndKey = shift; die 'wrong type '.ref($hashAndKey).' for $hashAndKey' if defined $hashAndKey && ref $hashAndKey ne 'CDS::HashAndKey'; |
|
0
|
|
|
|
|
|
|
12707
|
0
|
|
|
|
|
|
my $store = shift; |
12708
|
|
|
|
|
|
|
|
12709
|
0
|
|
|
|
|
|
my ($object, $error) = $store->get($hashAndKey->hash, $o); |
12710
|
0
|
0
|
|
|
|
|
return undef, undef, $error if defined $error; |
12711
|
0
|
0
|
|
|
|
|
return undef, 'Not found.', undef if ! $object; |
12712
|
0
|
|
|
|
|
|
return $object->crypt($hashAndKey->key); |
12713
|
|
|
|
|
|
|
} |
12714
|
|
|
|
|
|
|
|
12715
|
|
|
|
|
|
|
# Retrieves an object from one of the stores, and parses it as record. |
12716
|
|
|
|
|
|
|
sub getRecord { |
12717
|
0
|
|
|
0
|
|
|
my $o = shift; |
12718
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12719
|
0
|
|
|
|
|
|
my $store = shift; |
12720
|
|
|
|
|
|
|
|
12721
|
0
|
|
|
|
|
|
my ($object, $error) = $store->get($hash, $o); |
12722
|
0
|
0
|
|
|
|
|
return undef, undef, undef, $error if defined $error; |
12723
|
0
|
0
|
|
|
|
|
return undef, undef, 'Not found.', undef if ! $object; |
12724
|
0
|
|
0
|
|
|
|
my $record = CDS::Record->fromObject($object) // return undef, undef, 'Not a record.', undef; |
12725
|
0
|
|
|
|
|
|
return $record, $object; |
12726
|
|
|
|
|
|
|
} |
12727
|
|
|
|
|
|
|
|
12728
|
|
|
|
|
|
|
# Retrieves an object from one of the stores, decrypts it, and parses it as record. |
12729
|
|
|
|
|
|
|
sub getAndDecryptRecord { |
12730
|
0
|
|
|
0
|
|
|
my $o = shift; |
12731
|
0
|
0
|
0
|
|
|
|
my $hashAndKey = shift; die 'wrong type '.ref($hashAndKey).' for $hashAndKey' if defined $hashAndKey && ref $hashAndKey ne 'CDS::HashAndKey'; |
|
0
|
|
|
|
|
|
|
12732
|
0
|
|
|
|
|
|
my $store = shift; |
12733
|
|
|
|
|
|
|
|
12734
|
0
|
|
|
|
|
|
my ($object, $error) = $store->get($hashAndKey->hash, $o); |
12735
|
0
|
0
|
|
|
|
|
return undef, undef, undef, $error if defined $error; |
12736
|
0
|
0
|
|
|
|
|
return undef, undef, 'Not found.', undef if ! $object; |
12737
|
0
|
|
|
|
|
|
my $decrypted = $object->crypt($hashAndKey->key); |
12738
|
0
|
|
0
|
|
|
|
my $record = CDS::Record->fromObject($decrypted) // return undef, undef, 'Not a record.', undef; |
12739
|
0
|
|
|
|
|
|
return $record, $object; |
12740
|
|
|
|
|
|
|
} |
12741
|
|
|
|
|
|
|
|
12742
|
|
|
|
|
|
|
# Retrieves an public key object from one of the stores, and parses its public key. |
12743
|
|
|
|
|
|
|
sub getPublicKey { |
12744
|
0
|
|
|
0
|
|
|
my $o = shift; |
12745
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12746
|
0
|
|
|
|
|
|
my $store = shift; |
12747
|
|
|
|
|
|
|
|
12748
|
0
|
|
|
|
|
|
my ($object, $error) = $store->get($hash, $o); |
12749
|
0
|
0
|
|
|
|
|
return undef, undef, $error if defined $error; |
12750
|
0
|
0
|
|
|
|
|
return undef, 'Not found.', undef if ! $object; |
12751
|
0
|
|
0
|
|
|
|
return CDS::PublicKey->fromObject($object) // return undef, 'Not a public key.', undef; |
12752
|
|
|
|
|
|
|
} |
12753
|
|
|
|
|
|
|
|
12754
|
|
|
|
|
|
|
### Equality ### |
12755
|
|
|
|
|
|
|
|
12756
|
|
|
|
|
|
|
sub equals { |
12757
|
0
|
|
|
0
|
|
|
my $this = shift; |
12758
|
0
|
|
|
|
|
|
my $that = shift; |
12759
|
|
|
|
|
|
|
|
12760
|
0
|
0
|
0
|
|
|
|
return 1 if ! defined $this && ! defined $that; |
12761
|
0
|
0
|
0
|
|
|
|
return if ! defined $this || ! defined $that; |
12762
|
0
|
|
|
|
|
|
return $this->publicKey->hash->equals($that->publicKey->hash); |
12763
|
|
|
|
|
|
|
} |
12764
|
|
|
|
|
|
|
|
12765
|
|
|
|
|
|
|
### Open envelopes ### |
12766
|
|
|
|
|
|
|
|
12767
|
|
|
|
|
|
|
sub decryptKeyOnEnvelope { |
12768
|
0
|
|
|
0
|
|
|
my $o = shift; |
12769
|
0
|
0
|
0
|
|
|
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
12770
|
|
|
|
|
|
|
|
12771
|
|
|
|
|
|
|
# Read the AES key |
12772
|
0
|
|
|
|
|
|
my $hashBytes24 = substr($o->{publicKey}->hash->bytes, 0, 24); |
12773
|
0
|
|
|
|
|
|
my $encryptedAesKey = $envelope->child('encrypted for')->child($hashBytes24)->bytesValue; |
12774
|
0
|
0
|
|
|
|
|
$encryptedAesKey = $envelope->child('encrypted for')->child($o->{publicKey}->hash->bytes)->bytesValue if ! length $encryptedAesKey; # todo: remove this |
12775
|
0
|
0
|
|
|
|
|
return if ! length $encryptedAesKey; |
12776
|
|
|
|
|
|
|
|
12777
|
|
|
|
|
|
|
# Decrypt the AES key |
12778
|
0
|
|
|
|
|
|
my $aesKeyBytes = $o->decrypt($encryptedAesKey); |
12779
|
0
|
0
|
0
|
|
|
|
return if ! $aesKeyBytes || length $aesKeyBytes != 32; |
12780
|
|
|
|
|
|
|
|
12781
|
0
|
|
|
|
|
|
return $aesKeyBytes; |
12782
|
|
|
|
|
|
|
} |
12783
|
|
|
|
|
|
|
|
12784
|
|
|
|
|
|
|
# The result of parsing a KEYPAIR token (see Token.pm). |
12785
|
|
|
|
|
|
|
package CDS::KeyPairToken; |
12786
|
|
|
|
|
|
|
|
12787
|
|
|
|
|
|
|
sub new { |
12788
|
0
|
|
|
0
|
|
|
my $class = shift; |
12789
|
0
|
|
|
|
|
|
my $file = shift; |
12790
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12791
|
|
|
|
|
|
|
|
12792
|
0
|
|
|
|
|
|
return bless { |
12793
|
|
|
|
|
|
|
file => $file, |
12794
|
|
|
|
|
|
|
keyPair => $keyPair, |
12795
|
|
|
|
|
|
|
}; |
12796
|
|
|
|
|
|
|
} |
12797
|
|
|
|
|
|
|
|
12798
|
0
|
|
|
0
|
|
|
sub file { shift->{file} } |
12799
|
0
|
|
|
0
|
|
|
sub keyPair { shift->{keyPair} } |
12800
|
|
|
|
|
|
|
|
12801
|
|
|
|
|
|
|
package CDS::LoadActorGroup; |
12802
|
|
|
|
|
|
|
|
12803
|
|
|
|
|
|
|
sub load { |
12804
|
0
|
|
|
0
|
|
|
my $class = shift; |
12805
|
0
|
0
|
0
|
|
|
|
my $builder = shift; die 'wrong type '.ref($builder).' for $builder' if defined $builder && ref $builder ne 'CDS::ActorGroupBuilder'; |
|
0
|
|
|
|
|
|
|
12806
|
0
|
|
|
|
|
|
my $store = shift; |
12807
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12808
|
0
|
|
|
|
|
|
my $delegate = shift; |
12809
|
|
|
|
|
|
|
|
12810
|
0
|
|
|
|
|
|
my $o = bless { |
12811
|
|
|
|
|
|
|
store => $store, |
12812
|
|
|
|
|
|
|
keyPair => $keyPair, |
12813
|
|
|
|
|
|
|
knownPublicKeys => $builder->knownPublicKeys, |
12814
|
|
|
|
|
|
|
}; |
12815
|
|
|
|
|
|
|
|
12816
|
0
|
|
|
|
|
|
my $members = []; |
12817
|
0
|
|
|
|
|
|
for my $member ($builder->members) { |
12818
|
0
|
|
|
|
|
|
my $isActive = $member->status eq 'active'; |
12819
|
0
|
|
|
|
|
|
my $isIdle = $member->status eq 'idle'; |
12820
|
0
|
0
|
0
|
|
|
|
next if ! $isActive && ! $isIdle; |
12821
|
|
|
|
|
|
|
|
12822
|
0
|
|
|
|
|
|
my ($publicKey, $storeError) = $o->getPublicKey($member->hash); |
12823
|
0
|
0
|
|
|
|
|
return undef, $storeError if defined $storeError; |
12824
|
0
|
0
|
|
|
|
|
next if ! $publicKey; |
12825
|
|
|
|
|
|
|
|
12826
|
0
|
|
0
|
|
|
|
my $accountStore = $delegate->onLoadActorGroupVerifyStore($member->storeUrl) // next; |
12827
|
0
|
|
|
|
|
|
my $actorOnStore = CDS::ActorOnStore->new($publicKey, $accountStore); |
12828
|
0
|
|
|
|
|
|
push @$members, CDS::ActorGroup::Member->new($actorOnStore, $member->storeUrl, $member->revision, $isActive); |
12829
|
|
|
|
|
|
|
} |
12830
|
|
|
|
|
|
|
|
12831
|
0
|
|
|
|
|
|
my $entrustedActors = []; |
12832
|
0
|
|
|
|
|
|
for my $actor ($builder->entrustedActors) { |
12833
|
0
|
|
|
|
|
|
my ($publicKey, $storeError) = $o->getPublicKey($actor->hash); |
12834
|
0
|
0
|
|
|
|
|
return undef, $storeError if defined $storeError; |
12835
|
0
|
0
|
|
|
|
|
next if ! $publicKey; |
12836
|
|
|
|
|
|
|
|
12837
|
0
|
|
0
|
|
|
|
my $accountStore = $delegate->onLoadActorGroupVerifyStore($actor->storeUrl) // next; |
12838
|
0
|
|
|
|
|
|
my $actorOnStore = CDS::ActorOnStore->new($publicKey, $accountStore); |
12839
|
0
|
|
|
|
|
|
push @$entrustedActors, CDS::ActorGroup::EntrustedActor->new($actorOnStore, $actor->storeUrl); |
12840
|
|
|
|
|
|
|
} |
12841
|
|
|
|
|
|
|
|
12842
|
0
|
|
|
|
|
|
return CDS::ActorGroup->new($members, $builder->entrustedActorsRevision, $entrustedActors); |
12843
|
|
|
|
|
|
|
} |
12844
|
|
|
|
|
|
|
|
12845
|
|
|
|
|
|
|
sub getPublicKey { |
12846
|
0
|
|
|
0
|
|
|
my $o = shift; |
12847
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12848
|
|
|
|
|
|
|
|
12849
|
0
|
|
|
|
|
|
my $knownPublicKey = $o->{knownPublicKeys}->{$hash->bytes}; |
12850
|
0
|
0
|
|
|
|
|
return $knownPublicKey if $knownPublicKey; |
12851
|
|
|
|
|
|
|
|
12852
|
0
|
|
|
|
|
|
my ($publicKey, $invalidReason, $storeError) = $o->{keyPair}->getPublicKey($hash, $o->{store}); |
12853
|
0
|
0
|
|
|
|
|
return undef, $storeError if defined $storeError; |
12854
|
0
|
0
|
|
|
|
|
return if defined $invalidReason; |
12855
|
|
|
|
|
|
|
|
12856
|
0
|
|
|
|
|
|
$o->{knownPublicKeys}->{$hash->bytes} = $publicKey; |
12857
|
0
|
|
|
|
|
|
return $publicKey; |
12858
|
|
|
|
|
|
|
}; |
12859
|
|
|
|
|
|
|
|
12860
|
|
|
|
|
|
|
# A store that prints all accesses to a filehandle (STDERR by default). |
12861
|
|
|
|
|
|
|
package CDS::LogStore; |
12862
|
|
|
|
|
|
|
|
12863
|
1
|
|
|
1
|
|
6664
|
use parent -norequire, 'CDS::Store'; |
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
5
|
|
12864
|
|
|
|
|
|
|
|
12865
|
|
|
|
|
|
|
sub new { |
12866
|
0
|
|
|
0
|
|
|
my $class = shift; |
12867
|
0
|
|
|
|
|
|
my $store = shift; |
12868
|
0
|
|
0
|
|
|
|
my $fileHandle = shift // *STDERR; |
12869
|
0
|
|
0
|
|
|
|
my $prefix = shift // ''; |
12870
|
|
|
|
|
|
|
|
12871
|
0
|
|
|
|
|
|
return bless { |
12872
|
|
|
|
|
|
|
id => "Log Store\n".$store->id, |
12873
|
|
|
|
|
|
|
store => $store, |
12874
|
|
|
|
|
|
|
fileHandle => $fileHandle, |
12875
|
|
|
|
|
|
|
prefix => '', |
12876
|
|
|
|
|
|
|
}; |
12877
|
|
|
|
|
|
|
} |
12878
|
|
|
|
|
|
|
|
12879
|
0
|
|
|
0
|
|
|
sub id { shift->{id} } |
12880
|
0
|
|
|
0
|
|
|
sub store { shift->{store} } |
12881
|
0
|
|
|
0
|
|
|
sub fileHandle { shift->{fileHandle} } |
12882
|
0
|
|
|
0
|
|
|
sub prefix { shift->{prefix} } |
12883
|
|
|
|
|
|
|
|
12884
|
|
|
|
|
|
|
sub get { |
12885
|
0
|
|
|
0
|
|
|
my $o = shift; |
12886
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12887
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12888
|
|
|
|
|
|
|
|
12889
|
0
|
|
|
|
|
|
my $start = CDS::C::performanceStart(); |
12890
|
0
|
|
|
|
|
|
my ($object, $error) = $o->{store}->get($hash, $keyPair); |
12891
|
0
|
|
|
|
|
|
my $elapsed = CDS::C::performanceElapsed($start); |
12892
|
0
|
0
|
|
|
|
|
$o->log('get', $hash->shortHex, defined $object ? &formatByteLength($object->byteLength).' bytes' : defined $error ? 'failed: '.$error : 'not found', $elapsed); |
|
|
0
|
|
|
|
|
|
12893
|
0
|
|
|
|
|
|
return $object, $error; |
12894
|
|
|
|
|
|
|
} |
12895
|
|
|
|
|
|
|
|
12896
|
|
|
|
|
|
|
sub put { |
12897
|
0
|
|
|
0
|
|
|
my $o = shift; |
12898
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12899
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
12900
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12901
|
|
|
|
|
|
|
|
12902
|
0
|
|
|
|
|
|
my $start = CDS::C::performanceStart(); |
12903
|
0
|
|
|
|
|
|
my $error = $o->{store}->put($hash, $object, $keyPair); |
12904
|
0
|
|
|
|
|
|
my $elapsed = CDS::C::performanceElapsed($start); |
12905
|
0
|
0
|
|
|
|
|
$o->log('put', $hash->shortHex . ' ' . &formatByteLength($object->byteLength) . ' bytes', defined $error ? 'failed: '.$error : 'OK', $elapsed); |
12906
|
0
|
|
|
|
|
|
return $error; |
12907
|
|
|
|
|
|
|
} |
12908
|
|
|
|
|
|
|
|
12909
|
|
|
|
|
|
|
sub book { |
12910
|
0
|
|
|
0
|
|
|
my $o = shift; |
12911
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12912
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12913
|
|
|
|
|
|
|
|
12914
|
0
|
|
|
|
|
|
my $start = CDS::C::performanceStart(); |
12915
|
0
|
|
|
|
|
|
my ($booked, $error) = $o->{store}->book($hash, $keyPair); |
12916
|
0
|
|
|
|
|
|
my $elapsed = CDS::C::performanceElapsed($start); |
12917
|
0
|
0
|
|
|
|
|
$o->log('book', $hash->shortHex, defined $booked ? 'OK' : defined $error ? 'failed: '.$error : 'not found', $elapsed); |
|
|
0
|
|
|
|
|
|
12918
|
0
|
|
|
|
|
|
return $booked, $error; |
12919
|
|
|
|
|
|
|
} |
12920
|
|
|
|
|
|
|
|
12921
|
|
|
|
|
|
|
sub list { |
12922
|
0
|
|
|
0
|
|
|
my $o = shift; |
12923
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12924
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
12925
|
0
|
|
|
|
|
|
my $timeout = shift; |
12926
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12927
|
|
|
|
|
|
|
|
12928
|
0
|
|
|
|
|
|
my $start = CDS::C::performanceStart(); |
12929
|
0
|
|
|
|
|
|
my ($hashes, $error) = $o->{store}->list($accountHash, $boxLabel, $timeout, $keyPair); |
12930
|
0
|
|
|
|
|
|
my $elapsed = CDS::C::performanceElapsed($start); |
12931
|
0
|
0
|
|
|
|
|
$o->log('list', $accountHash->shortHex . ' ' . $boxLabel . ($timeout ? ' ' . $timeout . ' s' : ''), defined $hashes ? scalar(@$hashes).' entries' : 'failed: '.$error, $elapsed); |
|
|
0
|
|
|
|
|
|
12932
|
0
|
|
|
|
|
|
return $hashes, $error; |
12933
|
|
|
|
|
|
|
} |
12934
|
|
|
|
|
|
|
|
12935
|
|
|
|
|
|
|
sub add { |
12936
|
0
|
|
|
0
|
|
|
my $o = shift; |
12937
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12938
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
12939
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12940
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12941
|
|
|
|
|
|
|
|
12942
|
0
|
|
|
|
|
|
my $start = CDS::C::performanceStart(); |
12943
|
0
|
|
|
|
|
|
my $error = $o->{store}->add($accountHash, $boxLabel, $hash, $keyPair); |
12944
|
0
|
|
|
|
|
|
my $elapsed = CDS::C::performanceElapsed($start); |
12945
|
0
|
0
|
|
|
|
|
$o->log('add', $accountHash->shortHex . ' ' . $boxLabel . ' ' . $hash->shortHex, defined $error ? 'failed: '.$error : 'OK', $elapsed); |
12946
|
0
|
|
|
|
|
|
return $error; |
12947
|
|
|
|
|
|
|
} |
12948
|
|
|
|
|
|
|
|
12949
|
|
|
|
|
|
|
sub remove { |
12950
|
0
|
|
|
0
|
|
|
my $o = shift; |
12951
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12952
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
12953
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
12954
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12955
|
|
|
|
|
|
|
|
12956
|
0
|
|
|
|
|
|
my $start = CDS::C::performanceStart(); |
12957
|
0
|
|
|
|
|
|
my $error = $o->{store}->remove($accountHash, $boxLabel, $hash, $keyPair); |
12958
|
0
|
|
|
|
|
|
my $elapsed = CDS::C::performanceElapsed($start); |
12959
|
0
|
0
|
|
|
|
|
$o->log('remove', $accountHash->shortHex . ' ' . $boxLabel . ' ' . $hash->shortHex, defined $error ? 'failed: '.$error : 'OK', $elapsed); |
12960
|
0
|
|
|
|
|
|
return $error; |
12961
|
|
|
|
|
|
|
} |
12962
|
|
|
|
|
|
|
|
12963
|
|
|
|
|
|
|
sub modify { |
12964
|
0
|
|
|
0
|
|
|
my $o = shift; |
12965
|
0
|
|
|
|
|
|
my $modifications = shift; |
12966
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
12967
|
|
|
|
|
|
|
|
12968
|
0
|
|
|
|
|
|
my $start = CDS::C::performanceStart(); |
12969
|
0
|
|
|
|
|
|
my $error = $o->{store}->modify($modifications, $keyPair); |
12970
|
0
|
|
|
|
|
|
my $elapsed = CDS::C::performanceElapsed($start); |
12971
|
0
|
0
|
|
|
|
|
$o->log('modify', scalar(keys %{$modifications->objects}) . ' objects ' . scalar @{$modifications->additions} . ' additions ' . scalar @{$modifications->removals} . ' removals', defined $error ? 'failed: '.$error : 'OK', $elapsed); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
12972
|
0
|
|
|
|
|
|
return $error; |
12973
|
|
|
|
|
|
|
} |
12974
|
|
|
|
|
|
|
|
12975
|
|
|
|
|
|
|
sub log { |
12976
|
0
|
|
|
0
|
|
|
my $o = shift; |
12977
|
0
|
|
|
|
|
|
my $cmd = shift; |
12978
|
0
|
|
|
|
|
|
my $input = shift; |
12979
|
0
|
|
|
|
|
|
my $output = shift; |
12980
|
0
|
|
|
|
|
|
my $elapsed = shift; |
12981
|
|
|
|
|
|
|
|
12982
|
0
|
|
0
|
|
|
|
my $fh = $o->{fileHandle} // return; |
12983
|
0
|
|
|
|
|
|
print $fh $o->{prefix}, &left(8, $cmd), &left(40, $input), ' => ', &left(40, $output), &formatDuration($elapsed), ' us', "\n"; |
12984
|
|
|
|
|
|
|
} |
12985
|
|
|
|
|
|
|
|
12986
|
|
|
|
|
|
|
sub left { |
12987
|
0
|
|
|
0
|
|
|
my $width = shift; |
12988
|
0
|
|
|
|
|
|
my $text = shift; |
12989
|
|
|
|
|
|
|
# private |
12990
|
0
|
0
|
|
|
|
|
return $text . (' ' x ($width - length $text)) if length $text < $width; |
12991
|
0
|
|
|
|
|
|
return $text; |
12992
|
|
|
|
|
|
|
} |
12993
|
|
|
|
|
|
|
|
12994
|
|
|
|
|
|
|
sub formatByteLength { |
12995
|
0
|
|
|
0
|
|
|
my $byteLength = shift; |
12996
|
|
|
|
|
|
|
# private |
12997
|
0
|
|
|
|
|
|
my $s = ''.$byteLength; |
12998
|
0
|
0
|
|
|
|
|
$s = ' ' x (9 - length $s) . $s if length $s < 9; |
12999
|
0
|
|
|
|
|
|
my $len = length $s; |
13000
|
0
|
|
|
|
|
|
return substr($s, 0, $len - 6).' '.substr($s, $len - 6, 3).' '.substr($s, $len - 3, 3); |
13001
|
|
|
|
|
|
|
} |
13002
|
|
|
|
|
|
|
|
13003
|
|
|
|
|
|
|
sub formatDuration { |
13004
|
0
|
|
|
0
|
|
|
my $elapsed = shift; |
13005
|
|
|
|
|
|
|
# private |
13006
|
0
|
|
|
|
|
|
my $s = ''.$elapsed; |
13007
|
0
|
0
|
|
|
|
|
$s = ' ' x (9 - length $s) . $s if length $s < 9; |
13008
|
0
|
|
|
|
|
|
my $len = length $s; |
13009
|
0
|
|
|
|
|
|
return substr($s, 0, $len - 6).' '.substr($s, $len - 6, 3).' '.substr($s, $len - 3, 3); |
13010
|
|
|
|
|
|
|
} |
13011
|
|
|
|
|
|
|
|
13012
|
|
|
|
|
|
|
# Reads the message box of an actor. |
13013
|
|
|
|
|
|
|
package CDS::MessageBoxReader; |
13014
|
|
|
|
|
|
|
|
13015
|
|
|
|
|
|
|
sub new { |
13016
|
0
|
|
|
0
|
|
|
my $class = shift; |
13017
|
0
|
|
|
|
|
|
my $pool = shift; |
13018
|
0
|
0
|
0
|
|
|
|
my $actorOnStore = shift; die 'wrong type '.ref($actorOnStore).' for $actorOnStore' if defined $actorOnStore && ref $actorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
|
|
13019
|
0
|
|
|
|
|
|
my $streamTimeout = shift; |
13020
|
|
|
|
|
|
|
|
13021
|
0
|
|
0
|
|
|
|
return bless { |
13022
|
|
|
|
|
|
|
pool => $pool, |
13023
|
|
|
|
|
|
|
actorOnStore => $actorOnStore, |
13024
|
|
|
|
|
|
|
streamCache => CDS::StreamCache->new($pool, $actorOnStore, $streamTimeout // CDS->MINUTE), |
13025
|
|
|
|
|
|
|
entries => {}, |
13026
|
|
|
|
|
|
|
}; |
13027
|
|
|
|
|
|
|
} |
13028
|
|
|
|
|
|
|
|
13029
|
0
|
|
|
0
|
|
|
sub pool { shift->{pool} } |
13030
|
0
|
|
|
0
|
|
|
sub actorOnStore { shift->{actorOnStore} } |
13031
|
|
|
|
|
|
|
|
13032
|
|
|
|
|
|
|
sub read { |
13033
|
0
|
|
|
0
|
|
|
my $o = shift; |
13034
|
0
|
|
0
|
|
|
|
my $timeout = shift // 0; |
13035
|
|
|
|
|
|
|
|
13036
|
0
|
|
|
|
|
|
my $store = $o->{actorOnStore}->store; |
13037
|
0
|
|
|
|
|
|
my ($hashes, $listError) = $store->list($o->{actorOnStore}->publicKey->hash, 'messages', $timeout, $o->{pool}->{keyPair}); |
13038
|
0
|
0
|
|
|
|
|
return if defined $listError; |
13039
|
|
|
|
|
|
|
|
13040
|
0
|
|
|
|
|
|
for my $hash (@$hashes) { |
13041
|
0
|
|
|
|
|
|
my $entry = $o->{entries}->{$hash->bytes}; |
13042
|
0
|
0
|
|
|
|
|
$o->{entries}->{$hash->bytes} = $entry = CDS::MessageBoxReader::Entry->new($hash) if ! $entry; |
13043
|
0
|
0
|
|
|
|
|
next if $entry->{processed}; |
13044
|
|
|
|
|
|
|
|
13045
|
|
|
|
|
|
|
# Check the sender store, if necessary |
13046
|
0
|
0
|
|
|
|
|
if ($entry->{waitingForStore}) { |
13047
|
0
|
|
|
|
|
|
my ($dummy, $checkError) = $entry->{waitingForStore}->get(CDS->emptyBytesHash, $o->{pool}->{keyPair}); |
13048
|
0
|
0
|
|
|
|
|
next if defined $checkError; |
13049
|
|
|
|
|
|
|
} |
13050
|
|
|
|
|
|
|
|
13051
|
|
|
|
|
|
|
# Get the envelope |
13052
|
0
|
|
|
|
|
|
my ($object, $getError) = $o->{actorOnStore}->store->get($entry->{hash}, $o->{pool}->{keyPair}); |
13053
|
0
|
0
|
|
|
|
|
return if defined $getError; |
13054
|
|
|
|
|
|
|
|
13055
|
|
|
|
|
|
|
# Mark the entry as processed |
13056
|
0
|
|
|
|
|
|
$entry->{processed} = 1; |
13057
|
|
|
|
|
|
|
|
13058
|
0
|
0
|
|
|
|
|
if (! defined $object) { |
13059
|
0
|
|
|
|
|
|
$o->invalid($entry, 'Envelope object not found.'); |
13060
|
0
|
|
|
|
|
|
next; |
13061
|
|
|
|
|
|
|
} |
13062
|
|
|
|
|
|
|
|
13063
|
|
|
|
|
|
|
# Parse the record |
13064
|
0
|
|
|
|
|
|
my $envelope = CDS::Record->fromObject($object); |
13065
|
0
|
0
|
|
|
|
|
if (! $envelope) { |
13066
|
0
|
|
|
|
|
|
$o->invalid($entry, 'Envelope is not a record.'); |
13067
|
0
|
|
|
|
|
|
next; |
13068
|
|
|
|
|
|
|
} |
13069
|
|
|
|
|
|
|
|
13070
|
0
|
0
|
0
|
|
|
|
my $message = |
13071
|
|
|
|
|
|
|
$envelope->contains('head') && $envelope->contains('mac') ? |
13072
|
|
|
|
|
|
|
$o->readStreamMessage($entry, $envelope) : |
13073
|
|
|
|
|
|
|
$o->readNormalMessage($entry, $envelope); |
13074
|
0
|
0
|
|
|
|
|
next if ! $message; |
13075
|
|
|
|
|
|
|
|
13076
|
0
|
|
|
|
|
|
$o->{pool}->{delegate}->onMessageBoxEntry($message); |
13077
|
|
|
|
|
|
|
} |
13078
|
|
|
|
|
|
|
|
13079
|
0
|
|
|
|
|
|
$o->{streamCache}->removeObsolete; |
13080
|
0
|
|
|
|
|
|
return 1; |
13081
|
|
|
|
|
|
|
} |
13082
|
|
|
|
|
|
|
|
13083
|
|
|
|
|
|
|
sub readNormalMessage { |
13084
|
0
|
|
|
0
|
|
|
my $o = shift; |
13085
|
0
|
|
|
|
|
|
my $entry = shift; |
13086
|
0
|
0
|
0
|
|
|
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
13087
|
|
|
|
|
|
|
# private |
13088
|
|
|
|
|
|
|
# Read the embedded content object |
13089
|
0
|
|
|
|
|
|
my $encryptedBytes = $envelope->child('content')->bytesValue; |
13090
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Missing content object.') if ! length $encryptedBytes; |
13091
|
|
|
|
|
|
|
|
13092
|
|
|
|
|
|
|
# Decrypt the key |
13093
|
0
|
|
|
|
|
|
my $aesKey = $o->{pool}->{keyPair}->decryptKeyOnEnvelope($envelope); |
13094
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Not encrypted for us.') if ! $aesKey; |
13095
|
|
|
|
|
|
|
|
13096
|
|
|
|
|
|
|
# Decrypt the content |
13097
|
0
|
|
|
|
|
|
my $contentObject = CDS::Object->fromBytes(CDS::C::aesCrypt($encryptedBytes, $aesKey, CDS->zeroCTR)); |
13098
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Invalid content object.') if ! $contentObject; |
13099
|
|
|
|
|
|
|
|
13100
|
0
|
|
|
|
|
|
my $content = CDS::Record->fromObject($contentObject); |
13101
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Content object is not a record.') if ! $content; |
13102
|
|
|
|
|
|
|
|
13103
|
|
|
|
|
|
|
# Verify the sender hash |
13104
|
0
|
|
|
|
|
|
my $senderHash = $content->child('sender')->hashValue; |
13105
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Missing sender hash.') if ! $senderHash; |
13106
|
|
|
|
|
|
|
|
13107
|
|
|
|
|
|
|
# Verify the sender store |
13108
|
0
|
|
|
|
|
|
my $storeRecord = $content->child('store'); |
13109
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Missing sender store.') if ! scalar $storeRecord->children; |
13110
|
|
|
|
|
|
|
|
13111
|
0
|
|
|
|
|
|
my $senderStoreUrl = $storeRecord->textValue; |
13112
|
0
|
|
|
|
|
|
my $senderStore = $o->{pool}->{delegate}->onMessageBoxVerifyStore($senderStoreUrl, $entry->{hash}, $envelope, $senderHash); |
13113
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Invalid sender store.') if ! $senderStore; |
13114
|
|
|
|
|
|
|
|
13115
|
|
|
|
|
|
|
# Retrieve the sender's public key |
13116
|
0
|
|
|
|
|
|
my ($senderPublicKey, $invalidReason, $publicKeyStoreError) = $o->getPublicKey($senderHash, $senderStore); |
13117
|
0
|
0
|
|
|
|
|
return if defined $publicKeyStoreError; |
13118
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Failed to retrieve the sender\'s public key: '.$invalidReason) if defined $invalidReason; |
13119
|
|
|
|
|
|
|
|
13120
|
|
|
|
|
|
|
# Verify the signature |
13121
|
0
|
|
|
|
|
|
my $signedHash = CDS::Hash->calculateFor($encryptedBytes); |
13122
|
0
|
0
|
|
|
|
|
if (! CDS->verifyEnvelopeSignature($envelope, $senderPublicKey, $signedHash)) { |
13123
|
|
|
|
|
|
|
# For backwards compatibility with versions before 2020-05-05 |
13124
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Invalid signature.') if ! CDS->verifyEnvelopeSignature($envelope, $senderPublicKey, $contentObject->calculateHash); |
13125
|
|
|
|
|
|
|
} |
13126
|
|
|
|
|
|
|
|
13127
|
|
|
|
|
|
|
# The envelope is valid |
13128
|
0
|
|
|
|
|
|
my $sender = CDS::ActorOnStore->new($senderPublicKey, $senderStore); |
13129
|
0
|
|
|
|
|
|
my $source = CDS::Source->new($o->{pool}->{keyPair}, $o->{actorOnStore}, 'messages', $entry->{hash}); |
13130
|
0
|
|
|
|
|
|
return CDS::ReceivedMessage->new($o, $entry, $source, $envelope, $senderStoreUrl, $sender, $content); |
13131
|
|
|
|
|
|
|
} |
13132
|
|
|
|
|
|
|
|
13133
|
|
|
|
|
|
|
sub readStreamMessage { |
13134
|
0
|
|
|
0
|
|
|
my $o = shift; |
13135
|
0
|
|
|
|
|
|
my $entry = shift; |
13136
|
0
|
0
|
0
|
|
|
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
13137
|
|
|
|
|
|
|
# private |
13138
|
|
|
|
|
|
|
# Get the head |
13139
|
0
|
|
|
|
|
|
my $head = $envelope->child('head')->hashValue; |
13140
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Invalid head message hash.') if ! $head; |
13141
|
|
|
|
|
|
|
|
13142
|
|
|
|
|
|
|
# Get the head envelope |
13143
|
0
|
|
|
|
|
|
my $streamHead = $o->{streamCache}->readStreamHead($head); |
13144
|
0
|
0
|
|
|
|
|
return if ! $streamHead; |
13145
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Invalid stream head: '.$streamHead->error) if $streamHead->error; |
13146
|
|
|
|
|
|
|
|
13147
|
|
|
|
|
|
|
# Read the embedded content object |
13148
|
0
|
|
|
|
|
|
my $encryptedBytes = $envelope->child('content')->bytesValue; |
13149
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Missing content object.') if ! length $encryptedBytes; |
13150
|
|
|
|
|
|
|
|
13151
|
|
|
|
|
|
|
# Get the CTR |
13152
|
0
|
|
|
|
|
|
my $ctr = $envelope->child('ctr')->bytesValue; |
13153
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Invalid CTR.') if length $ctr != 16; |
13154
|
|
|
|
|
|
|
|
13155
|
|
|
|
|
|
|
# Get the MAC |
13156
|
0
|
|
|
|
|
|
my $mac = $envelope->child('mac')->bytesValue; |
13157
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Invalid MAC.') if ! $mac; |
13158
|
|
|
|
|
|
|
|
13159
|
|
|
|
|
|
|
# Verify the MAC |
13160
|
0
|
|
|
|
|
|
my $signedHash = CDS::Hash->calculateFor($encryptedBytes); |
13161
|
0
|
|
|
|
|
|
my $expectedMac = CDS::C::aesCrypt($signedHash->bytes, $streamHead->aesKey, $ctr); |
13162
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Invalid MAC.') if $mac ne $expectedMac; |
13163
|
|
|
|
|
|
|
|
13164
|
|
|
|
|
|
|
# Decrypt the content |
13165
|
0
|
|
|
|
|
|
my $contentObject = CDS::Object->fromBytes(CDS::C::aesCrypt($encryptedBytes, $streamHead->aesKey, CDS::C::counterPlusInt($ctr, 2))); |
13166
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Invalid content object.') if ! $contentObject; |
13167
|
|
|
|
|
|
|
|
13168
|
0
|
|
|
|
|
|
my $content = CDS::Record->fromObject($contentObject); |
13169
|
0
|
0
|
|
|
|
|
return $o->invalid($entry, 'Content object is not a record.') if ! $content; |
13170
|
|
|
|
|
|
|
|
13171
|
|
|
|
|
|
|
# The envelope is valid |
13172
|
0
|
|
|
|
|
|
my $source = CDS::Source->new($o->{pool}->{keyPair}, $o->{actorOnStore}, 'messages', $entry->{hash}); |
13173
|
0
|
|
|
|
|
|
return CDS::ReceivedMessage->new($o, $entry, $source, $envelope, $streamHead->senderStoreUrl, $streamHead->sender, $content, $streamHead); |
13174
|
|
|
|
|
|
|
} |
13175
|
|
|
|
|
|
|
|
13176
|
|
|
|
|
|
|
sub invalid { |
13177
|
0
|
|
|
0
|
|
|
my $o = shift; |
13178
|
0
|
|
|
|
|
|
my $entry = shift; |
13179
|
0
|
|
|
|
|
|
my $reason = shift; |
13180
|
|
|
|
|
|
|
# private |
13181
|
0
|
|
|
|
|
|
my $source = CDS::Source->new($o->{pool}->{keyPair}, $o->{actorOnStore}, 'messages', $entry->{hash}); |
13182
|
0
|
|
|
|
|
|
$o->{pool}->{delegate}->onMessageBoxInvalidEntry($source, $reason); |
13183
|
|
|
|
|
|
|
} |
13184
|
|
|
|
|
|
|
|
13185
|
|
|
|
|
|
|
sub getPublicKey { |
13186
|
0
|
|
|
0
|
|
|
my $o = shift; |
13187
|
0
|
0
|
0
|
|
|
|
my $senderHash = shift; die 'wrong type '.ref($senderHash).' for $senderHash' if defined $senderHash && ref $senderHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
13188
|
0
|
|
|
|
|
|
my $senderStore = shift; |
13189
|
0
|
|
|
|
|
|
my $senderStoreUrl = shift; |
13190
|
|
|
|
|
|
|
# private |
13191
|
|
|
|
|
|
|
# Use the account key if sender and recipient are the same |
13192
|
0
|
0
|
|
|
|
|
return $o->{actorOnStore}->publicKey if $senderHash->equals($o->{actorOnStore}->publicKey->hash); |
13193
|
|
|
|
|
|
|
|
13194
|
|
|
|
|
|
|
# Reuse a cached public key |
13195
|
0
|
|
|
|
|
|
my $cachedPublicKey = $o->{pool}->{publicKeyCache}->get($senderHash); |
13196
|
0
|
0
|
|
|
|
|
return $cachedPublicKey if $cachedPublicKey; |
13197
|
|
|
|
|
|
|
|
13198
|
|
|
|
|
|
|
# Retrieve the sender's public key from the sender's store |
13199
|
0
|
|
|
|
|
|
my ($publicKey, $invalidReason, $storeError) = $o->{pool}->{keyPair}->getPublicKey($senderHash, $senderStore); |
13200
|
0
|
0
|
|
|
|
|
return undef, undef, $storeError if defined $storeError; |
13201
|
0
|
0
|
|
|
|
|
return undef, $invalidReason if defined $invalidReason; |
13202
|
0
|
|
|
|
|
|
$o->{pool}->{publicKeyCache}->add($publicKey); |
13203
|
0
|
|
|
|
|
|
return $publicKey; |
13204
|
|
|
|
|
|
|
} |
13205
|
|
|
|
|
|
|
|
13206
|
|
|
|
|
|
|
package CDS::MessageBoxReader::Entry; |
13207
|
|
|
|
|
|
|
|
13208
|
|
|
|
|
|
|
sub new { |
13209
|
0
|
|
|
0
|
|
|
my $class = shift; |
13210
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
13211
|
|
|
|
|
|
|
|
13212
|
0
|
|
|
|
|
|
return bless { |
13213
|
|
|
|
|
|
|
hash => $hash, |
13214
|
|
|
|
|
|
|
processed => 0, |
13215
|
|
|
|
|
|
|
}; |
13216
|
|
|
|
|
|
|
} |
13217
|
|
|
|
|
|
|
|
13218
|
|
|
|
|
|
|
package CDS::MessageBoxReaderPool; |
13219
|
|
|
|
|
|
|
|
13220
|
|
|
|
|
|
|
sub new { |
13221
|
0
|
|
|
0
|
|
|
my $class = shift; |
13222
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
13223
|
0
|
|
|
|
|
|
my $publicKeyCache = shift; |
13224
|
0
|
|
|
|
|
|
my $delegate = shift; |
13225
|
|
|
|
|
|
|
|
13226
|
0
|
|
|
|
|
|
return bless { |
13227
|
|
|
|
|
|
|
keyPair => $keyPair, |
13228
|
|
|
|
|
|
|
publicKeyCache => $publicKeyCache, |
13229
|
|
|
|
|
|
|
delegate => $delegate, |
13230
|
|
|
|
|
|
|
}; |
13231
|
|
|
|
|
|
|
} |
13232
|
|
|
|
|
|
|
|
13233
|
0
|
|
|
0
|
|
|
sub keyPair { shift->{keyPair} } |
13234
|
0
|
|
|
0
|
|
|
sub publicKeyCache { shift->{publicKeyCache} } |
13235
|
|
|
|
|
|
|
|
13236
|
|
|
|
|
|
|
# Delegate |
13237
|
|
|
|
|
|
|
# onMessageBoxVerifyStore($senderStoreUrl, $hash, $envelope, $senderHash) |
13238
|
|
|
|
|
|
|
# onMessageBoxEntry($receivedMessage) |
13239
|
|
|
|
|
|
|
# onMessageBoxStream($receivedMessage) |
13240
|
|
|
|
|
|
|
# onMessageBoxInvalidEntry($source, $reason) |
13241
|
|
|
|
|
|
|
|
13242
|
|
|
|
|
|
|
package CDS::MessageChannel; |
13243
|
|
|
|
|
|
|
|
13244
|
|
|
|
|
|
|
sub new { |
13245
|
0
|
|
|
0
|
|
|
my $class = shift; |
13246
|
0
|
|
|
|
|
|
my $actor = shift; |
13247
|
0
|
|
|
|
|
|
my $label = shift; |
13248
|
0
|
|
|
|
|
|
my $validity = shift; |
13249
|
|
|
|
|
|
|
|
13250
|
0
|
|
|
|
|
|
my $o = bless { |
13251
|
|
|
|
|
|
|
actor => $actor, |
13252
|
|
|
|
|
|
|
label => $label, |
13253
|
|
|
|
|
|
|
validity => $validity, |
13254
|
|
|
|
|
|
|
}; |
13255
|
|
|
|
|
|
|
|
13256
|
0
|
|
|
|
|
|
$o->{unsaved} = CDS::Unsaved->new($actor->sentList->unsaved); |
13257
|
0
|
|
|
|
|
|
$o->{transfers} = []; |
13258
|
0
|
|
|
|
|
|
$o->{recipients} = []; |
13259
|
0
|
|
|
|
|
|
$o->{entrustedKeys} = []; |
13260
|
0
|
|
|
|
|
|
$o->{obsoleteHashes} = {}; |
13261
|
0
|
|
|
|
|
|
$o->{currentSubmissionId} = 0; |
13262
|
0
|
|
|
|
|
|
return $o; |
13263
|
|
|
|
|
|
|
} |
13264
|
|
|
|
|
|
|
|
13265
|
0
|
|
|
0
|
|
|
sub actor { shift->{actor} } |
13266
|
0
|
|
|
0
|
|
|
sub label { shift->{label} } |
13267
|
0
|
|
|
0
|
|
|
sub validity { shift->{validity} } |
13268
|
0
|
|
|
0
|
|
|
sub unsaved { shift->{unsaved} } |
13269
|
|
|
|
|
|
|
sub item { |
13270
|
0
|
|
|
0
|
|
|
my $o = shift; |
13271
|
0
|
|
|
|
|
|
$o->{actor}->sentList->getOrCreate($o->{label}) } |
13272
|
|
|
|
|
|
|
sub recipients { |
13273
|
0
|
|
|
0
|
|
|
my $o = shift; |
13274
|
0
|
|
|
|
|
|
@{$o->{recipients}} } |
|
0
|
|
|
|
|
|
|
13275
|
|
|
|
|
|
|
sub entrustedKeys { |
13276
|
0
|
|
|
0
|
|
|
my $o = shift; |
13277
|
0
|
|
|
|
|
|
@{$o->{entrustedKeys}} } |
|
0
|
|
|
|
|
|
|
13278
|
|
|
|
|
|
|
|
13279
|
|
|
|
|
|
|
sub addObject { |
13280
|
0
|
|
|
0
|
|
|
my $o = shift; |
13281
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
13282
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
13283
|
|
|
|
|
|
|
|
13284
|
0
|
|
|
|
|
|
$o->{unsaved}->state->addObject($hash, $object); |
13285
|
|
|
|
|
|
|
} |
13286
|
|
|
|
|
|
|
|
13287
|
|
|
|
|
|
|
sub addTransfer { |
13288
|
0
|
|
|
0
|
|
|
my $o = shift; |
13289
|
0
|
|
|
|
|
|
my $hashes = shift; |
13290
|
0
|
|
|
|
|
|
my $sourceStore = shift; |
13291
|
0
|
|
|
|
|
|
my $context = shift; |
13292
|
|
|
|
|
|
|
|
13293
|
0
|
0
|
|
|
|
|
return if ! scalar @$hashes; |
13294
|
0
|
|
|
|
|
|
push @{$o->{transfers}}, {hashes => $hashes, sourceStore => $sourceStore, context => $context}; |
|
0
|
|
|
|
|
|
|
13295
|
|
|
|
|
|
|
} |
13296
|
|
|
|
|
|
|
|
13297
|
|
|
|
|
|
|
sub setRecipientActorGroup { |
13298
|
0
|
|
|
0
|
|
|
my $o = shift; |
13299
|
0
|
0
|
0
|
|
|
|
my $actorGroup = shift; die 'wrong type '.ref($actorGroup).' for $actorGroup' if defined $actorGroup && ref $actorGroup ne 'CDS::ActorGroup'; |
|
0
|
|
|
|
|
|
|
13300
|
|
|
|
|
|
|
|
13301
|
0
|
|
|
|
|
|
$o->{recipients} = [map { $_->actorOnStore } $actorGroup->members]; |
|
0
|
|
|
|
|
|
|
13302
|
0
|
|
|
|
|
|
$o->{entrustedKeys} = [map { $_->actorOnStore->publicKey } $actorGroup->entrustedActors]; |
|
0
|
|
|
|
|
|
|
13303
|
|
|
|
|
|
|
} |
13304
|
|
|
|
|
|
|
|
13305
|
|
|
|
|
|
|
sub setRecipients { |
13306
|
0
|
|
|
0
|
|
|
my $o = shift; |
13307
|
0
|
|
|
|
|
|
my $recipients = shift; |
13308
|
0
|
|
|
|
|
|
my $entrustedKeys = shift; |
13309
|
|
|
|
|
|
|
|
13310
|
0
|
|
|
|
|
|
$o->{recipients} = $recipients; |
13311
|
0
|
|
|
|
|
|
$o->{entrustedKeys} = $entrustedKeys; |
13312
|
|
|
|
|
|
|
} |
13313
|
|
|
|
|
|
|
|
13314
|
|
|
|
|
|
|
sub submit { |
13315
|
0
|
|
|
0
|
|
|
my $o = shift; |
13316
|
0
|
|
|
|
|
|
my $message = shift; |
13317
|
0
|
|
|
|
|
|
my $done = shift; |
13318
|
|
|
|
|
|
|
|
13319
|
|
|
|
|
|
|
# Check if the sent list has been loaded |
13320
|
0
|
0
|
|
|
|
|
return if ! $o->{actor}->sentListReady; |
13321
|
|
|
|
|
|
|
|
13322
|
|
|
|
|
|
|
# Transfer |
13323
|
0
|
|
|
|
|
|
my $transfers = $o->{transfers}; |
13324
|
0
|
|
|
|
|
|
$o->{transfers} = []; |
13325
|
0
|
|
|
|
|
|
for my $transfer (@$transfers) { |
13326
|
0
|
|
|
|
|
|
my ($missingObject, $store, $error) = $o->{actor}->keyPair->transfer($transfer->{hashes}, $transfer->{sourceStore}, $o->{actor}->messagingPrivateRoot->unsaved); |
13327
|
0
|
0
|
|
|
|
|
return if defined $error; |
13328
|
|
|
|
|
|
|
|
13329
|
0
|
0
|
|
|
|
|
if ($missingObject) { |
13330
|
0
|
|
|
|
|
|
$missingObject->{context} = $transfer->{context}; |
13331
|
0
|
|
|
|
|
|
return undef, $missingObject; |
13332
|
|
|
|
|
|
|
} |
13333
|
|
|
|
|
|
|
} |
13334
|
|
|
|
|
|
|
|
13335
|
|
|
|
|
|
|
# Send the message |
13336
|
0
|
|
|
|
|
|
return CDS::MessageChannel::Submission->new($o, $message, $done); |
13337
|
|
|
|
|
|
|
} |
13338
|
|
|
|
|
|
|
|
13339
|
|
|
|
|
|
|
sub clear { |
13340
|
0
|
|
|
0
|
|
|
my $o = shift; |
13341
|
|
|
|
|
|
|
|
13342
|
0
|
|
|
|
|
|
$o->item->clear(CDS->now + $o->{validity}); |
13343
|
|
|
|
|
|
|
} |
13344
|
|
|
|
|
|
|
|
13345
|
|
|
|
|
|
|
package CDS::MessageChannel::Submission; |
13346
|
|
|
|
|
|
|
|
13347
|
|
|
|
|
|
|
sub new { |
13348
|
0
|
|
|
0
|
|
|
my $class = shift; |
13349
|
0
|
|
|
|
|
|
my $channel = shift; |
13350
|
0
|
|
|
|
|
|
my $message = shift; |
13351
|
0
|
|
|
|
|
|
my $done = shift; |
13352
|
|
|
|
|
|
|
|
13353
|
0
|
|
|
|
|
|
$channel->{currentSubmissionId} += 1; |
13354
|
|
|
|
|
|
|
|
13355
|
|
|
|
|
|
|
my $o = bless { |
13356
|
|
|
|
|
|
|
channel => $channel, |
13357
|
|
|
|
|
|
|
message => $message, |
13358
|
|
|
|
|
|
|
done => $done, |
13359
|
|
|
|
|
|
|
submissionId => $channel->{currentSubmissionId}, |
13360
|
0
|
|
|
|
|
|
recipients => [$channel->recipients], |
13361
|
|
|
|
|
|
|
entrustedKeys => [$channel->entrustedKeys], |
13362
|
|
|
|
|
|
|
expires => CDS->now + $channel->validity, |
13363
|
|
|
|
|
|
|
}; |
13364
|
|
|
|
|
|
|
|
13365
|
|
|
|
|
|
|
# Add the current envelope hash to the obsolete hashes |
13366
|
0
|
|
|
|
|
|
my $item = $channel->item; |
13367
|
0
|
0
|
|
|
|
|
$channel->{obsoleteHashes}->{$item->envelopeHash->bytes} = $item->envelopeHash if $item->envelopeHash; |
13368
|
0
|
|
|
|
|
|
$o->{obsoleteHashesSnapshot} = [values %{$channel->{obsoleteHashes}}]; |
|
0
|
|
|
|
|
|
|
13369
|
|
|
|
|
|
|
|
13370
|
|
|
|
|
|
|
# Create an envelope |
13371
|
0
|
|
|
|
|
|
my $publicKeys = []; |
13372
|
0
|
|
|
|
|
|
push @$publicKeys, $channel->{actor}->keyPair->publicKey; |
13373
|
0
|
|
|
|
|
|
push @$publicKeys, map { $_->publicKey } @{$o->{recipients}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
13374
|
0
|
|
|
|
|
|
push @$publicKeys, @{$o->{entrustedKeys}}; |
|
0
|
|
|
|
|
|
|
13375
|
0
|
|
|
|
|
|
$o->{envelopeObject} = $channel->{actor}->keyPair->createMessageEnvelope($channel->{actor}->messagingStoreUrl, $message, $publicKeys, $o->{expires})->toObject; |
13376
|
0
|
|
|
|
|
|
$o->{envelopeHash} = $o->{envelopeObject}->calculateHash; |
13377
|
|
|
|
|
|
|
|
13378
|
|
|
|
|
|
|
# Set the new item and wait until it gets saved |
13379
|
0
|
|
|
|
|
|
$channel->{unsaved}->startSaving; |
13380
|
0
|
|
|
|
|
|
$channel->{unsaved}->savingState->addDataSavedHandler($o); |
13381
|
0
|
|
|
|
|
|
$channel->{actor}->sentList->unsaved->state->merge($channel->{unsaved}->savingState); |
13382
|
0
|
|
|
|
|
|
$item->set($o->{expires}, $o->{envelopeHash}, $message); |
13383
|
0
|
|
|
|
|
|
$channel->{unsaved}->savingDone; |
13384
|
|
|
|
|
|
|
|
13385
|
0
|
|
|
|
|
|
return $o; |
13386
|
|
|
|
|
|
|
} |
13387
|
|
|
|
|
|
|
|
13388
|
0
|
|
|
0
|
|
|
sub channel { shift->{channel} } |
13389
|
0
|
|
|
0
|
|
|
sub message { shift->{message} } |
13390
|
|
|
|
|
|
|
sub recipients { |
13391
|
0
|
|
|
0
|
|
|
my $o = shift; |
13392
|
0
|
|
|
|
|
|
@{$o->{recipients}} } |
|
0
|
|
|
|
|
|
|
13393
|
|
|
|
|
|
|
sub entrustedKeys { |
13394
|
0
|
|
|
0
|
|
|
my $o = shift; |
13395
|
0
|
|
|
|
|
|
@{$o->{entrustedKeys}} } |
|
0
|
|
|
|
|
|
|
13396
|
0
|
|
|
0
|
|
|
sub expires { shift->{expires} } |
13397
|
0
|
|
|
0
|
|
|
sub envelopeObject { shift->{envelopeObject} } |
13398
|
0
|
|
|
0
|
|
|
sub envelopeHash { shift->{envelopeHash} } |
13399
|
|
|
|
|
|
|
|
13400
|
|
|
|
|
|
|
sub onDataSaved { |
13401
|
0
|
|
|
0
|
|
|
my $o = shift; |
13402
|
|
|
|
|
|
|
|
13403
|
|
|
|
|
|
|
# If we are not the head any more, give up |
13404
|
0
|
0
|
|
|
|
|
return $o->{done}->onMessageChannelSubmissionCancelled if $o->{submissionId} != $o->{channel}->{currentSubmissionId}; |
13405
|
0
|
|
|
|
|
|
$o->{channel}->{obsoleteHashes}->{$o->{envelopeHash}->bytes} = $o->{envelopeHash}; |
13406
|
|
|
|
|
|
|
|
13407
|
|
|
|
|
|
|
# Process all recipients |
13408
|
0
|
|
|
|
|
|
my $succeeded = 0; |
13409
|
0
|
|
|
|
|
|
my $failed = 0; |
13410
|
0
|
|
|
|
|
|
for my $recipient (@{$o->{recipients}}) { |
|
0
|
|
|
|
|
|
|
13411
|
0
|
|
|
|
|
|
my $modifications = CDS::StoreModifications->new; |
13412
|
|
|
|
|
|
|
|
13413
|
|
|
|
|
|
|
# Prepare the list of removals |
13414
|
0
|
|
|
|
|
|
my $removals = []; |
13415
|
0
|
|
|
|
|
|
for my $hash (@{$o->{obsoleteHashesSnapshot}}) { |
|
0
|
|
|
|
|
|
|
13416
|
0
|
|
|
|
|
|
$modifications->remove($recipient->publicKey->hash, 'messages', $hash); |
13417
|
|
|
|
|
|
|
} |
13418
|
|
|
|
|
|
|
|
13419
|
|
|
|
|
|
|
# Add the message entry |
13420
|
0
|
|
|
|
|
|
$modifications->add($recipient->publicKey->hash, 'messages', $o->{envelopeHash}, $o->{envelopeObject}); |
13421
|
0
|
|
|
|
|
|
my $error = $recipient->store->modify($modifications, $o->{channel}->{actor}->keyPair); |
13422
|
|
|
|
|
|
|
|
13423
|
0
|
0
|
|
|
|
|
if (defined $error) { |
13424
|
0
|
|
|
|
|
|
$failed += 1; |
13425
|
0
|
|
|
|
|
|
$o->{done}->onMessageChannelSubmissionRecipientFailed($recipient, $error); |
13426
|
|
|
|
|
|
|
} else { |
13427
|
0
|
|
|
|
|
|
$succeeded += 1; |
13428
|
0
|
|
|
|
|
|
$o->{done}->onMessageChannelSubmissionRecipientDone($recipient); |
13429
|
|
|
|
|
|
|
} |
13430
|
|
|
|
|
|
|
} |
13431
|
|
|
|
|
|
|
|
13432
|
0
|
0
|
0
|
|
|
|
if ($failed == 0 || scalar keys %{$o->{obsoleteHashes}} > 64) { |
|
0
|
|
|
|
|
|
|
13433
|
0
|
|
|
|
|
|
for my $hash (@{$o->{obsoleteHashesSnapshot}}) { |
|
0
|
|
|
|
|
|
|
13434
|
0
|
|
|
|
|
|
delete $o->{channel}->{obsoleteHashes}->{$hash->bytes}; |
13435
|
|
|
|
|
|
|
} |
13436
|
|
|
|
|
|
|
} |
13437
|
|
|
|
|
|
|
|
13438
|
0
|
|
|
|
|
|
$o->{done}->onMessageChannelSubmissionDone($succeeded, $failed); |
13439
|
|
|
|
|
|
|
} |
13440
|
|
|
|
|
|
|
|
13441
|
|
|
|
|
|
|
package CDS::MissingObject; |
13442
|
|
|
|
|
|
|
|
13443
|
|
|
|
|
|
|
sub new { |
13444
|
0
|
|
|
0
|
|
|
my $class = shift; |
13445
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
13446
|
0
|
|
|
|
|
|
my $store = shift; |
13447
|
|
|
|
|
|
|
|
13448
|
0
|
|
|
|
|
|
return bless {hash => $hash, store => $store, path => [], context => undef}; |
13449
|
|
|
|
|
|
|
} |
13450
|
|
|
|
|
|
|
|
13451
|
0
|
|
|
0
|
|
|
sub hash { shift->{hash} } |
13452
|
0
|
|
|
0
|
|
|
sub store { shift->{store} } |
13453
|
|
|
|
|
|
|
sub path { |
13454
|
0
|
|
|
0
|
|
|
my $o = shift; |
13455
|
0
|
|
|
|
|
|
@{$o->{path}} } |
|
0
|
|
|
|
|
|
|
13456
|
0
|
|
|
0
|
|
|
sub context { shift->{context} } |
13457
|
|
|
|
|
|
|
|
13458
|
|
|
|
|
|
|
package CDS::NewAnnounce; |
13459
|
|
|
|
|
|
|
|
13460
|
|
|
|
|
|
|
sub new { |
13461
|
0
|
|
|
0
|
|
|
my $class = shift; |
13462
|
0
|
|
|
|
|
|
my $messagingStore = shift; |
13463
|
|
|
|
|
|
|
|
13464
|
0
|
|
|
|
|
|
my $o = bless { |
13465
|
|
|
|
|
|
|
messagingStore => $messagingStore, |
13466
|
|
|
|
|
|
|
unsaved => CDS::Unsaved->new($messagingStore->store), |
13467
|
|
|
|
|
|
|
transfers => [], |
13468
|
|
|
|
|
|
|
card => CDS::Record->new, |
13469
|
|
|
|
|
|
|
}; |
13470
|
|
|
|
|
|
|
|
13471
|
0
|
|
|
|
|
|
my $publicKey = $messagingStore->actor->keyPair->publicKey; |
13472
|
0
|
|
|
|
|
|
$o->{card}->add('public key')->addHash($publicKey->hash); |
13473
|
0
|
|
|
|
|
|
$o->addObject($publicKey->hash, $publicKey->object); |
13474
|
0
|
|
|
|
|
|
return $o; |
13475
|
|
|
|
|
|
|
} |
13476
|
|
|
|
|
|
|
|
13477
|
0
|
|
|
0
|
|
|
sub messagingStore { shift->{messagingStore} } |
13478
|
0
|
|
|
0
|
|
|
sub card { shift->{card} } |
13479
|
|
|
|
|
|
|
|
13480
|
|
|
|
|
|
|
sub addObject { |
13481
|
0
|
|
|
0
|
|
|
my $o = shift; |
13482
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
13483
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
13484
|
|
|
|
|
|
|
|
13485
|
0
|
|
|
|
|
|
$o->{unsaved}->state->addObject($hash, $object); |
13486
|
|
|
|
|
|
|
} |
13487
|
|
|
|
|
|
|
|
13488
|
|
|
|
|
|
|
sub addTransfer { |
13489
|
0
|
|
|
0
|
|
|
my $o = shift; |
13490
|
0
|
|
|
|
|
|
my $hashes = shift; |
13491
|
0
|
|
|
|
|
|
my $sourceStore = shift; |
13492
|
0
|
|
|
|
|
|
my $context = shift; |
13493
|
|
|
|
|
|
|
|
13494
|
0
|
0
|
|
|
|
|
return if ! scalar @$hashes; |
13495
|
0
|
|
|
|
|
|
push @{$o->{transfers}}, {hashes => $hashes, sourceStore => $sourceStore, context => $context}; |
|
0
|
|
|
|
|
|
|
13496
|
|
|
|
|
|
|
} |
13497
|
|
|
|
|
|
|
|
13498
|
|
|
|
|
|
|
sub addActorGroup { |
13499
|
0
|
|
|
0
|
|
|
my $o = shift; |
13500
|
0
|
|
|
|
|
|
my $actorGroupBuilder = shift; |
13501
|
|
|
|
|
|
|
|
13502
|
0
|
|
|
|
|
|
$actorGroupBuilder->addToRecord($o->{card}, 0); |
13503
|
|
|
|
|
|
|
} |
13504
|
|
|
|
|
|
|
|
13505
|
|
|
|
|
|
|
sub submit { |
13506
|
0
|
|
|
0
|
|
|
my $o = shift; |
13507
|
|
|
|
|
|
|
|
13508
|
0
|
|
|
|
|
|
my $keyPair = $o->{messagingStore}->actor->keyPair; |
13509
|
|
|
|
|
|
|
|
13510
|
|
|
|
|
|
|
# Create the public card |
13511
|
0
|
|
|
|
|
|
my $cardObject = $o->{card}->toObject; |
13512
|
0
|
|
|
|
|
|
my $cardHash = $cardObject->calculateHash; |
13513
|
0
|
|
|
|
|
|
$o->addObject($cardHash, $cardObject); |
13514
|
|
|
|
|
|
|
|
13515
|
|
|
|
|
|
|
# Prepare the public envelope |
13516
|
0
|
|
|
|
|
|
my $me = $keyPair->publicKey->hash; |
13517
|
0
|
|
|
|
|
|
my $envelopeObject = $keyPair->createPublicEnvelope($cardHash)->toObject; |
13518
|
0
|
|
|
|
|
|
my $envelopeHash = $envelopeObject->calculateHash; |
13519
|
0
|
|
|
|
|
|
$o->addTransfer([$cardHash], $o->{unsaved}, 'Announcing'); |
13520
|
|
|
|
|
|
|
|
13521
|
|
|
|
|
|
|
# Transfer all trees |
13522
|
0
|
|
|
|
|
|
for my $transfer (@{$o->{transfers}}) { |
|
0
|
|
|
|
|
|
|
13523
|
0
|
|
|
|
|
|
my ($missingObject, $store, $error) = $keyPair->transfer($transfer->{hashes}, $transfer->{sourceStore}, $o->{messagingStore}->store); |
13524
|
0
|
0
|
|
|
|
|
return if defined $error; |
13525
|
|
|
|
|
|
|
|
13526
|
0
|
0
|
|
|
|
|
if ($missingObject) { |
13527
|
0
|
|
|
|
|
|
$missingObject->{context} = $transfer->{context}; |
13528
|
0
|
|
|
|
|
|
return undef, $missingObject; |
13529
|
|
|
|
|
|
|
} |
13530
|
|
|
|
|
|
|
} |
13531
|
|
|
|
|
|
|
|
13532
|
|
|
|
|
|
|
# Prepare a modification |
13533
|
0
|
|
|
|
|
|
my $modifications = CDS::StoreModifications->new; |
13534
|
0
|
|
|
|
|
|
$modifications->add($me, 'public', $envelopeHash, $envelopeObject); |
13535
|
|
|
|
|
|
|
|
13536
|
|
|
|
|
|
|
# List the current cards to remove them |
13537
|
|
|
|
|
|
|
# Ignore errors, in the worst case, we are going to have multiple entries in the public box |
13538
|
0
|
|
|
|
|
|
my ($hashes, $error) = $o->{messagingStore}->store->list($me, 'public', 0, $keyPair); |
13539
|
0
|
0
|
|
|
|
|
if ($hashes) { |
13540
|
0
|
|
|
|
|
|
for my $hash (@$hashes) { |
13541
|
0
|
|
|
|
|
|
$modifications->remove($me, 'public', $hash); |
13542
|
|
|
|
|
|
|
} |
13543
|
|
|
|
|
|
|
} |
13544
|
|
|
|
|
|
|
|
13545
|
|
|
|
|
|
|
# Modify the public box |
13546
|
0
|
|
|
|
|
|
my $modifyError = $o->{messagingStore}->store->modify($modifications, $keyPair); |
13547
|
0
|
0
|
|
|
|
|
return if defined $modifyError; |
13548
|
0
|
|
|
|
|
|
return $envelopeHash, $cardHash; |
13549
|
|
|
|
|
|
|
} |
13550
|
|
|
|
|
|
|
|
13551
|
|
|
|
|
|
|
package CDS::NewMessagingStore; |
13552
|
|
|
|
|
|
|
|
13553
|
|
|
|
|
|
|
sub new { |
13554
|
0
|
|
|
0
|
|
|
my $class = shift; |
13555
|
0
|
|
|
|
|
|
my $actor = shift; |
13556
|
0
|
|
|
|
|
|
my $store = shift; |
13557
|
|
|
|
|
|
|
|
13558
|
0
|
|
|
|
|
|
return bless { |
13559
|
|
|
|
|
|
|
actor => $actor, |
13560
|
|
|
|
|
|
|
store => $store, |
13561
|
|
|
|
|
|
|
}; |
13562
|
|
|
|
|
|
|
} |
13563
|
|
|
|
|
|
|
|
13564
|
0
|
|
|
0
|
|
|
sub actor { shift->{actor} } |
13565
|
0
|
|
|
0
|
|
|
sub store { shift->{store} } |
13566
|
|
|
|
|
|
|
|
13567
|
|
|
|
|
|
|
# A Condensation object. |
13568
|
|
|
|
|
|
|
# A valid object starts with a 4-byte length (big-endian), followed by 32 * length bytes of hashes, followed by 0 or more bytes of data. |
13569
|
|
|
|
|
|
|
package CDS::Object; |
13570
|
|
|
|
|
|
|
|
13571
|
0
|
|
|
0
|
|
|
sub emptyHeader { "\0\0\0\0" } |
13572
|
|
|
|
|
|
|
|
13573
|
|
|
|
|
|
|
sub create { |
13574
|
0
|
|
|
0
|
|
|
my $class = shift; |
13575
|
0
|
|
|
|
|
|
my $header = shift; |
13576
|
0
|
|
|
|
|
|
my $data = shift; |
13577
|
|
|
|
|
|
|
|
13578
|
0
|
0
|
|
|
|
|
return if length $header < 4; |
13579
|
0
|
|
|
|
|
|
my $hashesCount = unpack('L>', substr($header, 0, 4)); |
13580
|
0
|
0
|
|
|
|
|
return if length $header != 4 + $hashesCount * 32; |
13581
|
0
|
|
|
|
|
|
return bless { |
13582
|
|
|
|
|
|
|
bytes => $header.$data, |
13583
|
|
|
|
|
|
|
hashesCount => $hashesCount, |
13584
|
|
|
|
|
|
|
header => $header, |
13585
|
|
|
|
|
|
|
data => $data |
13586
|
|
|
|
|
|
|
}; |
13587
|
|
|
|
|
|
|
} |
13588
|
|
|
|
|
|
|
|
13589
|
|
|
|
|
|
|
sub fromBytes { |
13590
|
0
|
|
|
0
|
|
|
my $class = shift; |
13591
|
0
|
|
0
|
|
|
|
my $bytes = shift // return; |
13592
|
|
|
|
|
|
|
|
13593
|
0
|
0
|
|
|
|
|
return if length $bytes < 4; |
13594
|
|
|
|
|
|
|
|
13595
|
0
|
|
|
|
|
|
my $hashesCount = unpack 'L>', substr($bytes, 0, 4); |
13596
|
0
|
|
|
|
|
|
my $dataStart = $hashesCount * 32 + 4; |
13597
|
0
|
0
|
|
|
|
|
return if $dataStart > length $bytes; |
13598
|
|
|
|
|
|
|
|
13599
|
0
|
|
|
|
|
|
return bless { |
13600
|
|
|
|
|
|
|
bytes => $bytes, |
13601
|
|
|
|
|
|
|
hashesCount => $hashesCount, |
13602
|
|
|
|
|
|
|
header => substr($bytes, 0, $dataStart), |
13603
|
|
|
|
|
|
|
data => substr($bytes, $dataStart) |
13604
|
|
|
|
|
|
|
}; |
13605
|
|
|
|
|
|
|
} |
13606
|
|
|
|
|
|
|
|
13607
|
|
|
|
|
|
|
sub fromFile { |
13608
|
0
|
|
|
0
|
|
|
my $class = shift; |
13609
|
0
|
|
|
|
|
|
my $file = shift; |
13610
|
|
|
|
|
|
|
|
13611
|
0
|
|
|
|
|
|
return $class->fromBytes(CDS->readBytesFromFile($file)); |
13612
|
|
|
|
|
|
|
} |
13613
|
|
|
|
|
|
|
|
13614
|
0
|
|
|
0
|
|
|
sub bytes { shift->{bytes} } |
13615
|
0
|
|
|
0
|
|
|
sub header { shift->{header} } |
13616
|
0
|
|
|
0
|
|
|
sub data { shift->{data} } |
13617
|
0
|
|
|
0
|
|
|
sub hashesCount { shift->{hashesCount} } |
13618
|
|
|
|
|
|
|
sub byteLength { |
13619
|
0
|
|
|
0
|
|
|
my $o = shift; |
13620
|
0
|
|
|
|
|
|
length($o->{header}) + length($o->{data}) } |
13621
|
|
|
|
|
|
|
|
13622
|
|
|
|
|
|
|
sub calculateHash { |
13623
|
0
|
|
|
0
|
|
|
my $o = shift; |
13624
|
|
|
|
|
|
|
|
13625
|
0
|
|
|
|
|
|
return CDS::Hash->calculateFor($o->{bytes}); |
13626
|
|
|
|
|
|
|
} |
13627
|
|
|
|
|
|
|
|
13628
|
|
|
|
|
|
|
sub hashes { |
13629
|
0
|
|
|
0
|
|
|
my $o = shift; |
13630
|
|
|
|
|
|
|
|
13631
|
0
|
|
|
|
|
|
return map { CDS::Hash->fromBytes(substr($o->{header}, $_ * 32 + 4, 32)) } 0 .. $o->{hashesCount} - 1; |
|
0
|
|
|
|
|
|
|
13632
|
|
|
|
|
|
|
} |
13633
|
|
|
|
|
|
|
|
13634
|
|
|
|
|
|
|
sub hashAtIndex { |
13635
|
0
|
|
|
0
|
|
|
my $o = shift; |
13636
|
0
|
|
0
|
|
|
|
my $index = shift // return; |
13637
|
|
|
|
|
|
|
|
13638
|
0
|
0
|
0
|
|
|
|
return if $index < 0 || $index >= $o->{hashesCount}; |
13639
|
0
|
|
|
|
|
|
return CDS::Hash->fromBytes(substr($o->{header}, $index * 32 + 4, 32)); |
13640
|
|
|
|
|
|
|
} |
13641
|
|
|
|
|
|
|
|
13642
|
|
|
|
|
|
|
sub crypt { |
13643
|
0
|
|
|
0
|
|
|
my $o = shift; |
13644
|
0
|
|
|
|
|
|
my $key = shift; |
13645
|
|
|
|
|
|
|
|
13646
|
0
|
|
|
|
|
|
return CDS::Object->create($o->{header}, CDS::C::aesCrypt($o->{data}, $key, CDS->zeroCTR)); |
13647
|
|
|
|
|
|
|
} |
13648
|
|
|
|
|
|
|
|
13649
|
|
|
|
|
|
|
sub writeToFile { |
13650
|
0
|
|
|
0
|
|
|
my $o = shift; |
13651
|
0
|
|
|
|
|
|
my $file = shift; |
13652
|
|
|
|
|
|
|
|
13653
|
0
|
|
|
|
|
|
return CDS->writeBytesToFile($file, $o->{bytes}); |
13654
|
|
|
|
|
|
|
} |
13655
|
|
|
|
|
|
|
|
13656
|
|
|
|
|
|
|
# A store using a cache store to deliver frequently accessed objects faster, and a backend store. |
13657
|
|
|
|
|
|
|
package CDS::ObjectCache; |
13658
|
|
|
|
|
|
|
|
13659
|
1
|
|
|
1
|
|
5693
|
use parent -norequire, 'CDS::Store'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
13660
|
|
|
|
|
|
|
|
13661
|
|
|
|
|
|
|
sub new { |
13662
|
0
|
|
|
0
|
|
|
my $class = shift; |
13663
|
0
|
|
|
|
|
|
my $backend = shift; |
13664
|
0
|
|
|
|
|
|
my $cache = shift; |
13665
|
|
|
|
|
|
|
|
13666
|
0
|
|
|
|
|
|
return bless { |
13667
|
|
|
|
|
|
|
id => "Object Cache\n".$backend->id."\n".$cache->id, |
13668
|
|
|
|
|
|
|
backend => $backend, |
13669
|
|
|
|
|
|
|
cache => $cache, |
13670
|
|
|
|
|
|
|
}; |
13671
|
|
|
|
|
|
|
} |
13672
|
|
|
|
|
|
|
|
13673
|
0
|
|
|
0
|
|
|
sub id { shift->{id} } |
13674
|
0
|
|
|
0
|
|
|
sub backend { shift->{backend} } |
13675
|
0
|
|
|
0
|
|
|
sub cache { shift->{cache} } |
13676
|
|
|
|
|
|
|
|
13677
|
|
|
|
|
|
|
sub get { |
13678
|
0
|
|
|
0
|
|
|
my $o = shift; |
13679
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
13680
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
13681
|
|
|
|
|
|
|
|
13682
|
0
|
|
|
|
|
|
my $objectFromCache = $o->{cache}->get($hash); |
13683
|
0
|
0
|
|
|
|
|
return $objectFromCache if $objectFromCache; |
13684
|
|
|
|
|
|
|
|
13685
|
0
|
|
|
|
|
|
my ($object, $error) = $o->{backend}->get($hash, $keyPair); |
13686
|
0
|
0
|
|
|
|
|
return undef, $error if ! defined $object; |
13687
|
0
|
|
|
|
|
|
$o->{cache}->put($hash, $object, undef); |
13688
|
0
|
|
|
|
|
|
return $object; |
13689
|
|
|
|
|
|
|
} |
13690
|
|
|
|
|
|
|
|
13691
|
|
|
|
|
|
|
sub put { |
13692
|
0
|
|
|
0
|
|
|
my $o = shift; |
13693
|
|
|
|
|
|
|
|
13694
|
|
|
|
|
|
|
# The important thing is that the backend succeeds. The cache is a nice-to-have. |
13695
|
0
|
|
|
|
|
|
$o->{cache}->put(@_); |
13696
|
0
|
|
|
|
|
|
return $o->{backend}->put(@_); |
13697
|
|
|
|
|
|
|
} |
13698
|
|
|
|
|
|
|
|
13699
|
|
|
|
|
|
|
sub book { |
13700
|
0
|
|
|
0
|
|
|
my $o = shift; |
13701
|
|
|
|
|
|
|
|
13702
|
|
|
|
|
|
|
# The important thing is that the backend succeeds. The cache is a nice-to-have. |
13703
|
0
|
|
|
|
|
|
$o->{cache}->book(@_); |
13704
|
0
|
|
|
|
|
|
return $o->{backend}->book(@_); |
13705
|
|
|
|
|
|
|
} |
13706
|
|
|
|
|
|
|
|
13707
|
|
|
|
|
|
|
sub list { |
13708
|
0
|
|
|
0
|
|
|
my $o = shift; |
13709
|
|
|
|
|
|
|
|
13710
|
|
|
|
|
|
|
# Just pass this through to the backend. |
13711
|
0
|
|
|
|
|
|
return $o->{backend}->list(@_); |
13712
|
|
|
|
|
|
|
} |
13713
|
|
|
|
|
|
|
|
13714
|
|
|
|
|
|
|
sub add { |
13715
|
0
|
|
|
0
|
|
|
my $o = shift; |
13716
|
|
|
|
|
|
|
|
13717
|
|
|
|
|
|
|
# Just pass this through to the backend. |
13718
|
0
|
|
|
|
|
|
return $o->{backend}->add(@_); |
13719
|
|
|
|
|
|
|
} |
13720
|
|
|
|
|
|
|
|
13721
|
|
|
|
|
|
|
sub remove { |
13722
|
0
|
|
|
0
|
|
|
my $o = shift; |
13723
|
|
|
|
|
|
|
|
13724
|
|
|
|
|
|
|
# Just pass this through to the backend. |
13725
|
0
|
|
|
|
|
|
return $o->{backend}->remove(@_); |
13726
|
|
|
|
|
|
|
} |
13727
|
|
|
|
|
|
|
|
13728
|
|
|
|
|
|
|
sub modify { |
13729
|
0
|
|
|
0
|
|
|
my $o = shift; |
13730
|
|
|
|
|
|
|
|
13731
|
|
|
|
|
|
|
# Just pass this through to the backend. |
13732
|
0
|
|
|
|
|
|
return $o->{backend}->modify(@_); |
13733
|
|
|
|
|
|
|
} |
13734
|
|
|
|
|
|
|
|
13735
|
|
|
|
|
|
|
# The result of parsing an OBJECTFILE token (see Token.pm). |
13736
|
|
|
|
|
|
|
package CDS::ObjectFileToken; |
13737
|
|
|
|
|
|
|
|
13738
|
|
|
|
|
|
|
sub new { |
13739
|
0
|
|
|
0
|
|
|
my $class = shift; |
13740
|
0
|
|
|
|
|
|
my $file = shift; |
13741
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
13742
|
|
|
|
|
|
|
|
13743
|
0
|
|
|
|
|
|
return bless { |
13744
|
|
|
|
|
|
|
file => $file, |
13745
|
|
|
|
|
|
|
object => $object, |
13746
|
|
|
|
|
|
|
}; |
13747
|
|
|
|
|
|
|
} |
13748
|
|
|
|
|
|
|
|
13749
|
0
|
|
|
0
|
|
|
sub file { shift->{file} } |
13750
|
0
|
|
|
0
|
|
|
sub object { shift->{object} } |
13751
|
|
|
|
|
|
|
|
13752
|
|
|
|
|
|
|
# The result of parsing an OBJECT token. |
13753
|
|
|
|
|
|
|
package CDS::ObjectToken; |
13754
|
|
|
|
|
|
|
|
13755
|
|
|
|
|
|
|
sub new { |
13756
|
0
|
|
|
0
|
|
|
my $class = shift; |
13757
|
0
|
|
|
|
|
|
my $cliStore = shift; |
13758
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
13759
|
|
|
|
|
|
|
|
13760
|
0
|
|
|
|
|
|
return bless { |
13761
|
|
|
|
|
|
|
cliStore => $cliStore, |
13762
|
|
|
|
|
|
|
hash => $hash, |
13763
|
|
|
|
|
|
|
}; |
13764
|
|
|
|
|
|
|
} |
13765
|
|
|
|
|
|
|
|
13766
|
0
|
|
|
0
|
|
|
sub cliStore { shift->{cliStore} } |
13767
|
0
|
|
|
0
|
|
|
sub hash { shift->{hash} } |
13768
|
|
|
|
|
|
|
sub url { |
13769
|
0
|
|
|
0
|
|
|
my $o = shift; |
13770
|
0
|
|
|
|
|
|
$o->{cliStore}->url.'/objects/'.$o->{hash}->hex } |
13771
|
|
|
|
|
|
|
|
13772
|
|
|
|
|
|
|
package CDS::Parser; |
13773
|
|
|
|
|
|
|
|
13774
|
|
|
|
|
|
|
sub new { |
13775
|
0
|
|
|
0
|
|
|
my $class = shift; |
13776
|
0
|
|
|
|
|
|
my $actor = shift; |
13777
|
0
|
|
|
|
|
|
my $command = shift; |
13778
|
|
|
|
|
|
|
|
13779
|
0
|
|
|
|
|
|
my $start = CDS::Parser::Node->new(0); |
13780
|
0
|
|
|
|
|
|
return bless { |
13781
|
|
|
|
|
|
|
actor => $actor, |
13782
|
|
|
|
|
|
|
ui => $actor->ui, |
13783
|
|
|
|
|
|
|
start => $start, |
13784
|
|
|
|
|
|
|
states => [CDS::Parser::State->new($start)], |
13785
|
|
|
|
|
|
|
command => $command, |
13786
|
|
|
|
|
|
|
}; |
13787
|
|
|
|
|
|
|
} |
13788
|
|
|
|
|
|
|
|
13789
|
0
|
|
|
0
|
|
|
sub actor { shift->{actor} } |
13790
|
0
|
|
|
0
|
|
|
sub start { shift->{start} } |
13791
|
|
|
|
|
|
|
|
13792
|
|
|
|
|
|
|
sub execute { |
13793
|
0
|
|
|
0
|
|
|
my $o = shift; |
13794
|
|
|
|
|
|
|
|
13795
|
0
|
|
|
|
|
|
my $processed = [$o->{command}]; |
13796
|
0
|
|
|
|
|
|
for my $arg (@_) { |
13797
|
0
|
0
|
|
|
|
|
return $o->howToContinue($processed) if $arg eq '?'; |
13798
|
0
|
0
|
|
|
|
|
return $o->explain if $arg eq '??'; |
13799
|
0
|
|
|
|
|
|
my $token = CDS::Parser::Token->new($o->{actor}, $arg); |
13800
|
0
|
|
|
|
|
|
$o->advance($token); |
13801
|
0
|
0
|
|
|
|
|
return $o->invalid($processed, $token) if ! scalar @{$o->{states}}; |
|
0
|
|
|
|
|
|
|
13802
|
0
|
|
|
|
|
|
push @$processed, $arg; |
13803
|
|
|
|
|
|
|
} |
13804
|
|
|
|
|
|
|
|
13805
|
0
|
|
|
|
|
|
my @results = grep { $_->runHandler } @{$o->{states}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
13806
|
0
|
0
|
|
|
|
|
return $o->howToContinue($processed) if ! scalar @results; |
13807
|
|
|
|
|
|
|
|
13808
|
0
|
|
|
|
|
|
my $maxWeight = 0; |
13809
|
0
|
|
|
|
|
|
for my $result (@results) { |
13810
|
0
|
0
|
|
|
|
|
$maxWeight = $result->cumulativeWeight if $maxWeight < $result->cumulativeWeight; |
13811
|
|
|
|
|
|
|
} |
13812
|
|
|
|
|
|
|
|
13813
|
0
|
|
|
|
|
|
@results = grep { $_->cumulativeWeight == $maxWeight } @results; |
|
0
|
|
|
|
|
|
|
13814
|
0
|
0
|
|
|
|
|
return $o->ambiguous if scalar @results > 1; |
13815
|
|
|
|
|
|
|
|
13816
|
0
|
|
|
|
|
|
my $result = shift @results; |
13817
|
0
|
|
|
|
|
|
my $handler = $result->runHandler; |
13818
|
0
|
|
|
|
|
|
my $instance = &{$handler->{constructor}}(undef, $o->{actor}); |
|
0
|
|
|
|
|
|
|
13819
|
0
|
|
|
|
|
|
&{$handler->{function}}($instance, $result); |
|
0
|
|
|
|
|
|
|
13820
|
|
|
|
|
|
|
} |
13821
|
|
|
|
|
|
|
|
13822
|
|
|
|
|
|
|
sub advance { |
13823
|
0
|
|
|
0
|
|
|
my $o = shift; |
13824
|
0
|
|
|
|
|
|
my $token = shift; |
13825
|
|
|
|
|
|
|
|
13826
|
0
|
|
|
|
|
|
$o->{previousStates} = $o->{states}; |
13827
|
0
|
|
|
|
|
|
$o->{states} = []; |
13828
|
0
|
|
|
|
|
|
for my $state (@{$o->{previousStates}}) { |
|
0
|
|
|
|
|
|
|
13829
|
0
|
|
|
|
|
|
push @{$o->{states}}, $state->advance($token); |
|
0
|
|
|
|
|
|
|
13830
|
|
|
|
|
|
|
} |
13831
|
|
|
|
|
|
|
} |
13832
|
|
|
|
|
|
|
|
13833
|
|
|
|
|
|
|
sub showCompletions { |
13834
|
0
|
|
|
0
|
|
|
my $o = shift; |
13835
|
0
|
|
|
|
|
|
my $cmd = shift; |
13836
|
|
|
|
|
|
|
|
13837
|
|
|
|
|
|
|
# Parse the command line |
13838
|
0
|
|
|
|
|
|
my $state = ''; |
13839
|
0
|
|
|
|
|
|
my $arg = ''; |
13840
|
0
|
|
|
|
|
|
my @args; |
13841
|
0
|
|
|
|
|
|
for my $c (split //, $cmd) { |
13842
|
0
|
0
|
|
|
|
|
if ($state eq '') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
13843
|
0
|
0
|
|
|
|
|
if ($c eq ' ') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
13844
|
0
|
0
|
|
|
|
|
push @args, $arg if length $arg; |
13845
|
0
|
|
|
|
|
|
$arg = ''; |
13846
|
|
|
|
|
|
|
} elsif ($c eq '\'') { |
13847
|
0
|
0
|
|
|
|
|
push @args, $arg if length $arg; |
13848
|
0
|
|
|
|
|
|
$arg = ''; |
13849
|
0
|
|
|
|
|
|
$state = '\''; |
13850
|
|
|
|
|
|
|
} elsif ($c eq '"') { |
13851
|
0
|
0
|
|
|
|
|
push @args, $arg if length $arg; |
13852
|
0
|
|
|
|
|
|
$arg = ''; |
13853
|
0
|
|
|
|
|
|
$state = '"'; |
13854
|
|
|
|
|
|
|
} elsif ($c eq '\\') { |
13855
|
0
|
|
|
|
|
|
$state = '\\'; |
13856
|
|
|
|
|
|
|
} else { |
13857
|
0
|
|
|
|
|
|
$arg .= $c; |
13858
|
|
|
|
|
|
|
} |
13859
|
|
|
|
|
|
|
} elsif ($state eq '\\') { |
13860
|
0
|
|
|
|
|
|
$arg .= $c; |
13861
|
0
|
|
|
|
|
|
$state = ''; |
13862
|
|
|
|
|
|
|
} elsif ($state eq '\'') { |
13863
|
0
|
0
|
|
|
|
|
if ($c eq '\'') { |
13864
|
0
|
0
|
|
|
|
|
push @args, $arg if length $arg; |
13865
|
0
|
|
|
|
|
|
$arg = ''; |
13866
|
0
|
|
|
|
|
|
$state = ''; |
13867
|
|
|
|
|
|
|
} else { |
13868
|
0
|
|
|
|
|
|
$arg .= $c; |
13869
|
|
|
|
|
|
|
} |
13870
|
|
|
|
|
|
|
} elsif ($state eq '"') { |
13871
|
0
|
0
|
|
|
|
|
if ($c eq '"') { |
|
|
0
|
|
|
|
|
|
13872
|
0
|
0
|
|
|
|
|
push @args, $arg if length $arg; |
13873
|
0
|
|
|
|
|
|
$arg = ''; |
13874
|
0
|
|
|
|
|
|
$state = ''; |
13875
|
|
|
|
|
|
|
} elsif ($c eq '\\') { |
13876
|
0
|
|
|
|
|
|
$state = '"\\'; |
13877
|
|
|
|
|
|
|
} else { |
13878
|
0
|
|
|
|
|
|
$arg .= $c; |
13879
|
|
|
|
|
|
|
} |
13880
|
|
|
|
|
|
|
} elsif ($state eq '\\"') { |
13881
|
0
|
|
|
|
|
|
$arg .= $c; |
13882
|
0
|
|
|
|
|
|
$state = '"'; |
13883
|
|
|
|
|
|
|
} |
13884
|
|
|
|
|
|
|
} |
13885
|
|
|
|
|
|
|
|
13886
|
|
|
|
|
|
|
# Use the last token to complete |
13887
|
0
|
|
|
|
|
|
my $lastToken = CDS::Parser::Token->new($o->{actor}, $arg); |
13888
|
|
|
|
|
|
|
|
13889
|
|
|
|
|
|
|
# Look for possible states |
13890
|
0
|
|
|
|
|
|
shift @args; |
13891
|
0
|
|
|
|
|
|
for my $arg (@args) { |
13892
|
0
|
0
|
|
|
|
|
return if $arg eq '?'; |
13893
|
0
|
|
|
|
|
|
$o->advance(CDS::Parser::Token->new($o->{actor}, $arg)); |
13894
|
|
|
|
|
|
|
} |
13895
|
|
|
|
|
|
|
|
13896
|
|
|
|
|
|
|
# Complete the last token |
13897
|
0
|
|
|
|
|
|
my %possibilities; |
13898
|
0
|
|
|
|
|
|
for my $state (@{$o->{states}}) { |
|
0
|
|
|
|
|
|
|
13899
|
0
|
|
|
|
|
|
for my $possibility ($state->complete($lastToken)) { |
13900
|
0
|
|
|
|
|
|
$possibilities{$possibility} = 1; |
13901
|
|
|
|
|
|
|
} |
13902
|
|
|
|
|
|
|
} |
13903
|
|
|
|
|
|
|
|
13904
|
|
|
|
|
|
|
# Print all possibilities |
13905
|
0
|
|
|
|
|
|
for my $possibility (keys %possibilities) { |
13906
|
0
|
|
|
|
|
|
print $possibility, "\n"; |
13907
|
|
|
|
|
|
|
} |
13908
|
|
|
|
|
|
|
} |
13909
|
|
|
|
|
|
|
|
13910
|
|
|
|
|
|
|
sub ambiguous { |
13911
|
0
|
|
|
0
|
|
|
my $o = shift; |
13912
|
|
|
|
|
|
|
|
13913
|
0
|
|
|
|
|
|
$o->{ui}->space; |
13914
|
0
|
|
|
|
|
|
$o->{ui}->pRed('Your query is ambiguous. This is an error in the command grammar.'); |
13915
|
0
|
|
|
|
|
|
$o->explain; |
13916
|
|
|
|
|
|
|
} |
13917
|
|
|
|
|
|
|
|
13918
|
|
|
|
|
|
|
sub explain { |
13919
|
0
|
|
|
0
|
|
|
my $o = shift; |
13920
|
|
|
|
|
|
|
|
13921
|
0
|
0
|
|
|
|
|
for my $interpretation (sort { $b->cumulativeWeight <=> $a->cumulativeWeight || $b->isExecutable <=> $a->isExecutable } @{$o->{states}}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
13922
|
0
|
|
|
|
|
|
$o->{ui}->space; |
13923
|
0
|
0
|
|
|
|
|
$o->{ui}->title('Interpretation with weight ', $interpretation->cumulativeWeight, $interpretation->isExecutable ? $o->{ui}->green(' (executable)') : $o->{ui}->orange(' (incomplete)')); |
13924
|
0
|
|
|
|
|
|
$o->showTuples($interpretation->path); |
13925
|
|
|
|
|
|
|
} |
13926
|
|
|
|
|
|
|
|
13927
|
0
|
|
|
|
|
|
$o->{ui}->space; |
13928
|
|
|
|
|
|
|
} |
13929
|
|
|
|
|
|
|
|
13930
|
|
|
|
|
|
|
sub showTuples { |
13931
|
0
|
|
|
0
|
|
|
my $o = shift; |
13932
|
|
|
|
|
|
|
|
13933
|
0
|
|
|
|
|
|
for my $state (@_) { |
13934
|
0
|
|
|
|
|
|
my $label = $state->label; |
13935
|
0
|
|
|
|
|
|
my $value = $state->value; |
13936
|
|
|
|
|
|
|
|
13937
|
0
|
|
|
|
|
|
my $valueRef = ref $value; |
13938
|
0
|
0
|
0
|
|
|
|
my $valueText = |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
13939
|
|
|
|
|
|
|
$valueRef eq '' ? $value // '' : |
13940
|
|
|
|
|
|
|
$valueRef eq 'CDS::Hash' ? $value->hex : |
13941
|
|
|
|
|
|
|
$valueRef eq 'CDS::ErrorHandlingStore' ? $value->url : |
13942
|
|
|
|
|
|
|
$valueRef eq 'CDS::AccountToken' ? $value->actorHash->hex . ' on ' . $value->cliStore->url : |
13943
|
|
|
|
|
|
|
$valueRef; |
13944
|
0
|
0
|
|
|
|
|
$o->{ui}->line($o->{ui}->left(12, $label), $state->collectHandler ? $valueText : $o->{ui}->gray($valueText)); |
13945
|
|
|
|
|
|
|
} |
13946
|
|
|
|
|
|
|
} |
13947
|
|
|
|
|
|
|
|
13948
|
|
|
|
|
|
|
sub cmd { |
13949
|
0
|
|
|
0
|
|
|
my $o = shift; |
13950
|
0
|
|
|
|
|
|
my $processed = shift; |
13951
|
|
|
|
|
|
|
|
13952
|
0
|
|
|
|
|
|
my $cmd = join(' ', map { $_ =~ s/(\\|'|")/\\$1/g ; $_ } @$processed); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
13953
|
0
|
0
|
|
|
|
|
$cmd = '…'.substr($cmd, length($cmd) - 20, 20) if length $cmd > 30; |
13954
|
0
|
|
|
|
|
|
return $cmd; |
13955
|
|
|
|
|
|
|
} |
13956
|
|
|
|
|
|
|
|
13957
|
|
|
|
|
|
|
sub howToContinue { |
13958
|
0
|
|
|
0
|
|
|
my $o = shift; |
13959
|
0
|
|
|
|
|
|
my $processed = shift; |
13960
|
|
|
|
|
|
|
|
13961
|
0
|
|
|
|
|
|
my $cmd = $o->cmd($processed); |
13962
|
|
|
|
|
|
|
#$o->displayWarnings($o->{states}); |
13963
|
0
|
|
|
|
|
|
$o->{ui}->space; |
13964
|
0
|
|
|
|
|
|
for my $possibility (CDS::Parser::Continuations->collect($o->{states})) { |
13965
|
0
|
|
|
|
|
|
$o->{ui}->line($o->{ui}->gray($cmd), $possibility); |
13966
|
|
|
|
|
|
|
} |
13967
|
0
|
|
|
|
|
|
$o->{ui}->space; |
13968
|
|
|
|
|
|
|
} |
13969
|
|
|
|
|
|
|
|
13970
|
|
|
|
|
|
|
sub invalid { |
13971
|
0
|
|
|
0
|
|
|
my $o = shift; |
13972
|
0
|
|
|
|
|
|
my $processed = shift; |
13973
|
0
|
|
|
|
|
|
my $invalid = shift; |
13974
|
|
|
|
|
|
|
|
13975
|
0
|
|
|
|
|
|
my $cmd = $o->cmd($processed); |
13976
|
0
|
|
|
|
|
|
$o->displayWarnings($o->{previousStates}); |
13977
|
0
|
|
|
|
|
|
$o->{ui}->space; |
13978
|
|
|
|
|
|
|
|
13979
|
0
|
|
|
|
|
|
$o->{ui}->line($o->{ui}->gray($cmd), ' ', $o->{ui}->red($invalid->{text})); |
13980
|
0
|
0
|
|
|
|
|
if (scalar @{$invalid->{warnings}}) { |
|
0
|
|
|
|
|
|
|
13981
|
0
|
|
|
|
|
|
for my $warning (@{$invalid->{warnings}}) { |
|
0
|
|
|
|
|
|
|
13982
|
0
|
|
|
|
|
|
$o->{ui}->warning($warning); |
13983
|
|
|
|
|
|
|
} |
13984
|
|
|
|
|
|
|
} |
13985
|
|
|
|
|
|
|
|
13986
|
0
|
|
|
|
|
|
$o->{ui}->space; |
13987
|
0
|
|
|
|
|
|
$o->{ui}->title('Possible continuations'); |
13988
|
0
|
|
|
|
|
|
for my $possibility (CDS::Parser::Continuations->collect($o->{previousStates})) { |
13989
|
0
|
|
|
|
|
|
$o->{ui}->line($o->{ui}->gray($cmd), $possibility); |
13990
|
|
|
|
|
|
|
} |
13991
|
0
|
|
|
|
|
|
$o->{ui}->space; |
13992
|
|
|
|
|
|
|
} |
13993
|
|
|
|
|
|
|
|
13994
|
|
|
|
|
|
|
sub displayWarnings { |
13995
|
0
|
|
|
0
|
|
|
my $o = shift; |
13996
|
0
|
|
|
|
|
|
my $states = shift; |
13997
|
|
|
|
|
|
|
|
13998
|
0
|
|
|
|
|
|
for my $state (@$states) { |
13999
|
0
|
|
|
|
|
|
my $current = $state; |
14000
|
0
|
|
|
|
|
|
while ($current) { |
14001
|
0
|
|
|
|
|
|
for my $warning (@{$current->{warnings}}) { |
|
0
|
|
|
|
|
|
|
14002
|
0
|
|
|
|
|
|
$o->{ui}->warning($warning); |
14003
|
|
|
|
|
|
|
} |
14004
|
0
|
|
|
|
|
|
$current = $current->{previous}; |
14005
|
|
|
|
|
|
|
} |
14006
|
|
|
|
|
|
|
} |
14007
|
|
|
|
|
|
|
} |
14008
|
|
|
|
|
|
|
|
14009
|
|
|
|
|
|
|
# An arrow points from one node to another. The arrow is taken in State::advance if the next argument matches to the label. |
14010
|
|
|
|
|
|
|
package CDS::Parser::Arrow; |
14011
|
|
|
|
|
|
|
|
14012
|
|
|
|
|
|
|
sub new { |
14013
|
0
|
|
|
0
|
|
|
my $class = shift; |
14014
|
0
|
|
|
|
|
|
my $node = shift; |
14015
|
0
|
|
|
|
|
|
my $official = shift; |
14016
|
0
|
|
|
|
|
|
my $weight = shift; |
14017
|
0
|
|
|
|
|
|
my $label = shift; |
14018
|
0
|
|
|
|
|
|
my $handler = shift; |
14019
|
|
|
|
|
|
|
|
14020
|
0
|
|
|
|
|
|
return bless { |
14021
|
|
|
|
|
|
|
node => $node, # target node |
14022
|
|
|
|
|
|
|
official => $official, # whether to show this arrow with '?' |
14023
|
|
|
|
|
|
|
weight => $weight, # weight |
14024
|
|
|
|
|
|
|
label => $label, # label |
14025
|
|
|
|
|
|
|
handler => $handler, # handler to invoke if we take this arrow |
14026
|
|
|
|
|
|
|
}; |
14027
|
|
|
|
|
|
|
} |
14028
|
|
|
|
|
|
|
|
14029
|
|
|
|
|
|
|
package CDS::Parser::Continuations; |
14030
|
|
|
|
|
|
|
|
14031
|
|
|
|
|
|
|
sub collect { |
14032
|
0
|
|
|
0
|
|
|
my $class = shift; |
14033
|
0
|
|
|
|
|
|
my $states = shift; |
14034
|
|
|
|
|
|
|
|
14035
|
0
|
|
|
|
|
|
my $o = bless {possibilities => {}}; |
14036
|
|
|
|
|
|
|
|
14037
|
0
|
|
|
|
|
|
my $visitedNodes = {}; |
14038
|
0
|
|
|
|
|
|
for my $state (@$states) { |
14039
|
0
|
|
|
|
|
|
$o->visit($visitedNodes, $state->node, ''); |
14040
|
|
|
|
|
|
|
} |
14041
|
|
|
|
|
|
|
|
14042
|
0
|
|
|
|
|
|
for my $possibility (keys %{$o->{possibilities}}) { |
|
0
|
|
|
|
|
|
|
14043
|
0
|
0
|
|
|
|
|
delete $o->{possibilities}->{$possibility} if exists $o->{possibilities}->{$possibility.' …'}; |
14044
|
|
|
|
|
|
|
} |
14045
|
|
|
|
|
|
|
|
14046
|
0
|
|
|
|
|
|
return sort keys %{$o->{possibilities}}; |
|
0
|
|
|
|
|
|
|
14047
|
|
|
|
|
|
|
} |
14048
|
|
|
|
|
|
|
|
14049
|
|
|
|
|
|
|
sub visit { |
14050
|
0
|
|
|
0
|
|
|
my $o = shift; |
14051
|
0
|
|
|
|
|
|
my $visitedNodes = shift; |
14052
|
0
|
|
|
|
|
|
my $node = shift; |
14053
|
0
|
|
|
|
|
|
my $text = shift; |
14054
|
|
|
|
|
|
|
|
14055
|
0
|
|
|
|
|
|
$visitedNodes->{$node} = 1; |
14056
|
|
|
|
|
|
|
|
14057
|
0
|
|
|
|
|
|
my $arrows = []; |
14058
|
0
|
|
|
|
|
|
$node->collectArrows($arrows); |
14059
|
|
|
|
|
|
|
|
14060
|
0
|
|
|
|
|
|
for my $arrow (@$arrows) { |
14061
|
0
|
0
|
|
|
|
|
next if ! $arrow->{official}; |
14062
|
|
|
|
|
|
|
|
14063
|
0
|
|
|
|
|
|
my $text = $text.' '.$arrow->{label}; |
14064
|
0
|
0
|
|
|
|
|
$o->{possibilities}->{$text} = 1 if $arrow->{node}->hasHandler; |
14065
|
0
|
0
|
0
|
|
|
|
if ($arrow->{node}->endProposals || exists $visitedNodes->{$arrow->{node}}) { |
14066
|
0
|
0
|
|
|
|
|
$o->{possibilities}->{$text . ($o->canContinue($arrow->{node}) ? ' …' : '')} = 1; |
14067
|
0
|
|
|
|
|
|
next; |
14068
|
|
|
|
|
|
|
} |
14069
|
|
|
|
|
|
|
|
14070
|
0
|
|
|
|
|
|
$o->visit($visitedNodes, $arrow->{node}, $text); |
14071
|
|
|
|
|
|
|
} |
14072
|
|
|
|
|
|
|
|
14073
|
0
|
|
|
|
|
|
delete $visitedNodes->{$node}; |
14074
|
|
|
|
|
|
|
} |
14075
|
|
|
|
|
|
|
|
14076
|
|
|
|
|
|
|
sub canContinue { |
14077
|
0
|
|
|
0
|
|
|
my $o = shift; |
14078
|
0
|
|
|
|
|
|
my $node = shift; |
14079
|
|
|
|
|
|
|
|
14080
|
0
|
|
|
|
|
|
my $arrows = []; |
14081
|
0
|
|
|
|
|
|
$node->collectArrows($arrows); |
14082
|
|
|
|
|
|
|
|
14083
|
0
|
|
|
|
|
|
for my $arrow (@$arrows) { |
14084
|
0
|
0
|
|
|
|
|
next if ! $arrow->{official}; |
14085
|
0
|
|
|
|
|
|
return 1; |
14086
|
|
|
|
|
|
|
} |
14087
|
|
|
|
|
|
|
|
14088
|
0
|
|
|
|
|
|
return; |
14089
|
|
|
|
|
|
|
} |
14090
|
|
|
|
|
|
|
|
14091
|
|
|
|
|
|
|
# Nodes and arrows define the graph on which the parse state can move. |
14092
|
|
|
|
|
|
|
package CDS::Parser::Node; |
14093
|
|
|
|
|
|
|
|
14094
|
|
|
|
|
|
|
sub new { |
14095
|
0
|
|
|
0
|
|
|
my $class = shift; |
14096
|
0
|
|
|
|
|
|
my $endProposals = shift; |
14097
|
0
|
|
|
|
|
|
my $handler = shift; |
14098
|
|
|
|
|
|
|
|
14099
|
0
|
|
|
|
|
|
return bless { |
14100
|
|
|
|
|
|
|
arrows => [], # outgoing arrows |
14101
|
|
|
|
|
|
|
defaults => [], # default nodes, at which the current state could be as well |
14102
|
|
|
|
|
|
|
endProposals => $endProposals, # if set, the proposal search algorithm stops at this node |
14103
|
|
|
|
|
|
|
handler => $handler, # handler to be executed if parsing ends here |
14104
|
|
|
|
|
|
|
}; |
14105
|
|
|
|
|
|
|
} |
14106
|
|
|
|
|
|
|
|
14107
|
0
|
|
|
0
|
|
|
sub endProposals { shift->{endProposals} } |
14108
|
|
|
|
|
|
|
|
14109
|
|
|
|
|
|
|
# Adds an arrow. |
14110
|
|
|
|
|
|
|
sub addArrow { |
14111
|
0
|
|
|
0
|
|
|
my $o = shift; |
14112
|
0
|
|
|
|
|
|
my $to = shift; |
14113
|
0
|
|
|
|
|
|
my $official = shift; |
14114
|
0
|
|
|
|
|
|
my $weight = shift; |
14115
|
0
|
|
|
|
|
|
my $label = shift; |
14116
|
0
|
|
|
|
|
|
my $handler = shift; |
14117
|
|
|
|
|
|
|
|
14118
|
0
|
|
|
|
|
|
push @{$o->{arrows}}, CDS::Parser::Arrow->new($to, $official, $weight, $label, $handler); |
|
0
|
|
|
|
|
|
|
14119
|
|
|
|
|
|
|
} |
14120
|
|
|
|
|
|
|
|
14121
|
|
|
|
|
|
|
# Adds a default node. |
14122
|
|
|
|
|
|
|
sub addDefault { |
14123
|
0
|
|
|
0
|
|
|
my $o = shift; |
14124
|
0
|
|
|
|
|
|
my $node = shift; |
14125
|
|
|
|
|
|
|
|
14126
|
0
|
|
|
|
|
|
push @{$o->{defaults}}, $node; |
|
0
|
|
|
|
|
|
|
14127
|
|
|
|
|
|
|
} |
14128
|
|
|
|
|
|
|
|
14129
|
|
|
|
|
|
|
sub collectArrows { |
14130
|
0
|
|
|
0
|
|
|
my $o = shift; |
14131
|
0
|
|
|
|
|
|
my $arrows = shift; |
14132
|
|
|
|
|
|
|
|
14133
|
0
|
|
|
|
|
|
push @$arrows, @{$o->{arrows}}; |
|
0
|
|
|
|
|
|
|
14134
|
0
|
|
|
|
|
|
for my $default (@{$o->{defaults}}) { $default->collectArrows($arrows); } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
14135
|
|
|
|
|
|
|
} |
14136
|
|
|
|
|
|
|
|
14137
|
|
|
|
|
|
|
sub hasHandler { |
14138
|
0
|
|
|
0
|
|
|
my $o = shift; |
14139
|
|
|
|
|
|
|
|
14140
|
0
|
0
|
|
|
|
|
return 1 if $o->{handler}; |
14141
|
0
|
0
|
|
|
|
|
for my $default (@{$o->{defaults}}) { return 1 if $default->hasHandler; } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
14142
|
0
|
|
|
|
|
|
return; |
14143
|
|
|
|
|
|
|
} |
14144
|
|
|
|
|
|
|
|
14145
|
|
|
|
|
|
|
sub getHandler { |
14146
|
0
|
|
|
0
|
|
|
my $o = shift; |
14147
|
|
|
|
|
|
|
|
14148
|
0
|
0
|
|
|
|
|
return $o->{handler} if $o->{handler}; |
14149
|
0
|
|
|
|
|
|
for my $default (@{$o->{defaults}}) { |
|
0
|
|
|
|
|
|
|
14150
|
0
|
|
0
|
|
|
|
my $handler = $default->getHandler // next; |
14151
|
0
|
|
|
|
|
|
return $handler; |
14152
|
|
|
|
|
|
|
} |
14153
|
0
|
|
|
|
|
|
return; |
14154
|
|
|
|
|
|
|
} |
14155
|
|
|
|
|
|
|
|
14156
|
|
|
|
|
|
|
# A parser state denotes a possible current state (after having parsed a certain number of arguments). |
14157
|
|
|
|
|
|
|
# A parser keeps track of multiple states. When advancing, a state may disappear (if no possibility exists), or fan out (if multiple possibilities exist). |
14158
|
|
|
|
|
|
|
# A state is immutable. |
14159
|
|
|
|
|
|
|
package CDS::Parser::State; |
14160
|
|
|
|
|
|
|
|
14161
|
|
|
|
|
|
|
sub new { |
14162
|
0
|
|
|
0
|
|
|
my $class = shift; |
14163
|
0
|
|
|
|
|
|
my $node = shift; |
14164
|
0
|
|
|
|
|
|
my $previous = shift; |
14165
|
0
|
|
|
|
|
|
my $arrow = shift; |
14166
|
0
|
|
|
|
|
|
my $value = shift; |
14167
|
0
|
|
|
|
|
|
my $warnings = shift; |
14168
|
|
|
|
|
|
|
|
14169
|
|
|
|
|
|
|
return bless { |
14170
|
|
|
|
|
|
|
node => $node, # current node |
14171
|
|
|
|
|
|
|
previous => $previous, # previous state |
14172
|
|
|
|
|
|
|
arrow => $arrow, # the arrow we took to get here |
14173
|
|
|
|
|
|
|
value => $value, # the value we collected with the last arrow |
14174
|
|
|
|
|
|
|
warnings => $warnings, # the warnings we collected with the last arrow |
14175
|
0
|
0
|
|
|
|
|
cumulativeWeight => ($previous ? $previous->cumulativeWeight : 0) + ($arrow ? $arrow->{weight} : 0), # the weight we collected until here |
|
|
0
|
|
|
|
|
|
14176
|
|
|
|
|
|
|
}; |
14177
|
|
|
|
|
|
|
} |
14178
|
|
|
|
|
|
|
|
14179
|
0
|
|
|
0
|
|
|
sub node { shift->{node} } |
14180
|
|
|
|
|
|
|
sub runHandler { |
14181
|
0
|
|
|
0
|
|
|
my $o = shift; |
14182
|
0
|
|
|
|
|
|
$o->{node}->getHandler } |
14183
|
|
|
|
|
|
|
sub isExecutable { |
14184
|
0
|
|
|
0
|
|
|
my $o = shift; |
14185
|
0
|
0
|
|
|
|
|
$o->{node}->getHandler ? 1 : 0 } |
14186
|
|
|
|
|
|
|
sub collectHandler { |
14187
|
0
|
|
|
0
|
|
|
my $o = shift; |
14188
|
0
|
0
|
|
|
|
|
$o->{arrow} ? $o->{arrow}->{handler} : undef } |
14189
|
|
|
|
|
|
|
sub label { |
14190
|
0
|
|
|
0
|
|
|
my $o = shift; |
14191
|
0
|
0
|
|
|
|
|
$o->{arrow} ? $o->{arrow}->{label} : 'cds' } |
14192
|
0
|
|
|
0
|
|
|
sub value { shift->{value} } |
14193
|
0
|
|
|
0
|
|
|
sub arrow { shift->{arrow} } |
14194
|
0
|
|
|
0
|
|
|
sub cumulativeWeight { shift->{cumulativeWeight} } |
14195
|
|
|
|
|
|
|
|
14196
|
|
|
|
|
|
|
sub advance { |
14197
|
0
|
|
|
0
|
|
|
my $o = shift; |
14198
|
0
|
|
|
|
|
|
my $token = shift; |
14199
|
|
|
|
|
|
|
|
14200
|
0
|
|
|
|
|
|
my $arrows = []; |
14201
|
0
|
|
|
|
|
|
$o->{node}->collectArrows($arrows); |
14202
|
|
|
|
|
|
|
|
14203
|
|
|
|
|
|
|
# Let the token know what possibilities we have |
14204
|
0
|
|
|
|
|
|
for my $arrow (@$arrows) { |
14205
|
0
|
|
|
|
|
|
$token->prepare($arrow->{label}); |
14206
|
|
|
|
|
|
|
} |
14207
|
|
|
|
|
|
|
|
14208
|
|
|
|
|
|
|
# Ask the token to interpret the text |
14209
|
0
|
|
|
|
|
|
my @states; |
14210
|
0
|
|
|
|
|
|
for my $arrow (@$arrows) { |
14211
|
0
|
|
0
|
|
|
|
my $value = $token->as($arrow->{label}) // next; |
14212
|
0
|
|
|
|
|
|
push @states, CDS::Parser::State->new($arrow->{node}, $o, $arrow, $value, $token->{warnings}); |
14213
|
|
|
|
|
|
|
} |
14214
|
|
|
|
|
|
|
|
14215
|
0
|
|
|
|
|
|
return @states; |
14216
|
|
|
|
|
|
|
} |
14217
|
|
|
|
|
|
|
|
14218
|
|
|
|
|
|
|
sub complete { |
14219
|
0
|
|
|
0
|
|
|
my $o = shift; |
14220
|
0
|
|
|
|
|
|
my $token = shift; |
14221
|
|
|
|
|
|
|
|
14222
|
0
|
|
|
|
|
|
my $arrows = []; |
14223
|
0
|
|
|
|
|
|
$o->{node}->collectArrows($arrows); |
14224
|
|
|
|
|
|
|
|
14225
|
|
|
|
|
|
|
# Let the token know what possibilities we have |
14226
|
0
|
|
|
|
|
|
for my $arrow (@$arrows) { |
14227
|
0
|
0
|
|
|
|
|
next if ! $arrow->{official}; |
14228
|
0
|
|
|
|
|
|
$token->prepare($arrow->{label}); |
14229
|
|
|
|
|
|
|
} |
14230
|
|
|
|
|
|
|
|
14231
|
|
|
|
|
|
|
# Ask the token to interpret the text |
14232
|
0
|
|
|
|
|
|
for my $arrow (@$arrows) { |
14233
|
0
|
0
|
|
|
|
|
next if ! $arrow->{official}; |
14234
|
0
|
|
|
|
|
|
$token->complete($arrow->{label}); |
14235
|
|
|
|
|
|
|
} |
14236
|
|
|
|
|
|
|
|
14237
|
0
|
|
|
|
|
|
return @{$token->{possibilities}}; |
|
0
|
|
|
|
|
|
|
14238
|
|
|
|
|
|
|
} |
14239
|
|
|
|
|
|
|
|
14240
|
|
|
|
|
|
|
sub arrows { |
14241
|
0
|
|
|
0
|
|
|
my $o = shift; |
14242
|
|
|
|
|
|
|
|
14243
|
0
|
|
|
|
|
|
my $arrows = []; |
14244
|
0
|
|
|
|
|
|
$o->{node}->collectArrows($arrows); |
14245
|
0
|
|
|
|
|
|
return @$arrows; |
14246
|
|
|
|
|
|
|
} |
14247
|
|
|
|
|
|
|
|
14248
|
|
|
|
|
|
|
sub path { |
14249
|
0
|
|
|
0
|
|
|
my $o = shift; |
14250
|
|
|
|
|
|
|
|
14251
|
0
|
|
|
|
|
|
my @path; |
14252
|
0
|
|
|
|
|
|
my $state = $o; |
14253
|
0
|
|
|
|
|
|
while ($state) { |
14254
|
0
|
|
|
|
|
|
unshift @path, $state; |
14255
|
0
|
|
|
|
|
|
$state = $state->{previous}; |
14256
|
|
|
|
|
|
|
} |
14257
|
0
|
|
|
|
|
|
return @path; |
14258
|
|
|
|
|
|
|
} |
14259
|
|
|
|
|
|
|
|
14260
|
|
|
|
|
|
|
sub collect { |
14261
|
0
|
|
|
0
|
|
|
my $o = shift; |
14262
|
0
|
|
|
|
|
|
my $data = shift; |
14263
|
|
|
|
|
|
|
|
14264
|
0
|
|
|
|
|
|
for my $state ($o->path) { |
14265
|
0
|
|
0
|
|
|
|
my $collectHandler = $state->collectHandler // next; |
14266
|
0
|
|
|
|
|
|
&$collectHandler($data, $state->label, $state->value); |
14267
|
|
|
|
|
|
|
} |
14268
|
|
|
|
|
|
|
} |
14269
|
|
|
|
|
|
|
|
14270
|
|
|
|
|
|
|
package CDS::Parser::Token; |
14271
|
|
|
|
|
|
|
|
14272
|
|
|
|
|
|
|
sub new { |
14273
|
0
|
|
|
0
|
|
|
my $class = shift; |
14274
|
0
|
|
|
|
|
|
my $actor = shift; |
14275
|
0
|
|
|
|
|
|
my $text = shift; |
14276
|
|
|
|
|
|
|
|
14277
|
0
|
|
|
|
|
|
return bless { |
14278
|
|
|
|
|
|
|
actor => $actor, |
14279
|
|
|
|
|
|
|
text => $text, |
14280
|
|
|
|
|
|
|
keywords => {}, |
14281
|
|
|
|
|
|
|
cache => {}, |
14282
|
|
|
|
|
|
|
warnings => [], |
14283
|
|
|
|
|
|
|
possibilities => [], |
14284
|
|
|
|
|
|
|
}; |
14285
|
|
|
|
|
|
|
} |
14286
|
|
|
|
|
|
|
|
14287
|
|
|
|
|
|
|
sub prepare { |
14288
|
0
|
|
|
0
|
|
|
my $o = shift; |
14289
|
0
|
|
|
|
|
|
my $expect = shift; |
14290
|
|
|
|
|
|
|
|
14291
|
0
|
0
|
|
|
|
|
$o->{keywords}->{$expect} = 1 if $expect =~ /^[a-z0-9]*$/; |
14292
|
|
|
|
|
|
|
} |
14293
|
|
|
|
|
|
|
|
14294
|
|
|
|
|
|
|
sub as { |
14295
|
0
|
|
|
0
|
|
|
my $o = shift; |
14296
|
0
|
|
|
|
|
|
my $expect = shift; |
14297
|
0
|
0
|
|
|
|
|
exists $o->{cache}->{$expect} ? $o->{cache}->{$expect} : $o->{cache}->{$expect} = $o->produce($expect) } |
14298
|
|
|
|
|
|
|
|
14299
|
|
|
|
|
|
|
sub produce { |
14300
|
0
|
|
|
0
|
|
|
my $o = shift; |
14301
|
0
|
|
|
|
|
|
my $expect = shift; |
14302
|
|
|
|
|
|
|
|
14303
|
0
|
0
|
|
|
|
|
return $o->account if $expect eq 'ACCOUNT'; |
14304
|
0
|
0
|
|
|
|
|
return $o->hash if $expect eq 'ACTOR'; |
14305
|
0
|
0
|
|
|
|
|
return $o->actorGroup if $expect eq 'ACTORGROUP'; |
14306
|
0
|
0
|
|
|
|
|
return $o->aesKey if $expect eq 'AESKEY'; |
14307
|
0
|
0
|
|
|
|
|
return $o->box if $expect eq 'BOX'; |
14308
|
0
|
0
|
|
|
|
|
return $o->boxLabel if $expect eq 'BOXLABEL'; |
14309
|
0
|
0
|
|
|
|
|
return $o->file if $expect eq 'FILE'; |
14310
|
0
|
0
|
|
|
|
|
return $o->filename if $expect eq 'FILENAME'; |
14311
|
0
|
0
|
|
|
|
|
return $o->folder if $expect eq 'FOLDER'; |
14312
|
0
|
0
|
|
|
|
|
return $o->foldername if $expect eq 'FOLDERNAME'; |
14313
|
0
|
0
|
|
|
|
|
return $o->group if $expect eq 'GROUP'; |
14314
|
0
|
0
|
|
|
|
|
return $o->hash if $expect eq 'HASH'; |
14315
|
0
|
0
|
|
|
|
|
return $o->keyPair if $expect eq 'KEYPAIR'; |
14316
|
0
|
0
|
|
|
|
|
return $o->label if $expect eq 'LABEL'; |
14317
|
0
|
0
|
|
|
|
|
return $o->object if $expect eq 'OBJECT'; |
14318
|
0
|
0
|
|
|
|
|
return $o->objectFile if $expect eq 'OBJECTFILE'; |
14319
|
0
|
0
|
|
|
|
|
return $o->port if $expect eq 'PORT'; |
14320
|
0
|
0
|
|
|
|
|
return $o->store if $expect eq 'STORE'; |
14321
|
0
|
0
|
|
|
|
|
return $o->text if $expect eq 'TEXT'; |
14322
|
0
|
0
|
|
|
|
|
return $o->user if $expect eq 'USER'; |
14323
|
0
|
0
|
|
|
|
|
return $o->{text} eq $expect ? '' : undef; |
14324
|
|
|
|
|
|
|
} |
14325
|
|
|
|
|
|
|
|
14326
|
|
|
|
|
|
|
sub complete { |
14327
|
0
|
|
|
0
|
|
|
my $o = shift; |
14328
|
0
|
|
|
|
|
|
my $expect = shift; |
14329
|
|
|
|
|
|
|
|
14330
|
0
|
0
|
|
|
|
|
return $o->completeAccount if $expect eq 'ACCOUNT'; |
14331
|
0
|
0
|
|
|
|
|
return $o->completeHash if $expect eq 'ACTOR'; |
14332
|
0
|
0
|
|
|
|
|
return $o->completeActorGroup if $expect eq 'ACTORGROUP'; |
14333
|
0
|
0
|
|
|
|
|
return if $expect eq 'AESKEY'; |
14334
|
0
|
0
|
|
|
|
|
return $o->completeBox if $expect eq 'BOX'; |
14335
|
0
|
0
|
|
|
|
|
return $o->completeBoxLabel if $expect eq 'BOXLABEL'; |
14336
|
0
|
0
|
|
|
|
|
return $o->completeFile if $expect eq 'FILE'; |
14337
|
0
|
0
|
|
|
|
|
return $o->completeFile if $expect eq 'FILENAME'; |
14338
|
0
|
0
|
|
|
|
|
return $o->completeFolder if $expect eq 'FOLDER'; |
14339
|
0
|
0
|
|
|
|
|
return $o->completeFolder if $expect eq 'FOLDERNAME'; |
14340
|
0
|
0
|
|
|
|
|
return $o->completeGroup if $expect eq 'GROUP'; |
14341
|
0
|
0
|
|
|
|
|
return $o->completeHash if $expect eq 'HASH'; |
14342
|
0
|
0
|
|
|
|
|
return $o->completeKeyPair if $expect eq 'KEYPAIR'; |
14343
|
0
|
0
|
|
|
|
|
return $o->completeLabel if $expect eq 'LABEL'; |
14344
|
0
|
0
|
|
|
|
|
return $o->completeObject if $expect eq 'OBJECT'; |
14345
|
0
|
0
|
|
|
|
|
return $o->completeObjectFile if $expect eq 'OBJECTFILE'; |
14346
|
0
|
0
|
|
|
|
|
return $o->completeStoreUrl if $expect eq 'STORE'; |
14347
|
0
|
0
|
|
|
|
|
return $o->completeUser if $expect eq 'USER'; |
14348
|
0
|
0
|
|
|
|
|
return if $expect eq 'TEXT'; |
14349
|
0
|
|
|
|
|
|
$o->addPossibility($expect); |
14350
|
|
|
|
|
|
|
} |
14351
|
|
|
|
|
|
|
|
14352
|
|
|
|
|
|
|
sub addPossibility { |
14353
|
0
|
|
|
0
|
|
|
my $o = shift; |
14354
|
0
|
|
|
|
|
|
my $possibility = shift; |
14355
|
|
|
|
|
|
|
|
14356
|
0
|
0
|
|
|
|
|
push @{$o->{possibilities}}, $possibility.' ' if substr($possibility, 0, length $o->{text}) eq $o->{text}; |
|
0
|
|
|
|
|
|
|
14357
|
|
|
|
|
|
|
} |
14358
|
|
|
|
|
|
|
|
14359
|
|
|
|
|
|
|
sub addPartialPossibility { |
14360
|
0
|
|
|
0
|
|
|
my $o = shift; |
14361
|
0
|
|
|
|
|
|
my $possibility = shift; |
14362
|
|
|
|
|
|
|
|
14363
|
0
|
0
|
|
|
|
|
push @{$o->{possibilities}}, $possibility if substr($possibility, 0, length $o->{text}) eq $o->{text}; |
|
0
|
|
|
|
|
|
|
14364
|
|
|
|
|
|
|
} |
14365
|
|
|
|
|
|
|
|
14366
|
|
|
|
|
|
|
sub isKeyword { |
14367
|
0
|
|
|
0
|
|
|
my $o = shift; |
14368
|
0
|
|
|
|
|
|
exists $o->{keywords}->{$o->{text}} } |
14369
|
|
|
|
|
|
|
|
14370
|
|
|
|
|
|
|
sub account { |
14371
|
0
|
|
|
0
|
|
|
my $o = shift; |
14372
|
|
|
|
|
|
|
|
14373
|
|
|
|
|
|
|
# From a remembered account |
14374
|
0
|
|
|
|
|
|
my $record = $o->{actor}->remembered($o->{text}); |
14375
|
0
|
|
|
|
|
|
my $storeUrl = $record->child('store')->textValue; |
14376
|
0
|
|
|
|
|
|
my $actorHash = CDS::Hash->fromBytes($record->child('actor')->bytesValue); |
14377
|
0
|
0
|
0
|
|
|
|
if ($actorHash && length $storeUrl) { |
14378
|
0
|
|
0
|
|
|
|
my $store = $o->{actor}->storeForUrl($storeUrl) // return $o->warning('Invalid store URL "', $storeUrl, '" in remembered account.'); |
14379
|
0
|
|
|
|
|
|
my $accountToken = CDS::AccountToken->new($store, $actorHash); |
14380
|
0
|
0
|
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as a keyword. If you mean the account, write "', $accountToken->url, '".') if $o->isKeyword; |
14381
|
0
|
|
|
|
|
|
return $accountToken; |
14382
|
|
|
|
|
|
|
} |
14383
|
|
|
|
|
|
|
|
14384
|
|
|
|
|
|
|
# From a URL |
14385
|
0
|
0
|
|
|
|
|
if ($o->{text} =~ /^\s*(.*?)\/accounts\/([0-9a-fA-F]{64,64})\/*\s*$/) { |
14386
|
0
|
|
|
|
|
|
my $storeUrl = $1; |
14387
|
0
|
|
|
|
|
|
my $actorHash = CDS::Hash->fromHex($2); |
14388
|
0
|
0
|
0
|
|
|
|
$storeUrl = 'file://'.Cwd::abs_path($storeUrl) if $storeUrl !~ /^[a-zA-Z0-9_\+-]*:/ && -d $storeUrl; |
14389
|
0
|
|
0
|
|
|
|
my $cliStore = $o->{actor}->storeForUrl($storeUrl) // return $o->warning('Invalid store URL "', $storeUrl, '".'); |
14390
|
0
|
|
|
|
|
|
return CDS::AccountToken->new($cliStore, $actorHash); |
14391
|
|
|
|
|
|
|
} |
14392
|
|
|
|
|
|
|
|
14393
|
0
|
|
|
|
|
|
return; |
14394
|
|
|
|
|
|
|
} |
14395
|
|
|
|
|
|
|
|
14396
|
|
|
|
|
|
|
sub completeAccount { |
14397
|
0
|
|
|
0
|
|
|
my $o = shift; |
14398
|
|
|
|
|
|
|
|
14399
|
0
|
|
|
|
|
|
$o->completeUrl; |
14400
|
|
|
|
|
|
|
|
14401
|
0
|
|
|
|
|
|
my $records = $o->{actor}->rememberedRecords; |
14402
|
0
|
|
|
|
|
|
for my $label (keys %$records) { |
14403
|
0
|
|
|
|
|
|
my $record = $records->{$label}; |
14404
|
0
|
|
|
|
|
|
my $storeUrl = $record->child('store')->textValue; |
14405
|
0
|
0
|
|
|
|
|
next if ! length $storeUrl; |
14406
|
0
|
|
0
|
|
|
|
my $actorHash = CDS::Hash->fromBytes($record->child('actor')->bytesValue) // next; |
14407
|
|
|
|
|
|
|
|
14408
|
0
|
|
|
|
|
|
$o->addPossibility($label); |
14409
|
0
|
|
|
|
|
|
$o->addPossibility($storeUrl.'/accounts/'.$actorHash->hex); |
14410
|
|
|
|
|
|
|
} |
14411
|
|
|
|
|
|
|
|
14412
|
0
|
|
|
|
|
|
return; |
14413
|
|
|
|
|
|
|
} |
14414
|
|
|
|
|
|
|
|
14415
|
|
|
|
|
|
|
sub aesKey { |
14416
|
0
|
|
|
0
|
|
|
my $o = shift; |
14417
|
|
|
|
|
|
|
|
14418
|
0
|
0
|
|
|
|
|
$o->{text} =~ /^[0-9A-Fa-f]{64}$/ || return; |
14419
|
0
|
|
|
|
|
|
return pack('H*', $o->{text}); |
14420
|
|
|
|
|
|
|
} |
14421
|
|
|
|
|
|
|
|
14422
|
|
|
|
|
|
|
sub box { |
14423
|
0
|
|
|
0
|
|
|
my $o = shift; |
14424
|
|
|
|
|
|
|
|
14425
|
|
|
|
|
|
|
# From a URL |
14426
|
0
|
0
|
|
|
|
|
if ($o->{text} =~ /^\s*(.*?)\/accounts\/([0-9a-fA-F]{64,64})\/(messages|private|public)\/*\s*$/) { |
14427
|
0
|
|
|
|
|
|
my $storeUrl = $1; |
14428
|
0
|
|
|
|
|
|
my $boxLabel = $3; |
14429
|
0
|
|
|
|
|
|
my $actorHash = CDS::Hash->fromHex($2); |
14430
|
0
|
0
|
0
|
|
|
|
$storeUrl = 'file://'.Cwd::abs_path($storeUrl) if $storeUrl !~ /^[a-zA-Z0-9_\+-]*:/ && -d $storeUrl; |
14431
|
0
|
|
0
|
|
|
|
my $cliStore = $o->{actor}->storeForUrl($storeUrl) // return $o->warning('Invalid store URL "', $storeUrl, '".'); |
14432
|
0
|
|
|
|
|
|
my $accountToken = CDS::AccountToken->new($cliStore, $actorHash); |
14433
|
0
|
|
|
|
|
|
return CDS::BoxToken->new($accountToken, $boxLabel); |
14434
|
|
|
|
|
|
|
} |
14435
|
|
|
|
|
|
|
|
14436
|
0
|
|
|
|
|
|
return; |
14437
|
|
|
|
|
|
|
} |
14438
|
|
|
|
|
|
|
|
14439
|
|
|
|
|
|
|
sub completeBox { |
14440
|
0
|
|
|
0
|
|
|
my $o = shift; |
14441
|
|
|
|
|
|
|
|
14442
|
0
|
|
|
|
|
|
$o->completeUrl; |
14443
|
0
|
|
|
|
|
|
return; |
14444
|
|
|
|
|
|
|
} |
14445
|
|
|
|
|
|
|
|
14446
|
|
|
|
|
|
|
sub boxLabel { |
14447
|
0
|
|
|
0
|
|
|
my $o = shift; |
14448
|
|
|
|
|
|
|
|
14449
|
0
|
0
|
|
|
|
|
return $o->{text} if $o->{text} eq 'messages'; |
14450
|
0
|
0
|
|
|
|
|
return $o->{text} if $o->{text} eq 'private'; |
14451
|
0
|
0
|
|
|
|
|
return $o->{text} if $o->{text} eq 'public'; |
14452
|
0
|
|
|
|
|
|
return; |
14453
|
|
|
|
|
|
|
} |
14454
|
|
|
|
|
|
|
|
14455
|
|
|
|
|
|
|
sub completeBoxLabel { |
14456
|
0
|
|
|
0
|
|
|
my $o = shift; |
14457
|
|
|
|
|
|
|
|
14458
|
0
|
|
|
|
|
|
$o->addPossibility('messages'); |
14459
|
0
|
|
|
|
|
|
$o->addPossibility('private'); |
14460
|
0
|
|
|
|
|
|
$o->addPossibility('public'); |
14461
|
|
|
|
|
|
|
} |
14462
|
|
|
|
|
|
|
|
14463
|
|
|
|
|
|
|
sub file { |
14464
|
0
|
|
|
0
|
|
|
my $o = shift; |
14465
|
|
|
|
|
|
|
|
14466
|
0
|
|
0
|
|
|
|
my $file = Cwd::abs_path($o->{text}) // return; |
14467
|
0
|
0
|
|
|
|
|
return if ! -f $file; |
14468
|
0
|
0
|
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as keyword. If you mean the file, write "./', $o->{text}, '".') if $o->isKeyword; |
14469
|
0
|
|
|
|
|
|
return $file; |
14470
|
|
|
|
|
|
|
} |
14471
|
|
|
|
|
|
|
|
14472
|
|
|
|
|
|
|
sub completeFile { |
14473
|
0
|
|
|
0
|
|
|
my $o = shift; |
14474
|
|
|
|
|
|
|
|
14475
|
0
|
|
|
|
|
|
my $folder = './'; |
14476
|
0
|
|
|
|
|
|
my $startFilename = $o->{text}; |
14477
|
0
|
0
|
|
|
|
|
$startFilename = $ENV{HOME}.'/'.$1 if $startFilename =~ /^~\/(.*)$/; |
14478
|
0
|
0
|
|
|
|
|
if ($startFilename eq '~') { |
|
|
0
|
|
|
|
|
|
14479
|
0
|
|
|
|
|
|
$folder = $ENV{HOME}.'/'; |
14480
|
0
|
|
|
|
|
|
$startFilename = ''; |
14481
|
|
|
|
|
|
|
} elsif ($startFilename =~ /^(.*\/)([^\/]*)$/) { |
14482
|
0
|
|
|
|
|
|
$folder = $1; |
14483
|
0
|
|
|
|
|
|
$startFilename = $2; |
14484
|
|
|
|
|
|
|
} |
14485
|
|
|
|
|
|
|
|
14486
|
0
|
|
|
|
|
|
for my $filename (CDS->listFolder($folder)) { |
14487
|
0
|
0
|
|
|
|
|
next if $filename eq '.'; |
14488
|
0
|
0
|
|
|
|
|
next if $filename eq '..'; |
14489
|
0
|
0
|
|
|
|
|
next if substr($filename, 0, length $startFilename) ne $startFilename; |
14490
|
0
|
|
|
|
|
|
my $file = $folder.$filename; |
14491
|
0
|
0
|
|
|
|
|
$file .= '/' if -d $file; |
14492
|
0
|
0
|
|
|
|
|
$file .= ' ' if -f $file; |
14493
|
0
|
|
|
|
|
|
push @{$o->{possibilities}}, $file; |
|
0
|
|
|
|
|
|
|
14494
|
|
|
|
|
|
|
} |
14495
|
|
|
|
|
|
|
} |
14496
|
|
|
|
|
|
|
|
14497
|
|
|
|
|
|
|
sub filename { |
14498
|
0
|
|
|
0
|
|
|
my $o = shift; |
14499
|
|
|
|
|
|
|
|
14500
|
0
|
0
|
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as keyword. If you mean the file, write "./', $o->{text}, '".') if $o->isKeyword; |
14501
|
0
|
|
|
|
|
|
return Cwd::abs_path($o->{text}); |
14502
|
|
|
|
|
|
|
} |
14503
|
|
|
|
|
|
|
|
14504
|
|
|
|
|
|
|
sub folder { |
14505
|
0
|
|
|
0
|
|
|
my $o = shift; |
14506
|
|
|
|
|
|
|
|
14507
|
0
|
|
0
|
|
|
|
my $folder = Cwd::abs_path($o->{text}) // return; |
14508
|
0
|
0
|
|
|
|
|
return if ! -d $folder; |
14509
|
0
|
0
|
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as keyword. If you mean the folder, write "./', $o->{text}, '".') if $o->isKeyword; |
14510
|
0
|
|
|
|
|
|
return $folder; |
14511
|
|
|
|
|
|
|
} |
14512
|
|
|
|
|
|
|
|
14513
|
|
|
|
|
|
|
sub completeFolder { |
14514
|
0
|
|
|
0
|
|
|
my $o = shift; |
14515
|
|
|
|
|
|
|
|
14516
|
0
|
|
|
|
|
|
my $folder = './'; |
14517
|
0
|
|
|
|
|
|
my $startFilename = $o->{text}; |
14518
|
0
|
0
|
|
|
|
|
if ($o->{text} =~ /^(.*\/)([^\/]*)$/) { |
14519
|
0
|
|
|
|
|
|
$folder = $1; |
14520
|
0
|
|
|
|
|
|
$startFilename = $2; |
14521
|
|
|
|
|
|
|
} |
14522
|
|
|
|
|
|
|
|
14523
|
0
|
|
|
|
|
|
for my $filename (CDS->listFolder($folder)) { |
14524
|
0
|
0
|
|
|
|
|
next if $filename eq '.'; |
14525
|
0
|
0
|
|
|
|
|
next if $filename eq '..'; |
14526
|
0
|
0
|
|
|
|
|
next if substr($filename, 0, length $startFilename) ne $startFilename; |
14527
|
0
|
|
|
|
|
|
my $file = $folder.$filename; |
14528
|
0
|
0
|
|
|
|
|
next if ! -d $file; |
14529
|
0
|
|
|
|
|
|
push @{$o->{possibilities}}, $file.'/'; |
|
0
|
|
|
|
|
|
|
14530
|
|
|
|
|
|
|
} |
14531
|
|
|
|
|
|
|
} |
14532
|
|
|
|
|
|
|
|
14533
|
|
|
|
|
|
|
sub foldername { |
14534
|
0
|
|
|
0
|
|
|
my $o = shift; |
14535
|
|
|
|
|
|
|
|
14536
|
0
|
0
|
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as keyword. If you mean the folder, write "./', $o->{text}, '".') if $o->isKeyword; |
14537
|
0
|
|
|
|
|
|
return Cwd::abs_path($o->{text}); |
14538
|
|
|
|
|
|
|
} |
14539
|
|
|
|
|
|
|
|
14540
|
|
|
|
|
|
|
sub group { |
14541
|
0
|
|
|
0
|
|
|
my $o = shift; |
14542
|
|
|
|
|
|
|
|
14543
|
0
|
0
|
|
|
|
|
return int($1) if $o->{text} =~ /^\s*(\d{1,5})\s*$/; |
14544
|
0
|
|
|
|
|
|
return getgrnam($o->{text}); |
14545
|
|
|
|
|
|
|
} |
14546
|
|
|
|
|
|
|
|
14547
|
|
|
|
|
|
|
sub completeGroup { |
14548
|
0
|
|
|
0
|
|
|
my $o = shift; |
14549
|
|
|
|
|
|
|
|
14550
|
0
|
|
|
|
|
|
while (my $name = getgrent) { |
14551
|
0
|
|
|
|
|
|
$o->addPossibility($name); |
14552
|
|
|
|
|
|
|
} |
14553
|
|
|
|
|
|
|
} |
14554
|
|
|
|
|
|
|
|
14555
|
|
|
|
|
|
|
sub hash { |
14556
|
0
|
|
|
0
|
|
|
my $o = shift; |
14557
|
|
|
|
|
|
|
|
14558
|
0
|
|
|
|
|
|
my $hash = CDS::Hash->fromHex($o->{text}); |
14559
|
0
|
0
|
|
|
|
|
return $hash if $hash; |
14560
|
|
|
|
|
|
|
|
14561
|
|
|
|
|
|
|
# Check if it's a remembered actor hash |
14562
|
0
|
|
|
|
|
|
my $record = $o->{actor}->remembered($o->{text}); |
14563
|
0
|
|
0
|
|
|
|
my $actorHash = CDS::Hash->fromBytes($record->child('actor')->bytesValue) // return; |
14564
|
0
|
0
|
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as keyword. If you mean the actor, write "', $actorHash->hex, '".') if $o->isKeyword; |
14565
|
0
|
|
|
|
|
|
return $actorHash; |
14566
|
|
|
|
|
|
|
} |
14567
|
|
|
|
|
|
|
|
14568
|
|
|
|
|
|
|
sub completeHash { |
14569
|
0
|
|
|
0
|
|
|
my $o = shift; |
14570
|
|
|
|
|
|
|
|
14571
|
0
|
|
|
|
|
|
my $records = $o->{actor}->rememberedRecords; |
14572
|
0
|
|
|
|
|
|
for my $label (keys %$records) { |
14573
|
0
|
|
|
|
|
|
my $record = $records->{$label}; |
14574
|
0
|
|
0
|
|
|
|
my $hash = CDS::Hash->fromBytes($record->child('actor')->bytesValue) // next; |
14575
|
0
|
|
|
|
|
|
$o->addPossibility($label); |
14576
|
0
|
|
|
|
|
|
$o->addPossibility($hash->hex); |
14577
|
|
|
|
|
|
|
} |
14578
|
|
|
|
|
|
|
|
14579
|
0
|
|
|
|
|
|
for my $child ($o->{actor}->actorGroupSelector->children) { |
14580
|
0
|
|
0
|
|
|
|
my $hash = $child->record->child('hash')->hashValue // next; |
14581
|
0
|
|
|
|
|
|
$o->addPossibility($hash->hex); |
14582
|
|
|
|
|
|
|
} |
14583
|
|
|
|
|
|
|
} |
14584
|
|
|
|
|
|
|
|
14585
|
|
|
|
|
|
|
sub keyPair { |
14586
|
0
|
|
|
0
|
|
|
my $o = shift; |
14587
|
|
|
|
|
|
|
|
14588
|
|
|
|
|
|
|
# Remembered key pair |
14589
|
0
|
|
|
|
|
|
my $record = $o->{actor}->remembered($o->{text}); |
14590
|
0
|
|
|
|
|
|
my $file = $record->child('key pair')->textValue; |
14591
|
|
|
|
|
|
|
|
14592
|
|
|
|
|
|
|
# Key pair from file |
14593
|
0
|
0
|
|
|
|
|
if (! length $file) { |
14594
|
0
|
|
0
|
|
|
|
$file = Cwd::abs_path($o->{text}) // return; |
14595
|
0
|
0
|
0
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as keyword. If you mean the file, write "./', $o->{text}, '".') if $o->isKeyword && -f $file; |
14596
|
|
|
|
|
|
|
} |
14597
|
|
|
|
|
|
|
|
14598
|
|
|
|
|
|
|
# Load the key pair |
14599
|
0
|
0
|
|
|
|
|
return if ! -f $file; |
14600
|
0
|
|
0
|
|
|
|
my $bytes = CDS->readBytesFromFile($file) // return $o->warning('The key pair file "', $file, '" could not be read.'); |
14601
|
0
|
|
0
|
|
|
|
my $keyPair = CDS::KeyPair->fromRecord(CDS::Record->fromObject(CDS::Object->fromBytes($bytes))) // return $o->warning('The file "', $file, '" does not contain a key pair.'); |
14602
|
0
|
|
|
|
|
|
return CDS::KeyPairToken->new($file, $keyPair); |
14603
|
|
|
|
|
|
|
} |
14604
|
|
|
|
|
|
|
|
14605
|
|
|
|
|
|
|
sub completeKeyPair { |
14606
|
0
|
|
|
0
|
|
|
my $o = shift; |
14607
|
|
|
|
|
|
|
|
14608
|
0
|
|
|
|
|
|
$o->completeFile; |
14609
|
|
|
|
|
|
|
|
14610
|
0
|
|
|
|
|
|
my $records = $o->{actor}->rememberedRecords; |
14611
|
0
|
|
|
|
|
|
for my $label (keys %$records) { |
14612
|
0
|
|
|
|
|
|
my $record = $records->{$label}; |
14613
|
0
|
0
|
|
|
|
|
next if ! length $record->child('key pair')->textValue; |
14614
|
0
|
|
|
|
|
|
$o->addPossibility($label); |
14615
|
|
|
|
|
|
|
} |
14616
|
|
|
|
|
|
|
} |
14617
|
|
|
|
|
|
|
|
14618
|
|
|
|
|
|
|
sub label { |
14619
|
0
|
|
|
0
|
|
|
my $o = shift; |
14620
|
|
|
|
|
|
|
|
14621
|
0
|
|
|
|
|
|
my $records = $o->{actor}->remembered($o->{text}); |
14622
|
0
|
0
|
|
|
|
|
return $o->{text} if $records->children; |
14623
|
0
|
|
|
|
|
|
return; |
14624
|
|
|
|
|
|
|
} |
14625
|
|
|
|
|
|
|
|
14626
|
|
|
|
|
|
|
sub completeLabel { |
14627
|
0
|
|
|
0
|
|
|
my $o = shift; |
14628
|
|
|
|
|
|
|
|
14629
|
0
|
|
|
|
|
|
my $records = $o->{actor}->rememberedRecords; |
14630
|
0
|
|
|
|
|
|
for my $label (keys %$records) { |
14631
|
0
|
0
|
|
|
|
|
next if substr($label, 0, length $o->{text}) ne $o->{text}; |
14632
|
0
|
|
|
|
|
|
$o->addPossibility($label); |
14633
|
|
|
|
|
|
|
} |
14634
|
|
|
|
|
|
|
} |
14635
|
|
|
|
|
|
|
|
14636
|
|
|
|
|
|
|
sub object { |
14637
|
0
|
|
|
0
|
|
|
my $o = shift; |
14638
|
|
|
|
|
|
|
|
14639
|
|
|
|
|
|
|
# Folder stores use the first two hex digits as folder |
14640
|
0
|
0
|
|
|
|
|
my $url = $o->{text} =~ /^\s*(.*?\/objects\/)([0-9a-fA-F]{2,2})\/([0-9a-fA-F]{62,62})\/*\s*$/ ? $1.$2.$3 : $o->{text}; |
14641
|
|
|
|
|
|
|
|
14642
|
|
|
|
|
|
|
# From a URL |
14643
|
0
|
0
|
|
|
|
|
if ($url =~ /^\s*(.*?)\/objects\/([0-9a-fA-F]{64,64})\/*\s*$/) { |
14644
|
0
|
|
|
|
|
|
my $storeUrl = $1; |
14645
|
0
|
|
|
|
|
|
my $hash = CDS::Hash->fromHex($2); |
14646
|
0
|
0
|
0
|
|
|
|
$storeUrl = 'file://'.Cwd::abs_path($storeUrl) if $storeUrl !~ /^[a-zA-Z0-9_\+-]*:/ && -d $storeUrl; |
14647
|
0
|
|
0
|
|
|
|
my $cliStore = $o->{actor}->storeForUrl($storeUrl) // return $o->warning('Invalid store URL "', $storeUrl, '".'); |
14648
|
0
|
|
|
|
|
|
return CDS::ObjectToken->new($cliStore, $hash); |
14649
|
|
|
|
|
|
|
} |
14650
|
|
|
|
|
|
|
|
14651
|
0
|
|
|
|
|
|
return; |
14652
|
|
|
|
|
|
|
} |
14653
|
|
|
|
|
|
|
|
14654
|
|
|
|
|
|
|
sub completeObject { |
14655
|
0
|
|
|
0
|
|
|
my $o = shift; |
14656
|
|
|
|
|
|
|
|
14657
|
0
|
|
|
|
|
|
$o->completeUrl; |
14658
|
0
|
|
|
|
|
|
return; |
14659
|
|
|
|
|
|
|
} |
14660
|
|
|
|
|
|
|
|
14661
|
|
|
|
|
|
|
sub objectFile { |
14662
|
0
|
|
|
0
|
|
|
my $o = shift; |
14663
|
|
|
|
|
|
|
|
14664
|
|
|
|
|
|
|
# Key pair from file |
14665
|
0
|
|
0
|
|
|
|
my $file = Cwd::abs_path($o->{text}) // return; |
14666
|
0
|
0
|
0
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as keyword. If you mean the file, write "./', $o->{text}, '".') if $o->isKeyword && -f $file; |
14667
|
|
|
|
|
|
|
|
14668
|
|
|
|
|
|
|
# Load the object |
14669
|
0
|
0
|
|
|
|
|
return if ! -f $file; |
14670
|
0
|
|
0
|
|
|
|
my $bytes = CDS->readBytesFromFile($file) // return $o->warning('The object file "', $file, '" could not be read.'); |
14671
|
0
|
|
0
|
|
|
|
my $object = CDS::Object->fromBytes($bytes) // return $o->warning('The file "', $file, '" does not contain a Condensation object.'); |
14672
|
0
|
|
|
|
|
|
return CDS::ObjectFileToken->new($file, $object); |
14673
|
|
|
|
|
|
|
} |
14674
|
|
|
|
|
|
|
|
14675
|
|
|
|
|
|
|
sub completeObjectFile { |
14676
|
0
|
|
|
0
|
|
|
my $o = shift; |
14677
|
|
|
|
|
|
|
|
14678
|
0
|
|
|
|
|
|
$o->completeFile; |
14679
|
0
|
|
|
|
|
|
return; |
14680
|
|
|
|
|
|
|
} |
14681
|
|
|
|
|
|
|
|
14682
|
|
|
|
|
|
|
sub actorGroup { |
14683
|
0
|
|
|
0
|
|
|
my $o = shift; |
14684
|
|
|
|
|
|
|
|
14685
|
|
|
|
|
|
|
# We only accept named actor groups. Accepting a single account as actor group is ambiguous whenever ACCOUNT and ACTORGROUP are accepted. For commands that are requiring an ACTORGROUP, they can also accept an ACCOUNT and then convert it. |
14686
|
|
|
|
|
|
|
|
14687
|
|
|
|
|
|
|
# Check if it's an actor group label |
14688
|
0
|
|
|
|
|
|
my $record = $o->{actor}->remembered($o->{text})->child('actor group'); |
14689
|
0
|
0
|
|
|
|
|
return if ! scalar $record->children; |
14690
|
0
|
0
|
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as keyword. To refer to the actor group, rename it.') if $o->isKeyword; |
14691
|
|
|
|
|
|
|
|
14692
|
0
|
|
|
|
|
|
my $builder = CDS::ActorGroupBuilder->new; |
14693
|
0
|
|
|
|
|
|
$builder->addKnownPublicKey($o->{actor}->keyPair->publicKey); |
14694
|
0
|
|
|
|
|
|
$builder->parse($record, 1); |
14695
|
0
|
|
|
|
|
|
my ($actorGroup, $storeError) = $builder->load($o->{actor}->groupDocument->unsaved, $o->{actor}->keyPair, $o); |
14696
|
0
|
0
|
|
|
|
|
return $o->{actor}->storeError($o->{actor}->storageStore, $storeError) if defined $storeError; |
14697
|
0
|
|
|
|
|
|
return CDS::ActorGroupToken->new($o->{text}, $actorGroup); |
14698
|
|
|
|
|
|
|
} |
14699
|
|
|
|
|
|
|
|
14700
|
|
|
|
|
|
|
sub onLoadActorGroupVerifyStore { |
14701
|
0
|
|
|
0
|
|
|
my $o = shift; |
14702
|
0
|
|
|
|
|
|
my $storeUrl = shift; |
14703
|
0
|
|
|
|
|
|
$o->{actor}->storeForUrl($storeUrl); } |
14704
|
|
|
|
|
|
|
|
14705
|
|
|
|
|
|
|
sub completeActorGroup { |
14706
|
0
|
|
|
0
|
|
|
my $o = shift; |
14707
|
|
|
|
|
|
|
|
14708
|
0
|
|
|
|
|
|
my $records = $o->{actor}->rememberedRecords; |
14709
|
0
|
|
|
|
|
|
for my $label (keys %$records) { |
14710
|
0
|
|
|
|
|
|
my $record = $records->{$label}; |
14711
|
0
|
0
|
|
|
|
|
next if ! scalar $record->child('actor group')->children; |
14712
|
0
|
|
|
|
|
|
$o->addPossibility($label); |
14713
|
|
|
|
|
|
|
} |
14714
|
0
|
|
|
|
|
|
return; |
14715
|
|
|
|
|
|
|
} |
14716
|
|
|
|
|
|
|
|
14717
|
|
|
|
|
|
|
sub port { |
14718
|
0
|
|
|
0
|
|
|
my $o = shift; |
14719
|
|
|
|
|
|
|
|
14720
|
0
|
|
|
|
|
|
my $port = int($o->{text}); |
14721
|
0
|
0
|
0
|
|
|
|
return if $port <= 0 || $port > 65536; |
14722
|
0
|
|
|
|
|
|
return $port; |
14723
|
|
|
|
|
|
|
} |
14724
|
|
|
|
|
|
|
|
14725
|
|
|
|
|
|
|
sub rememberedStoreUrl { |
14726
|
0
|
|
|
0
|
|
|
my $o = shift; |
14727
|
|
|
|
|
|
|
|
14728
|
0
|
|
|
|
|
|
my $record = $o->{actor}->remembered($o->{text}); |
14729
|
0
|
|
|
|
|
|
my $storeUrl = $record->child('store')->textValue; |
14730
|
0
|
0
|
|
|
|
|
return if ! length $storeUrl; |
14731
|
|
|
|
|
|
|
|
14732
|
0
|
0
|
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as keyword. If you mean the store, write "', $storeUrl, '".') if $o->isKeyword; |
14733
|
0
|
|
|
|
|
|
return $storeUrl; |
14734
|
|
|
|
|
|
|
} |
14735
|
|
|
|
|
|
|
|
14736
|
|
|
|
|
|
|
sub directStoreUrl { |
14737
|
0
|
|
|
0
|
|
|
my $o = shift; |
14738
|
|
|
|
|
|
|
|
14739
|
0
|
0
|
|
|
|
|
return $o->warning('"', $o->{text}, '" is interpreted as keyword. If you mean the folder store, write "./', $o->{text}, '".') if $o->isKeyword; |
14740
|
0
|
0
|
|
|
|
|
return if $o->{text} =~ /[0-9a-f]{32}/; |
14741
|
|
|
|
|
|
|
|
14742
|
0
|
0
|
|
|
|
|
return $o->{text} if $o->{text} =~ /^[a-zA-Z0-9_\+-]*:/; |
14743
|
0
|
0
|
0
|
|
|
|
return 'file://'.Cwd::abs_path($o->{text}) if -d $o->{text} && -d $o->{text}.'/accounts' && -d $o->{text}.'/objects'; |
|
|
|
0
|
|
|
|
|
14744
|
0
|
|
|
|
|
|
return; |
14745
|
|
|
|
|
|
|
} |
14746
|
|
|
|
|
|
|
|
14747
|
|
|
|
|
|
|
sub store { |
14748
|
0
|
|
|
0
|
|
|
my $o = shift; |
14749
|
|
|
|
|
|
|
|
14750
|
0
|
|
0
|
|
|
|
my $url = $o->rememberedStoreUrl // $o->directStoreUrl // return; |
|
|
|
0
|
|
|
|
|
14751
|
0
|
|
0
|
|
|
|
return $o->{actor}->storeForUrl($url) // return $o->warning('"', $o->{text}, '" looks like a store, but no implementation is available to handle this protocol.'); |
14752
|
|
|
|
|
|
|
} |
14753
|
|
|
|
|
|
|
|
14754
|
|
|
|
|
|
|
sub completeFolderStoreUrl { |
14755
|
0
|
|
|
0
|
|
|
my $o = shift; |
14756
|
|
|
|
|
|
|
|
14757
|
0
|
|
|
|
|
|
my $folder = './'; |
14758
|
0
|
|
|
|
|
|
my $startFilename = $o->{text}; |
14759
|
0
|
0
|
|
|
|
|
if ($o->{text} =~ /^(.*\/)([^\/]*)$/) { |
14760
|
0
|
|
|
|
|
|
$folder = $1; |
14761
|
0
|
|
|
|
|
|
$startFilename = $2; |
14762
|
|
|
|
|
|
|
} |
14763
|
|
|
|
|
|
|
|
14764
|
0
|
|
|
|
|
|
for my $filename (CDS->listFolder($folder)) { |
14765
|
0
|
0
|
|
|
|
|
next if $filename eq '.'; |
14766
|
0
|
0
|
|
|
|
|
next if $filename eq '..'; |
14767
|
0
|
0
|
|
|
|
|
next if substr($filename, 0, length $startFilename) ne $startFilename; |
14768
|
0
|
|
|
|
|
|
my $file = $folder.$filename; |
14769
|
0
|
0
|
|
|
|
|
next if ! -d $file; |
14770
|
0
|
0
|
0
|
|
|
|
push @{$o->{possibilities}}, $file . (-d $file.'/accounts' && -d $file.'/objects' ? ' ' : '/'); |
|
0
|
|
|
|
|
|
|
14771
|
|
|
|
|
|
|
} |
14772
|
|
|
|
|
|
|
} |
14773
|
|
|
|
|
|
|
|
14774
|
|
|
|
|
|
|
sub completeStoreUrl { |
14775
|
0
|
|
|
0
|
|
|
my $o = shift; |
14776
|
|
|
|
|
|
|
|
14777
|
0
|
|
|
|
|
|
$o->completeFolderStoreUrl; |
14778
|
0
|
|
|
|
|
|
$o->completeUrl; |
14779
|
|
|
|
|
|
|
|
14780
|
0
|
|
|
|
|
|
my $records = $o->{actor}->rememberedRecords; |
14781
|
0
|
|
|
|
|
|
for my $label (keys %$records) { |
14782
|
0
|
|
|
|
|
|
my $record = $records->{$label}; |
14783
|
0
|
0
|
|
|
|
|
next if length $record->child('actor')->bytesValue; |
14784
|
0
|
|
|
|
|
|
my $storeUrl = $record->child('store')->textValue; |
14785
|
0
|
0
|
|
|
|
|
next if ! length $storeUrl; |
14786
|
0
|
|
|
|
|
|
$o->addPossibility($label); |
14787
|
0
|
|
|
|
|
|
$o->addPossibility($storeUrl); |
14788
|
|
|
|
|
|
|
} |
14789
|
|
|
|
|
|
|
} |
14790
|
|
|
|
|
|
|
|
14791
|
|
|
|
|
|
|
sub completeUrl { |
14792
|
0
|
|
|
0
|
|
|
my $o = shift; |
14793
|
|
|
|
|
|
|
|
14794
|
0
|
|
|
|
|
|
$o->addPartialPossibility('http://'); |
14795
|
0
|
|
|
|
|
|
$o->addPartialPossibility('https://'); |
14796
|
0
|
|
|
|
|
|
$o->addPartialPossibility('ftp://'); |
14797
|
0
|
|
|
|
|
|
$o->addPartialPossibility('sftp://'); |
14798
|
0
|
|
|
|
|
|
$o->addPartialPossibility('file://'); |
14799
|
|
|
|
|
|
|
} |
14800
|
|
|
|
|
|
|
|
14801
|
|
|
|
|
|
|
sub text { |
14802
|
0
|
|
|
0
|
|
|
my $o = shift; |
14803
|
|
|
|
|
|
|
|
14804
|
0
|
|
|
|
|
|
return $o->{text}; |
14805
|
|
|
|
|
|
|
} |
14806
|
|
|
|
|
|
|
|
14807
|
|
|
|
|
|
|
sub user { |
14808
|
0
|
|
|
0
|
|
|
my $o = shift; |
14809
|
|
|
|
|
|
|
|
14810
|
0
|
0
|
|
|
|
|
return int($1) if $o->{text} =~ /^\s*(\d{1,5})\s*$/; |
14811
|
0
|
|
|
|
|
|
return getpwnam($o->{text}); |
14812
|
|
|
|
|
|
|
} |
14813
|
|
|
|
|
|
|
|
14814
|
|
|
|
|
|
|
sub completeUser { |
14815
|
0
|
|
|
0
|
|
|
my $o = shift; |
14816
|
|
|
|
|
|
|
|
14817
|
0
|
|
|
|
|
|
while (my $name = getpwent) { |
14818
|
0
|
|
|
|
|
|
$o->addPossibility($name); |
14819
|
|
|
|
|
|
|
} |
14820
|
|
|
|
|
|
|
} |
14821
|
|
|
|
|
|
|
|
14822
|
|
|
|
|
|
|
sub warning { |
14823
|
0
|
|
|
0
|
|
|
my $o = shift; |
14824
|
|
|
|
|
|
|
|
14825
|
0
|
|
|
|
|
|
push @{$o->{warnings}}, join('', @_); |
|
0
|
|
|
|
|
|
|
14826
|
0
|
|
|
|
|
|
return; |
14827
|
|
|
|
|
|
|
} |
14828
|
|
|
|
|
|
|
|
14829
|
|
|
|
|
|
|
# Reads the private box of an actor. |
14830
|
|
|
|
|
|
|
package CDS::PrivateBoxReader; |
14831
|
|
|
|
|
|
|
|
14832
|
|
|
|
|
|
|
sub new { |
14833
|
0
|
|
|
0
|
|
|
my $class = shift; |
14834
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
14835
|
0
|
|
|
|
|
|
my $store = shift; |
14836
|
0
|
|
|
|
|
|
my $delegate = shift; |
14837
|
|
|
|
|
|
|
|
14838
|
0
|
|
|
|
|
|
return bless { |
14839
|
|
|
|
|
|
|
keyPair => $keyPair, |
14840
|
|
|
|
|
|
|
actorOnStore => CDS::ActorOnStore->new($keyPair->publicKey, $store), |
14841
|
|
|
|
|
|
|
delegate => $delegate, |
14842
|
|
|
|
|
|
|
entries => {}, |
14843
|
|
|
|
|
|
|
}; |
14844
|
|
|
|
|
|
|
} |
14845
|
|
|
|
|
|
|
|
14846
|
0
|
|
|
0
|
|
|
sub keyPair { shift->{keyPair} } |
14847
|
0
|
|
|
0
|
|
|
sub actorOnStore { shift->{actorOnStore} } |
14848
|
0
|
|
|
0
|
|
|
sub delegate { shift->{delegate} } |
14849
|
|
|
|
|
|
|
|
14850
|
|
|
|
|
|
|
sub read { |
14851
|
0
|
|
|
0
|
|
|
my $o = shift; |
14852
|
|
|
|
|
|
|
|
14853
|
0
|
|
|
|
|
|
my $store = $o->{actorOnStore}->store; |
14854
|
0
|
|
|
|
|
|
my ($hashes, $listError) = $store->list($o->{actorOnStore}->publicKey->hash, 'private', 0, $o->{keyPair}); |
14855
|
0
|
0
|
|
|
|
|
return if defined $listError; |
14856
|
|
|
|
|
|
|
|
14857
|
|
|
|
|
|
|
# Keep track of the processed entries |
14858
|
0
|
|
|
|
|
|
my $newEntries = {}; |
14859
|
0
|
|
|
|
|
|
for my $hash (@$hashes) { |
14860
|
0
|
|
0
|
|
|
|
$newEntries->{$hash->bytes} = $o->{entries}->{$hash->bytes} // {hash => $hash, processed => 0}; |
14861
|
|
|
|
|
|
|
} |
14862
|
0
|
|
|
|
|
|
$o->{entries} = $newEntries; |
14863
|
|
|
|
|
|
|
|
14864
|
|
|
|
|
|
|
# Process new entries |
14865
|
0
|
|
|
|
|
|
for my $entry (values %$newEntries) { |
14866
|
0
|
0
|
|
|
|
|
next if $entry->{processed}; |
14867
|
|
|
|
|
|
|
|
14868
|
|
|
|
|
|
|
# Get the envelope |
14869
|
0
|
|
|
|
|
|
my ($object, $getError) = $store->get($entry->{hash}, $o->{keyPair}); |
14870
|
0
|
0
|
|
|
|
|
return if defined $getError; |
14871
|
|
|
|
|
|
|
|
14872
|
0
|
0
|
|
|
|
|
if (! defined $object) { |
14873
|
0
|
|
|
|
|
|
$o->invalid($entry, 'Envelope object not found.'); |
14874
|
0
|
|
|
|
|
|
next; |
14875
|
|
|
|
|
|
|
} |
14876
|
|
|
|
|
|
|
|
14877
|
|
|
|
|
|
|
# Parse the record |
14878
|
0
|
|
|
|
|
|
my $envelope = CDS::Record->fromObject($object); |
14879
|
0
|
0
|
|
|
|
|
if (! $envelope) { |
14880
|
0
|
|
|
|
|
|
$o->invalid($entry, 'Envelope is not a record.'); |
14881
|
0
|
|
|
|
|
|
next; |
14882
|
|
|
|
|
|
|
} |
14883
|
|
|
|
|
|
|
|
14884
|
|
|
|
|
|
|
# Read the content hash |
14885
|
0
|
|
|
|
|
|
my $contentHash = $envelope->child('content')->hashValue; |
14886
|
0
|
0
|
|
|
|
|
if (! $contentHash) { |
14887
|
0
|
|
|
|
|
|
$o->invalid($entry, 'Missing content hash.'); |
14888
|
0
|
|
|
|
|
|
next; |
14889
|
|
|
|
|
|
|
} |
14890
|
|
|
|
|
|
|
|
14891
|
|
|
|
|
|
|
# Verify the signature |
14892
|
0
|
0
|
|
|
|
|
if (! CDS->verifyEnvelopeSignature($envelope, $o->{keyPair}->publicKey, $contentHash)) { |
14893
|
0
|
|
|
|
|
|
$o->invalid($entry, 'Invalid signature.'); |
14894
|
0
|
|
|
|
|
|
next; |
14895
|
|
|
|
|
|
|
} |
14896
|
|
|
|
|
|
|
|
14897
|
|
|
|
|
|
|
# Decrypt the key |
14898
|
0
|
|
|
|
|
|
my $aesKey = $o->{keyPair}->decryptKeyOnEnvelope($envelope); |
14899
|
0
|
0
|
|
|
|
|
if (! $aesKey) { |
14900
|
0
|
|
|
|
|
|
$o->invalid($entry, 'Not encrypted for us.'); |
14901
|
0
|
|
|
|
|
|
next; |
14902
|
|
|
|
|
|
|
} |
14903
|
|
|
|
|
|
|
|
14904
|
|
|
|
|
|
|
# Retrieve the content |
14905
|
0
|
|
|
|
|
|
my $contentHashAndKey = CDS::HashAndKey->new($contentHash, $aesKey); |
14906
|
0
|
|
|
|
|
|
my ($contentRecord, $contentObject, $contentInvalidReason, $contentStoreError) = $o->{keyPair}->getAndDecryptRecord($contentHashAndKey, $store); |
14907
|
0
|
0
|
|
|
|
|
return if defined $contentStoreError; |
14908
|
|
|
|
|
|
|
|
14909
|
0
|
0
|
|
|
|
|
if (defined $contentInvalidReason) { |
14910
|
0
|
|
|
|
|
|
$o->invalid($entry, $contentInvalidReason); |
14911
|
0
|
|
|
|
|
|
next; |
14912
|
|
|
|
|
|
|
} |
14913
|
|
|
|
|
|
|
|
14914
|
0
|
|
|
|
|
|
$entry->{processed} = 1; |
14915
|
0
|
|
|
|
|
|
my $source = CDS::Source->new($o->{keyPair}, $o->{actorOnStore}, 'private', $entry->{hash}); |
14916
|
0
|
|
|
|
|
|
$o->{delegate}->onPrivateBoxEntry($source, $envelope, $contentHashAndKey, $contentRecord); |
14917
|
|
|
|
|
|
|
} |
14918
|
|
|
|
|
|
|
|
14919
|
0
|
|
|
|
|
|
return 1; |
14920
|
|
|
|
|
|
|
} |
14921
|
|
|
|
|
|
|
|
14922
|
|
|
|
|
|
|
sub invalid { |
14923
|
0
|
|
|
0
|
|
|
my $o = shift; |
14924
|
0
|
|
|
|
|
|
my $entry = shift; |
14925
|
0
|
|
|
|
|
|
my $reason = shift; |
14926
|
|
|
|
|
|
|
|
14927
|
0
|
|
|
|
|
|
$entry->{processed} = 1; |
14928
|
0
|
|
|
|
|
|
my $source = CDS::Source->new($o->{actorOnStore}, 'private', $entry->{hash}); |
14929
|
0
|
|
|
|
|
|
$o->{delegate}->onPrivateBoxInvalidEntry($source, $reason); |
14930
|
|
|
|
|
|
|
} |
14931
|
|
|
|
|
|
|
|
14932
|
|
|
|
|
|
|
# Delegate |
14933
|
|
|
|
|
|
|
# onPrivateBoxEntry($source, $envelope, $contentHashAndKey, $contentRecord) |
14934
|
|
|
|
|
|
|
# onPrivateBoxInvalidEntry($source, $reason) |
14935
|
|
|
|
|
|
|
|
14936
|
|
|
|
|
|
|
package CDS::PrivateRoot; |
14937
|
|
|
|
|
|
|
|
14938
|
|
|
|
|
|
|
sub new { |
14939
|
0
|
|
|
0
|
|
|
my $class = shift; |
14940
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
14941
|
0
|
|
|
|
|
|
my $store = shift; |
14942
|
0
|
|
|
|
|
|
my $delegate = shift; |
14943
|
|
|
|
|
|
|
|
14944
|
0
|
|
|
|
|
|
my $o = bless { |
14945
|
|
|
|
|
|
|
unsaved => CDS::Unsaved->new($store), |
14946
|
|
|
|
|
|
|
delegate => $delegate, |
14947
|
|
|
|
|
|
|
dataHandlers => {}, |
14948
|
|
|
|
|
|
|
hasChanges => 0, |
14949
|
|
|
|
|
|
|
procured => 0, |
14950
|
|
|
|
|
|
|
mergedEntries => [], |
14951
|
|
|
|
|
|
|
}; |
14952
|
|
|
|
|
|
|
|
14953
|
0
|
|
|
|
|
|
$o->{privateBoxReader} = CDS::PrivateBoxReader->new($keyPair, $store, $o); |
14954
|
0
|
|
|
|
|
|
return $o; |
14955
|
|
|
|
|
|
|
} |
14956
|
|
|
|
|
|
|
|
14957
|
0
|
|
|
0
|
|
|
sub delegate { shift->{delegate} } |
14958
|
0
|
|
|
0
|
|
|
sub privateBoxReader { shift->{privateBoxReader} } |
14959
|
0
|
|
|
0
|
|
|
sub unsaved { shift->{unsaved} } |
14960
|
0
|
|
|
0
|
|
|
sub hasChanges { shift->{hasChanges} } |
14961
|
0
|
|
|
0
|
|
|
sub procured { shift->{procured} } |
14962
|
|
|
|
|
|
|
|
14963
|
|
|
|
|
|
|
sub addDataHandler { |
14964
|
0
|
|
|
0
|
|
|
my $o = shift; |
14965
|
0
|
|
|
|
|
|
my $label = shift; |
14966
|
0
|
|
|
|
|
|
my $dataHandler = shift; |
14967
|
|
|
|
|
|
|
|
14968
|
0
|
|
|
|
|
|
$o->{dataHandlers}->{$label} = $dataHandler; |
14969
|
|
|
|
|
|
|
} |
14970
|
|
|
|
|
|
|
|
14971
|
|
|
|
|
|
|
sub removeDataHandler { |
14972
|
0
|
|
|
0
|
|
|
my $o = shift; |
14973
|
0
|
|
|
|
|
|
my $label = shift; |
14974
|
0
|
|
|
|
|
|
my $dataHandler = shift; |
14975
|
|
|
|
|
|
|
|
14976
|
0
|
|
|
|
|
|
my $registered = $o->{dataHandlers}->{$label}; |
14977
|
0
|
0
|
|
|
|
|
return if $registered != $dataHandler; |
14978
|
0
|
|
|
|
|
|
delete $o->{dataHandlers}->{$label}; |
14979
|
|
|
|
|
|
|
} |
14980
|
|
|
|
|
|
|
|
14981
|
|
|
|
|
|
|
# *** Procurement |
14982
|
|
|
|
|
|
|
|
14983
|
|
|
|
|
|
|
sub procure { |
14984
|
0
|
|
|
0
|
|
|
my $o = shift; |
14985
|
0
|
|
|
|
|
|
my $interval = shift; |
14986
|
|
|
|
|
|
|
|
14987
|
0
|
|
|
|
|
|
my $now = CDS->now; |
14988
|
0
|
0
|
|
|
|
|
return $o->{procured} if $o->{procured} + $interval > $now; |
14989
|
0
|
|
0
|
|
|
|
$o->{privateBoxReader}->read // return; |
14990
|
0
|
|
|
|
|
|
$o->{procured} = $now; |
14991
|
0
|
|
|
|
|
|
return $now; |
14992
|
|
|
|
|
|
|
} |
14993
|
|
|
|
|
|
|
|
14994
|
|
|
|
|
|
|
# *** Merging |
14995
|
|
|
|
|
|
|
|
14996
|
|
|
|
|
|
|
sub onPrivateBoxEntry { |
14997
|
0
|
|
|
0
|
|
|
my $o = shift; |
14998
|
0
|
0
|
0
|
|
|
|
my $source = shift; die 'wrong type '.ref($source).' for $source' if defined $source && ref $source ne 'CDS::Source'; |
|
0
|
|
|
|
|
|
|
14999
|
0
|
0
|
0
|
|
|
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
15000
|
0
|
|
|
|
|
|
my $contentHashAndKey = shift; |
15001
|
0
|
|
|
|
|
|
my $content = shift; |
15002
|
|
|
|
|
|
|
|
15003
|
0
|
|
|
|
|
|
for my $section ($content->children) { |
15004
|
0
|
|
0
|
|
|
|
my $dataHandler = $o->{dataHandlers}->{$section->bytes} // next; |
15005
|
0
|
|
|
|
|
|
$dataHandler->mergeData($section); |
15006
|
|
|
|
|
|
|
} |
15007
|
|
|
|
|
|
|
|
15008
|
0
|
|
|
|
|
|
push @{$o->{mergedEntries}}, $source->hash; |
|
0
|
|
|
|
|
|
|
15009
|
|
|
|
|
|
|
} |
15010
|
|
|
|
|
|
|
|
15011
|
|
|
|
|
|
|
sub onPrivateBoxInvalidEntry { |
15012
|
0
|
|
|
0
|
|
|
my $o = shift; |
15013
|
0
|
0
|
0
|
|
|
|
my $source = shift; die 'wrong type '.ref($source).' for $source' if defined $source && ref $source ne 'CDS::Source'; |
|
0
|
|
|
|
|
|
|
15014
|
0
|
|
|
|
|
|
my $reason = shift; |
15015
|
|
|
|
|
|
|
|
15016
|
0
|
|
|
|
|
|
$o->{delegate}->onPrivateRootReadingInvalidEntry($source, $reason); |
15017
|
0
|
|
|
|
|
|
$source->discard; |
15018
|
|
|
|
|
|
|
} |
15019
|
|
|
|
|
|
|
|
15020
|
|
|
|
|
|
|
# *** Saving |
15021
|
|
|
|
|
|
|
|
15022
|
|
|
|
|
|
|
sub dataChanged { |
15023
|
0
|
|
|
0
|
|
|
my $o = shift; |
15024
|
|
|
|
|
|
|
|
15025
|
0
|
|
|
|
|
|
$o->{hasChanges} = 1; |
15026
|
|
|
|
|
|
|
} |
15027
|
|
|
|
|
|
|
|
15028
|
|
|
|
|
|
|
sub save { |
15029
|
0
|
|
|
0
|
|
|
my $o = shift; |
15030
|
0
|
|
|
|
|
|
my $entrustedKeys = shift; |
15031
|
|
|
|
|
|
|
|
15032
|
0
|
|
|
|
|
|
$o->{unsaved}->startSaving; |
15033
|
0
|
0
|
|
|
|
|
return $o->savingSucceeded if ! $o->{hasChanges}; |
15034
|
0
|
|
|
|
|
|
$o->{hasChanges} = 0; |
15035
|
|
|
|
|
|
|
|
15036
|
|
|
|
|
|
|
# Create the record |
15037
|
0
|
|
|
|
|
|
my $record = CDS::Record->new; |
15038
|
0
|
|
|
|
|
|
$record->add('created')->addInteger(CDS->now); |
15039
|
0
|
|
|
|
|
|
$record->add('client')->add(CDS->version); |
15040
|
0
|
|
|
|
|
|
for my $label (keys %{$o->{dataHandlers}}) { |
|
0
|
|
|
|
|
|
|
15041
|
0
|
|
|
|
|
|
my $dataHandler = $o->{dataHandlers}->{$label}; |
15042
|
0
|
|
|
|
|
|
$dataHandler->addDataTo($record->add($label)); |
15043
|
|
|
|
|
|
|
} |
15044
|
|
|
|
|
|
|
|
15045
|
|
|
|
|
|
|
# Submit the object |
15046
|
0
|
|
|
|
|
|
my $key = CDS->randomKey; |
15047
|
0
|
|
|
|
|
|
my $object = $record->toObject->crypt($key); |
15048
|
0
|
|
|
|
|
|
my $hash = $object->calculateHash; |
15049
|
0
|
|
|
|
|
|
$o->{unsaved}->savingState->addObject($hash, $object); |
15050
|
0
|
|
|
|
|
|
my $hashAndKey = CDS::HashAndKey->new($hash, $key); |
15051
|
|
|
|
|
|
|
|
15052
|
|
|
|
|
|
|
# Create the envelope |
15053
|
0
|
|
|
|
|
|
my $keyPair = $o->{privateBoxReader}->keyPair; |
15054
|
0
|
|
|
|
|
|
my $publicKeys = [$keyPair->publicKey, @$entrustedKeys]; |
15055
|
0
|
|
|
|
|
|
my $envelopeObject = $keyPair->createPrivateEnvelope($hashAndKey, $publicKeys)->toObject; |
15056
|
0
|
|
|
|
|
|
my $envelopeHash = $envelopeObject->calculateHash; |
15057
|
0
|
|
|
|
|
|
$o->{unsaved}->savingState->addObject($envelopeHash, $envelopeObject); |
15058
|
|
|
|
|
|
|
|
15059
|
|
|
|
|
|
|
# Transfer |
15060
|
0
|
|
|
|
|
|
my ($missing, $store, $storeError) = $keyPair->transfer([$hash], $o->{unsaved}, $o->{privateBoxReader}->actorOnStore->store); |
15061
|
0
|
0
|
0
|
|
|
|
return $o->savingFailed($missing) if defined $missing || defined $storeError; |
15062
|
|
|
|
|
|
|
|
15063
|
|
|
|
|
|
|
# Modify the private box |
15064
|
0
|
|
|
|
|
|
my $modifications = CDS::StoreModifications->new; |
15065
|
0
|
|
|
|
|
|
$modifications->add($keyPair->publicKey->hash, 'private', $envelopeHash, $envelopeObject); |
15066
|
0
|
|
|
|
|
|
for my $hash (@{$o->{mergedEntries}}) { |
|
0
|
|
|
|
|
|
|
15067
|
0
|
|
|
|
|
|
$modifications->remove($keyPair->publicKey->hash, 'private', $hash); |
15068
|
|
|
|
|
|
|
} |
15069
|
|
|
|
|
|
|
|
15070
|
0
|
|
|
|
|
|
my $modifyError = $o->{privateBoxReader}->actorOnStore->store->modify($modifications, $keyPair); |
15071
|
0
|
0
|
|
|
|
|
return $o->savingFailed if defined $modifyError; |
15072
|
|
|
|
|
|
|
|
15073
|
|
|
|
|
|
|
# Set the new merged hashes |
15074
|
0
|
|
|
|
|
|
$o->{mergedEntries} = [$envelopeHash]; |
15075
|
0
|
|
|
|
|
|
return $o->savingSucceeded; |
15076
|
|
|
|
|
|
|
} |
15077
|
|
|
|
|
|
|
|
15078
|
|
|
|
|
|
|
sub savingSucceeded { |
15079
|
0
|
|
|
0
|
|
|
my $o = shift; |
15080
|
|
|
|
|
|
|
|
15081
|
|
|
|
|
|
|
# Discard all merged sources |
15082
|
0
|
|
|
|
|
|
for my $source ($o->{unsaved}->savingState->mergedSources) { |
15083
|
0
|
|
|
|
|
|
$source->discard; |
15084
|
|
|
|
|
|
|
} |
15085
|
|
|
|
|
|
|
|
15086
|
|
|
|
|
|
|
# Call all data saved handlers |
15087
|
0
|
|
|
|
|
|
for my $handler ($o->{unsaved}->savingState->dataSavedHandlers) { |
15088
|
0
|
|
|
|
|
|
$handler->onDataSaved; |
15089
|
|
|
|
|
|
|
} |
15090
|
|
|
|
|
|
|
|
15091
|
0
|
|
|
|
|
|
$o->{unsaved}->savingDone; |
15092
|
0
|
|
|
|
|
|
return 1; |
15093
|
|
|
|
|
|
|
} |
15094
|
|
|
|
|
|
|
|
15095
|
|
|
|
|
|
|
sub savingFailed { |
15096
|
0
|
|
|
0
|
|
|
my $o = shift; |
15097
|
0
|
|
|
|
|
|
my $missing = shift; |
15098
|
|
|
|
|
|
|
# private |
15099
|
0
|
|
|
|
|
|
$o->{unsaved}->savingFailed; |
15100
|
0
|
|
|
|
|
|
$o->{hasChanges} = 1; |
15101
|
0
|
|
|
|
|
|
return undef, $missing; |
15102
|
|
|
|
|
|
|
} |
15103
|
|
|
|
|
|
|
|
15104
|
|
|
|
|
|
|
# A public key of somebody. |
15105
|
|
|
|
|
|
|
package CDS::PublicKey; |
15106
|
|
|
|
|
|
|
|
15107
|
|
|
|
|
|
|
sub fromObject { |
15108
|
0
|
|
|
0
|
|
|
my $class = shift; |
15109
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
15110
|
|
|
|
|
|
|
|
15111
|
0
|
|
0
|
|
|
|
my $record = CDS::Record->fromObject($object) // return; |
15112
|
0
|
|
0
|
|
|
|
my $rsaPublicKey = CDS::C::publicKeyNew($record->child('e')->bytesValue, $record->child('n')->bytesValue) // return; |
15113
|
0
|
|
|
|
|
|
return bless { |
15114
|
|
|
|
|
|
|
hash => $object->calculateHash, |
15115
|
|
|
|
|
|
|
rsaPublicKey => $rsaPublicKey, |
15116
|
|
|
|
|
|
|
object => $object, |
15117
|
|
|
|
|
|
|
lastAccess => 0, # used by PublicKeyCache |
15118
|
|
|
|
|
|
|
}; |
15119
|
|
|
|
|
|
|
} |
15120
|
|
|
|
|
|
|
|
15121
|
0
|
|
|
0
|
|
|
sub object { shift->{object} } |
15122
|
|
|
|
|
|
|
sub bytes { |
15123
|
0
|
|
|
0
|
|
|
my $o = shift; |
15124
|
0
|
|
|
|
|
|
$o->{object}->bytes } |
15125
|
|
|
|
|
|
|
|
15126
|
|
|
|
|
|
|
### Public key interface ### |
15127
|
|
|
|
|
|
|
|
15128
|
0
|
|
|
0
|
|
|
sub hash { shift->{hash} } |
15129
|
|
|
|
|
|
|
sub encrypt { |
15130
|
0
|
|
|
0
|
|
|
my $o = shift; |
15131
|
0
|
|
|
|
|
|
my $bytes = shift; |
15132
|
0
|
|
|
|
|
|
CDS::C::publicKeyEncrypt($o->{rsaPublicKey}, $bytes) } |
15133
|
|
|
|
|
|
|
sub verifyHash { |
15134
|
0
|
|
|
0
|
|
|
my $o = shift; |
15135
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15136
|
0
|
|
|
|
|
|
my $signature = shift; |
15137
|
0
|
|
|
|
|
|
CDS::C::publicKeyVerify($o->{rsaPublicKey}, $hash->bytes, $signature) } |
15138
|
|
|
|
|
|
|
|
15139
|
|
|
|
|
|
|
package CDS::PublicKeyCache; |
15140
|
|
|
|
|
|
|
|
15141
|
|
|
|
|
|
|
sub new { |
15142
|
0
|
|
|
0
|
|
|
my $class = shift; |
15143
|
0
|
|
|
|
|
|
my $maxSize = shift; |
15144
|
|
|
|
|
|
|
|
15145
|
0
|
|
|
|
|
|
return bless { |
15146
|
|
|
|
|
|
|
cache => {}, |
15147
|
|
|
|
|
|
|
maxSize => $maxSize, |
15148
|
|
|
|
|
|
|
}; |
15149
|
|
|
|
|
|
|
} |
15150
|
|
|
|
|
|
|
|
15151
|
|
|
|
|
|
|
sub add { |
15152
|
0
|
|
|
0
|
|
|
my $o = shift; |
15153
|
0
|
0
|
0
|
|
|
|
my $publicKey = shift; die 'wrong type '.ref($publicKey).' for $publicKey' if defined $publicKey && ref $publicKey ne 'CDS::PublicKey'; |
|
0
|
|
|
|
|
|
|
15154
|
|
|
|
|
|
|
|
15155
|
0
|
|
|
|
|
|
$o->{cache}->{$publicKey->hash->bytes} = {publicKey => $publicKey, lastAccess => CDS->now}; |
15156
|
0
|
|
|
|
|
|
$o->deleteOldest; |
15157
|
0
|
|
|
|
|
|
return; |
15158
|
|
|
|
|
|
|
} |
15159
|
|
|
|
|
|
|
|
15160
|
|
|
|
|
|
|
sub get { |
15161
|
0
|
|
|
0
|
|
|
my $o = shift; |
15162
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15163
|
|
|
|
|
|
|
|
15164
|
0
|
|
0
|
|
|
|
my $entry = $o->{cache}->{$hash->bytes} // return; |
15165
|
0
|
|
|
|
|
|
$entry->{lastAccess} = CDS->now; |
15166
|
0
|
|
|
|
|
|
return $entry->{publicKey}; |
15167
|
|
|
|
|
|
|
} |
15168
|
|
|
|
|
|
|
|
15169
|
|
|
|
|
|
|
sub deleteOldest { |
15170
|
0
|
|
|
0
|
|
|
my $o = shift; |
15171
|
|
|
|
|
|
|
# private |
15172
|
0
|
0
|
|
|
|
|
return if scalar values %{$o->{cache}} < $o->{maxSize}; |
|
0
|
|
|
|
|
|
|
15173
|
|
|
|
|
|
|
|
15174
|
0
|
|
|
|
|
|
my @entries = sort { $a->{lastAccess} <=> $b->{lastAccess} } values %{$o->{cache}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
15175
|
0
|
|
|
|
|
|
my $toRemove = int(scalar(@entries) - $o->{maxSize} / 2); |
15176
|
0
|
|
|
|
|
|
for my $entry (@entries) { |
15177
|
0
|
|
|
|
|
|
$toRemove -= 1; |
15178
|
0
|
0
|
|
|
|
|
last if $toRemove <= 0; |
15179
|
0
|
|
|
|
|
|
delete $o->{cache}->{$entry->{publicKey}->hash->bytes}; |
15180
|
|
|
|
|
|
|
} |
15181
|
|
|
|
|
|
|
} |
15182
|
|
|
|
|
|
|
|
15183
|
|
|
|
|
|
|
package CDS::PutTree; |
15184
|
|
|
|
|
|
|
|
15185
|
|
|
|
|
|
|
sub new { |
15186
|
0
|
|
|
0
|
|
|
my $o = shift; |
15187
|
0
|
|
|
|
|
|
my $store = shift; |
15188
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
15189
|
0
|
|
|
|
|
|
my $commitPool = shift; |
15190
|
|
|
|
|
|
|
|
15191
|
0
|
|
|
|
|
|
return bless { |
15192
|
|
|
|
|
|
|
store => $store, |
15193
|
|
|
|
|
|
|
commitPool => $commitPool, |
15194
|
|
|
|
|
|
|
keyPair => $keyPair, |
15195
|
|
|
|
|
|
|
done => {}, |
15196
|
|
|
|
|
|
|
}; |
15197
|
|
|
|
|
|
|
} |
15198
|
|
|
|
|
|
|
|
15199
|
|
|
|
|
|
|
sub put { |
15200
|
0
|
|
|
0
|
|
|
my $o = shift; |
15201
|
0
|
0
|
0
|
|
|
|
my $hash = shift // return; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
0
|
|
|
|
|
15202
|
|
|
|
|
|
|
|
15203
|
0
|
0
|
|
|
|
|
return if $o->{done}->{$hash->bytes}; |
15204
|
|
|
|
|
|
|
|
15205
|
|
|
|
|
|
|
# Get the item |
15206
|
0
|
|
0
|
|
|
|
my $hashAndObject = $o->{commitPool}->object($hash) // return; |
15207
|
|
|
|
|
|
|
|
15208
|
|
|
|
|
|
|
# Upload all children |
15209
|
0
|
|
|
|
|
|
for my $hash ($hashAndObject->object->hashes) { |
15210
|
0
|
|
|
|
|
|
my $error = $o->put($hash); |
15211
|
0
|
0
|
|
|
|
|
return $error if defined $error; |
15212
|
|
|
|
|
|
|
} |
15213
|
|
|
|
|
|
|
|
15214
|
|
|
|
|
|
|
# Upload this object |
15215
|
0
|
|
|
|
|
|
my $error = $o->{store}->put($hashAndObject->hash, $hashAndObject->object, $o->{keyPair}); |
15216
|
0
|
0
|
|
|
|
|
return $error if defined $error; |
15217
|
0
|
|
|
|
|
|
$o->{done}->{$hash->bytes} = 1; |
15218
|
0
|
|
|
|
|
|
return; |
15219
|
|
|
|
|
|
|
} |
15220
|
|
|
|
|
|
|
|
15221
|
|
|
|
|
|
|
package CDS::ReceivedMessage; |
15222
|
|
|
|
|
|
|
|
15223
|
|
|
|
|
|
|
sub new { |
15224
|
0
|
|
|
0
|
|
|
my $class = shift; |
15225
|
0
|
|
|
|
|
|
my $messageBoxReader = shift; |
15226
|
0
|
|
|
|
|
|
my $entry = shift; |
15227
|
0
|
0
|
0
|
|
|
|
my $source = shift; die 'wrong type '.ref($source).' for $source' if defined $source && ref $source ne 'CDS::Source'; |
|
0
|
|
|
|
|
|
|
15228
|
0
|
0
|
0
|
|
|
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
15229
|
0
|
|
|
|
|
|
my $senderStoreUrl = shift; |
15230
|
0
|
|
|
|
|
|
my $sender = shift; |
15231
|
0
|
|
|
|
|
|
my $content = shift; |
15232
|
0
|
|
|
|
|
|
my $streamHead = shift; |
15233
|
|
|
|
|
|
|
|
15234
|
0
|
|
|
|
|
|
return bless { |
15235
|
|
|
|
|
|
|
messageBoxReader => $messageBoxReader, |
15236
|
|
|
|
|
|
|
entry => $entry, |
15237
|
|
|
|
|
|
|
source => $source, |
15238
|
|
|
|
|
|
|
envelope => $envelope, |
15239
|
|
|
|
|
|
|
senderStoreUrl => $senderStoreUrl, |
15240
|
|
|
|
|
|
|
sender => $sender, |
15241
|
|
|
|
|
|
|
content => $content, |
15242
|
|
|
|
|
|
|
streamHead => $streamHead, |
15243
|
|
|
|
|
|
|
isDone => 0, |
15244
|
|
|
|
|
|
|
}; |
15245
|
|
|
|
|
|
|
} |
15246
|
|
|
|
|
|
|
|
15247
|
0
|
|
|
0
|
|
|
sub source { shift->{source} } |
15248
|
0
|
|
|
0
|
|
|
sub envelope { shift->{envelope} } |
15249
|
0
|
|
|
0
|
|
|
sub senderStoreUrl { shift->{senderStoreUrl} } |
15250
|
0
|
|
|
0
|
|
|
sub sender { shift->{sender} } |
15251
|
0
|
|
|
0
|
|
|
sub content { shift->{content} } |
15252
|
|
|
|
|
|
|
|
15253
|
|
|
|
|
|
|
sub waitForSenderStore { |
15254
|
0
|
|
|
0
|
|
|
my $o = shift; |
15255
|
|
|
|
|
|
|
|
15256
|
0
|
|
|
|
|
|
$o->{entry}->{waitingForStore} = $o->sender->store; |
15257
|
|
|
|
|
|
|
} |
15258
|
|
|
|
|
|
|
|
15259
|
|
|
|
|
|
|
sub skip { |
15260
|
0
|
|
|
0
|
|
|
my $o = shift; |
15261
|
|
|
|
|
|
|
|
15262
|
0
|
|
|
|
|
|
$o->{entry}->{processed} = 0; |
15263
|
|
|
|
|
|
|
} |
15264
|
|
|
|
|
|
|
|
15265
|
|
|
|
|
|
|
# A record is a tree, whereby each nodes holds a byte sequence and an optional hash. |
15266
|
|
|
|
|
|
|
# Child nodes are ordered, although the order does not always matter. |
15267
|
|
|
|
|
|
|
package CDS::Record; |
15268
|
|
|
|
|
|
|
|
15269
|
|
|
|
|
|
|
sub fromObject { |
15270
|
0
|
|
|
0
|
|
|
my $class = shift; |
15271
|
0
|
0
|
0
|
|
|
|
my $object = shift // return; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
0
|
|
|
|
|
15272
|
|
|
|
|
|
|
|
15273
|
0
|
|
|
|
|
|
my $root = CDS::Record->new; |
15274
|
0
|
|
0
|
|
|
|
$root->addFromObject($object) // return; |
15275
|
0
|
|
|
|
|
|
return $root; |
15276
|
|
|
|
|
|
|
} |
15277
|
|
|
|
|
|
|
|
15278
|
|
|
|
|
|
|
sub new { |
15279
|
0
|
|
|
0
|
|
|
my $class = shift; |
15280
|
0
|
|
|
|
|
|
my $bytes = shift; |
15281
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15282
|
|
|
|
|
|
|
|
15283
|
0
|
|
0
|
|
|
|
bless { |
15284
|
|
|
|
|
|
|
bytes => $bytes // '', |
15285
|
|
|
|
|
|
|
hash => $hash, |
15286
|
|
|
|
|
|
|
children => [], |
15287
|
|
|
|
|
|
|
}; |
15288
|
|
|
|
|
|
|
} |
15289
|
|
|
|
|
|
|
|
15290
|
|
|
|
|
|
|
# *** Adding |
15291
|
|
|
|
|
|
|
|
15292
|
|
|
|
|
|
|
# Adds a record |
15293
|
|
|
|
|
|
|
sub add { |
15294
|
0
|
|
|
0
|
|
|
my $o = shift; |
15295
|
0
|
|
|
|
|
|
my $bytes = shift; |
15296
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15297
|
|
|
|
|
|
|
|
15298
|
0
|
|
|
|
|
|
my $record = CDS::Record->new($bytes, $hash); |
15299
|
0
|
|
|
|
|
|
push @{$o->{children}}, $record; |
|
0
|
|
|
|
|
|
|
15300
|
0
|
|
|
|
|
|
return $record; |
15301
|
|
|
|
|
|
|
} |
15302
|
|
|
|
|
|
|
|
15303
|
|
|
|
|
|
|
sub addText { |
15304
|
0
|
|
|
0
|
|
|
my $o = shift; |
15305
|
0
|
|
|
|
|
|
my $value = shift; |
15306
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15307
|
0
|
|
0
|
|
|
|
$o->add(Encode::encode_utf8($value // ''), $hash) } |
15308
|
|
|
|
|
|
|
sub addBoolean { |
15309
|
0
|
|
|
0
|
|
|
my $o = shift; |
15310
|
0
|
|
|
|
|
|
my $value = shift; |
15311
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15312
|
0
|
|
|
|
|
|
$o->add(CDS->bytesFromBoolean($value), $hash) } |
15313
|
|
|
|
|
|
|
sub addInteger { |
15314
|
0
|
|
|
0
|
|
|
my $o = shift; |
15315
|
0
|
|
|
|
|
|
my $value = shift; |
15316
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15317
|
0
|
|
0
|
|
|
|
$o->add(CDS->bytesFromInteger($value // 0), $hash) } |
15318
|
|
|
|
|
|
|
sub addUnsigned { |
15319
|
0
|
|
|
0
|
|
|
my $o = shift; |
15320
|
0
|
|
|
|
|
|
my $value = shift; |
15321
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15322
|
0
|
|
0
|
|
|
|
$o->add(CDS->bytesFromUnsigned($value // 0), $hash) } |
15323
|
|
|
|
|
|
|
sub addFloat32 { |
15324
|
0
|
|
|
0
|
|
|
my $o = shift; |
15325
|
0
|
|
|
|
|
|
my $value = shift; |
15326
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15327
|
0
|
|
0
|
|
|
|
$o->add(CDS->bytesFromFloat32($value // 0), $hash) } |
15328
|
|
|
|
|
|
|
sub addFloat64 { |
15329
|
0
|
|
|
0
|
|
|
my $o = shift; |
15330
|
0
|
|
|
|
|
|
my $value = shift; |
15331
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15332
|
0
|
|
0
|
|
|
|
$o->add(CDS->bytesFromFloat64($value // 0), $hash) } |
15333
|
|
|
|
|
|
|
sub addHash { |
15334
|
0
|
|
|
0
|
|
|
my $o = shift; |
15335
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15336
|
0
|
|
|
|
|
|
$o->add('', $hash) } |
15337
|
|
|
|
|
|
|
sub addHashAndKey { |
15338
|
0
|
|
|
0
|
|
|
my $o = shift; |
15339
|
0
|
0
|
0
|
|
|
|
my $hashAndKey = shift; die 'wrong type '.ref($hashAndKey).' for $hashAndKey' if defined $hashAndKey && ref $hashAndKey ne 'CDS::HashAndKey'; |
|
0
|
|
|
|
|
|
|
15340
|
0
|
0
|
|
|
|
|
$hashAndKey ? $o->add($hashAndKey->key, $hashAndKey->hash) : $o->add('') } |
15341
|
|
|
|
|
|
|
sub addRecord { |
15342
|
0
|
|
|
0
|
|
|
my $o = shift; |
15343
|
0
|
|
|
|
|
|
push @{$o->{children}}, @_; return; } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
15344
|
|
|
|
|
|
|
|
15345
|
|
|
|
|
|
|
sub addFromObject { |
15346
|
0
|
|
|
0
|
|
|
my $o = shift; |
15347
|
0
|
0
|
0
|
|
|
|
my $object = shift // return; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
0
|
|
|
|
|
15348
|
|
|
|
|
|
|
|
15349
|
0
|
0
|
|
|
|
|
return 1 if ! length $object->data; |
15350
|
0
|
|
|
|
|
|
return CDS::RecordReader->new($object)->readChildren($o); |
15351
|
|
|
|
|
|
|
} |
15352
|
|
|
|
|
|
|
|
15353
|
|
|
|
|
|
|
# *** Set value |
15354
|
|
|
|
|
|
|
|
15355
|
|
|
|
|
|
|
sub set { |
15356
|
0
|
|
|
0
|
|
|
my $o = shift; |
15357
|
0
|
|
|
|
|
|
my $bytes = shift; |
15358
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15359
|
|
|
|
|
|
|
|
15360
|
0
|
|
|
|
|
|
$o->{bytes} = $bytes; |
15361
|
0
|
|
|
|
|
|
$o->{hash} = $hash; |
15362
|
0
|
|
|
|
|
|
return; |
15363
|
|
|
|
|
|
|
} |
15364
|
|
|
|
|
|
|
|
15365
|
|
|
|
|
|
|
# *** Querying |
15366
|
|
|
|
|
|
|
|
15367
|
|
|
|
|
|
|
# Returns true if the record contains a child with the indicated bytes. |
15368
|
|
|
|
|
|
|
sub contains { |
15369
|
0
|
|
|
0
|
|
|
my $o = shift; |
15370
|
0
|
|
|
|
|
|
my $bytes = shift; |
15371
|
|
|
|
|
|
|
|
15372
|
0
|
|
|
|
|
|
for my $child (@{$o->{children}}) { |
|
0
|
|
|
|
|
|
|
15373
|
0
|
0
|
|
|
|
|
return 1 if $child->{bytes} eq $bytes; |
15374
|
|
|
|
|
|
|
} |
15375
|
0
|
|
|
|
|
|
return; |
15376
|
|
|
|
|
|
|
} |
15377
|
|
|
|
|
|
|
|
15378
|
|
|
|
|
|
|
# Returns the child record for the given bytes. If no record with these bytes exists, a record with these bytes is returned (but not added). |
15379
|
|
|
|
|
|
|
sub child { |
15380
|
0
|
|
|
0
|
|
|
my $o = shift; |
15381
|
0
|
|
|
|
|
|
my $bytes = shift; |
15382
|
|
|
|
|
|
|
|
15383
|
0
|
|
|
|
|
|
for my $child (@{$o->{children}}) { |
|
0
|
|
|
|
|
|
|
15384
|
0
|
0
|
|
|
|
|
return $child if $child->{bytes} eq $bytes; |
15385
|
|
|
|
|
|
|
} |
15386
|
0
|
|
|
|
|
|
return $o->new($bytes); |
15387
|
|
|
|
|
|
|
} |
15388
|
|
|
|
|
|
|
|
15389
|
|
|
|
|
|
|
# Returns the first child, or an empty record. |
15390
|
|
|
|
|
|
|
sub firstChild { |
15391
|
0
|
|
|
0
|
|
|
my $o = shift; |
15392
|
0
|
|
0
|
|
|
|
$o->{children}->[0] // $o->new } |
15393
|
|
|
|
|
|
|
|
15394
|
|
|
|
|
|
|
# Returns the nth child, or an empty record. |
15395
|
|
|
|
|
|
|
sub nthChild { |
15396
|
0
|
|
|
0
|
|
|
my $o = shift; |
15397
|
0
|
|
|
|
|
|
my $i = shift; |
15398
|
0
|
|
0
|
|
|
|
$o->{children}->[$i] // $o->new } |
15399
|
|
|
|
|
|
|
|
15400
|
|
|
|
|
|
|
sub containsText { |
15401
|
0
|
|
|
0
|
|
|
my $o = shift; |
15402
|
0
|
|
|
|
|
|
my $text = shift; |
15403
|
0
|
|
0
|
|
|
|
$o->contains(Encode::encode_utf8($text // '')) } |
15404
|
|
|
|
|
|
|
sub childWithText { |
15405
|
0
|
|
|
0
|
|
|
my $o = shift; |
15406
|
0
|
|
|
|
|
|
my $text = shift; |
15407
|
0
|
|
0
|
|
|
|
$o->child(Encode::encode_utf8($text // '')) } |
15408
|
|
|
|
|
|
|
|
15409
|
|
|
|
|
|
|
# *** Get value |
15410
|
|
|
|
|
|
|
|
15411
|
0
|
|
|
0
|
|
|
sub bytes { shift->{bytes} } |
15412
|
0
|
|
|
0
|
|
|
sub hash { shift->{hash} } |
15413
|
|
|
|
|
|
|
sub children { |
15414
|
0
|
|
|
0
|
|
|
my $o = shift; |
15415
|
0
|
|
|
|
|
|
@{$o->{children}} } |
|
0
|
|
|
|
|
|
|
15416
|
|
|
|
|
|
|
|
15417
|
|
|
|
|
|
|
sub asText { |
15418
|
0
|
|
|
0
|
|
|
my $o = shift; |
15419
|
0
|
|
0
|
|
|
|
Encode::decode_utf8($o->{bytes}) // '' } |
15420
|
|
|
|
|
|
|
sub asBoolean { |
15421
|
0
|
|
|
0
|
|
|
my $o = shift; |
15422
|
0
|
|
|
|
|
|
CDS->booleanFromBytes($o->{bytes}) } |
15423
|
|
|
|
|
|
|
sub asInteger { |
15424
|
0
|
|
|
0
|
|
|
my $o = shift; |
15425
|
0
|
|
0
|
|
|
|
CDS->integerFromBytes($o->{bytes}) // 0 } |
15426
|
|
|
|
|
|
|
sub asUnsigned { |
15427
|
0
|
|
|
0
|
|
|
my $o = shift; |
15428
|
0
|
|
0
|
|
|
|
CDS->unsignedFromBytes($o->{bytes}) // 0 } |
15429
|
|
|
|
|
|
|
sub asFloat { |
15430
|
0
|
|
|
0
|
|
|
my $o = shift; |
15431
|
0
|
|
0
|
|
|
|
CDS->floatFromBytes($o->{bytes}) // 0 } |
15432
|
|
|
|
|
|
|
|
15433
|
|
|
|
|
|
|
sub asHashAndKey { |
15434
|
0
|
|
|
0
|
|
|
my $o = shift; |
15435
|
|
|
|
|
|
|
|
15436
|
0
|
0
|
|
|
|
|
return if ! $o->{hash}; |
15437
|
0
|
0
|
|
|
|
|
return if length $o->{bytes} != 32; |
15438
|
0
|
|
|
|
|
|
return CDS::HashAndKey->new($o->{hash}, $o->{bytes}); |
15439
|
|
|
|
|
|
|
} |
15440
|
|
|
|
|
|
|
|
15441
|
|
|
|
|
|
|
sub bytesValue { |
15442
|
0
|
|
|
0
|
|
|
my $o = shift; |
15443
|
0
|
|
|
|
|
|
$o->firstChild->bytes } |
15444
|
|
|
|
|
|
|
sub hashValue { |
15445
|
0
|
|
|
0
|
|
|
my $o = shift; |
15446
|
0
|
|
|
|
|
|
$o->firstChild->hash } |
15447
|
|
|
|
|
|
|
sub textValue { |
15448
|
0
|
|
|
0
|
|
|
my $o = shift; |
15449
|
0
|
|
|
|
|
|
$o->firstChild->asText } |
15450
|
|
|
|
|
|
|
sub booleanValue { |
15451
|
0
|
|
|
0
|
|
|
my $o = shift; |
15452
|
0
|
|
|
|
|
|
$o->firstChild->asBoolean } |
15453
|
|
|
|
|
|
|
sub integerValue { |
15454
|
0
|
|
|
0
|
|
|
my $o = shift; |
15455
|
0
|
|
|
|
|
|
$o->firstChild->asInteger } |
15456
|
|
|
|
|
|
|
sub unsignedValue { |
15457
|
0
|
|
|
0
|
|
|
my $o = shift; |
15458
|
0
|
|
|
|
|
|
$o->firstChild->asUnsigned } |
15459
|
|
|
|
|
|
|
sub floatValue { |
15460
|
0
|
|
|
0
|
|
|
my $o = shift; |
15461
|
0
|
|
|
|
|
|
$o->firstChild->asFloat } |
15462
|
|
|
|
|
|
|
sub hashAndKeyValue { |
15463
|
0
|
|
|
0
|
|
|
my $o = shift; |
15464
|
0
|
|
|
|
|
|
$o->firstChild->asHashAndKey } |
15465
|
|
|
|
|
|
|
|
15466
|
|
|
|
|
|
|
# *** Dependent hashes |
15467
|
|
|
|
|
|
|
|
15468
|
|
|
|
|
|
|
sub dependentHashes { |
15469
|
0
|
|
|
0
|
|
|
my $o = shift; |
15470
|
|
|
|
|
|
|
|
15471
|
0
|
|
|
|
|
|
my $hashes = {}; |
15472
|
0
|
|
|
|
|
|
$o->traverseHashes($hashes); |
15473
|
0
|
|
|
|
|
|
return values %$hashes; |
15474
|
|
|
|
|
|
|
} |
15475
|
|
|
|
|
|
|
|
15476
|
|
|
|
|
|
|
sub traverseHashes { |
15477
|
0
|
|
|
0
|
|
|
my $o = shift; |
15478
|
0
|
|
|
|
|
|
my $hashes = shift; |
15479
|
|
|
|
|
|
|
# private |
15480
|
0
|
0
|
|
|
|
|
$hashes->{$o->{hash}->bytes} = $o->{hash} if $o->{hash}; |
15481
|
0
|
|
|
|
|
|
for my $child (@{$o->{children}}) { |
|
0
|
|
|
|
|
|
|
15482
|
0
|
|
|
|
|
|
$child->traverseHashes($hashes); |
15483
|
|
|
|
|
|
|
} |
15484
|
|
|
|
|
|
|
} |
15485
|
|
|
|
|
|
|
|
15486
|
|
|
|
|
|
|
# *** Size |
15487
|
|
|
|
|
|
|
|
15488
|
|
|
|
|
|
|
sub countEntries { |
15489
|
0
|
|
|
0
|
|
|
my $o = shift; |
15490
|
|
|
|
|
|
|
|
15491
|
0
|
|
|
|
|
|
my $count = 1; |
15492
|
0
|
|
|
|
|
|
for my $child (@{$o->{children}}) { $count += $child->countEntries; } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
15493
|
0
|
|
|
|
|
|
return $count; |
15494
|
|
|
|
|
|
|
} |
15495
|
|
|
|
|
|
|
|
15496
|
|
|
|
|
|
|
sub calculateSize { |
15497
|
0
|
|
|
0
|
|
|
my $o = shift; |
15498
|
|
|
|
|
|
|
|
15499
|
0
|
|
|
|
|
|
return 4 + $o->calculateSizeContribution; |
15500
|
|
|
|
|
|
|
} |
15501
|
|
|
|
|
|
|
|
15502
|
|
|
|
|
|
|
sub calculateSizeContribution { |
15503
|
0
|
|
|
0
|
|
|
my $o = shift; |
15504
|
|
|
|
|
|
|
# private |
15505
|
0
|
|
|
|
|
|
my $byteLength = length $o->{bytes}; |
15506
|
0
|
0
|
|
|
|
|
my $size = $byteLength < 30 ? 1 : $byteLength < 286 ? 2 : 9; |
|
|
0
|
|
|
|
|
|
15507
|
0
|
|
|
|
|
|
$size += $byteLength; |
15508
|
0
|
0
|
|
|
|
|
$size += 32 + 4 if $o->{hash}; |
15509
|
0
|
|
|
|
|
|
for my $child (@{$o->{children}}) { |
|
0
|
|
|
|
|
|
|
15510
|
0
|
|
|
|
|
|
$size += $child->calculateSizeContribution; |
15511
|
|
|
|
|
|
|
} |
15512
|
0
|
|
|
|
|
|
return $size; |
15513
|
|
|
|
|
|
|
} |
15514
|
|
|
|
|
|
|
|
15515
|
|
|
|
|
|
|
# *** Serialization |
15516
|
|
|
|
|
|
|
|
15517
|
|
|
|
|
|
|
# Serializes this record into a Condensation object. |
15518
|
|
|
|
|
|
|
sub toObject { |
15519
|
0
|
|
|
0
|
|
|
my $o = shift; |
15520
|
|
|
|
|
|
|
|
15521
|
0
|
|
|
|
|
|
my $writer = CDS::RecordWriter->new; |
15522
|
0
|
|
|
|
|
|
$writer->writeChildren($o); |
15523
|
0
|
|
|
|
|
|
return CDS::Object->create($writer->header, $writer->data); |
15524
|
|
|
|
|
|
|
} |
15525
|
|
|
|
|
|
|
|
15526
|
|
|
|
|
|
|
package CDS::RecordReader; |
15527
|
|
|
|
|
|
|
|
15528
|
|
|
|
|
|
|
sub new { |
15529
|
0
|
|
|
0
|
|
|
my $class = shift; |
15530
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
15531
|
|
|
|
|
|
|
|
15532
|
0
|
|
|
|
|
|
return bless { |
15533
|
|
|
|
|
|
|
object => $object, |
15534
|
|
|
|
|
|
|
data => $object->data, |
15535
|
|
|
|
|
|
|
pos => 0, |
15536
|
|
|
|
|
|
|
hasError => 0 |
15537
|
|
|
|
|
|
|
}; |
15538
|
|
|
|
|
|
|
} |
15539
|
|
|
|
|
|
|
|
15540
|
0
|
|
|
0
|
|
|
sub hasError { shift->{hasError} } |
15541
|
|
|
|
|
|
|
|
15542
|
|
|
|
|
|
|
sub readChildren { |
15543
|
0
|
|
|
0
|
|
|
my $o = shift; |
15544
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
15545
|
|
|
|
|
|
|
|
15546
|
0
|
|
|
|
|
|
while (1) { |
15547
|
|
|
|
|
|
|
# Flags |
15548
|
0
|
|
0
|
|
|
|
my $flags = $o->readUnsigned8 // return; |
15549
|
|
|
|
|
|
|
|
15550
|
|
|
|
|
|
|
# Data |
15551
|
0
|
|
|
|
|
|
my $length = $flags & 0x1f; |
15552
|
0
|
0
|
0
|
|
|
|
my $byteLength = $length == 30 ? 30 + ($o->readUnsigned8 // return) : $length == 31 ? ($o->readUnsigned64 // return) : $length; |
|
|
0
|
0
|
|
|
|
|
15553
|
0
|
|
|
|
|
|
my $bytes = $o->readBytes($byteLength); |
15554
|
0
|
0
|
0
|
|
|
|
my $hash = $flags & 0x20 ? $o->{object}->hashAtIndex($o->readUnsigned32 // return) : undef; |
15555
|
0
|
0
|
|
|
|
|
return if $o->{hasError}; |
15556
|
|
|
|
|
|
|
|
15557
|
|
|
|
|
|
|
# Children |
15558
|
0
|
|
|
|
|
|
my $child = $record->add($bytes, $hash); |
15559
|
0
|
0
|
0
|
|
|
|
return if $flags & 0x40 && ! $o->readChildren($child); |
15560
|
0
|
0
|
|
|
|
|
return 1 if ! ($flags & 0x80); |
15561
|
|
|
|
|
|
|
} |
15562
|
|
|
|
|
|
|
} |
15563
|
|
|
|
|
|
|
|
15564
|
|
|
|
|
|
|
sub use { |
15565
|
0
|
|
|
0
|
|
|
my $o = shift; |
15566
|
0
|
|
|
|
|
|
my $length = shift; |
15567
|
|
|
|
|
|
|
|
15568
|
0
|
|
|
|
|
|
my $start = $o->{pos}; |
15569
|
0
|
|
|
|
|
|
$o->{pos} += $length; |
15570
|
0
|
0
|
|
|
|
|
return substr($o->{data}, $start, $length) if $o->{pos} <= length $o->{data}; |
15571
|
0
|
|
|
|
|
|
$o->{hasError} = 1; |
15572
|
0
|
|
|
|
|
|
return; |
15573
|
|
|
|
|
|
|
} |
15574
|
|
|
|
|
|
|
|
15575
|
|
|
|
|
|
|
sub readUnsigned8 { |
15576
|
0
|
|
|
0
|
|
|
my $o = shift; |
15577
|
0
|
|
0
|
|
|
|
unpack('C', $o->use(1) // return) } |
15578
|
|
|
|
|
|
|
sub readUnsigned32 { |
15579
|
0
|
|
|
0
|
|
|
my $o = shift; |
15580
|
0
|
|
0
|
|
|
|
unpack('L>', $o->use(4) // return) } |
15581
|
|
|
|
|
|
|
sub readUnsigned64 { |
15582
|
0
|
|
|
0
|
|
|
my $o = shift; |
15583
|
0
|
|
0
|
|
|
|
unpack('Q>', $o->use(8) // return) } |
15584
|
|
|
|
|
|
|
sub readBytes { |
15585
|
0
|
|
|
0
|
|
|
my $o = shift; |
15586
|
0
|
|
|
|
|
|
my $length = shift; |
15587
|
0
|
|
|
|
|
|
$o->use($length) } |
15588
|
|
|
|
|
|
|
sub trailer { |
15589
|
0
|
|
|
0
|
|
|
my $o = shift; |
15590
|
0
|
|
|
|
|
|
substr($o->{data}, $o->{pos}) } |
15591
|
|
|
|
|
|
|
|
15592
|
|
|
|
|
|
|
package CDS::RecordWriter; |
15593
|
|
|
|
|
|
|
|
15594
|
|
|
|
|
|
|
sub new { |
15595
|
0
|
|
|
0
|
|
|
my $class = shift; |
15596
|
|
|
|
|
|
|
|
15597
|
0
|
|
|
|
|
|
return bless { |
15598
|
|
|
|
|
|
|
hashesCount => 0, |
15599
|
|
|
|
|
|
|
hashes => '', |
15600
|
|
|
|
|
|
|
data => '' |
15601
|
|
|
|
|
|
|
}; |
15602
|
|
|
|
|
|
|
} |
15603
|
|
|
|
|
|
|
|
15604
|
|
|
|
|
|
|
sub header { |
15605
|
0
|
|
|
0
|
|
|
my $o = shift; |
15606
|
0
|
|
|
|
|
|
pack('L>', $o->{hashesCount}).$o->{hashes} } |
15607
|
0
|
|
|
0
|
|
|
sub data { shift->{data} } |
15608
|
|
|
|
|
|
|
|
15609
|
|
|
|
|
|
|
sub writeChildren { |
15610
|
0
|
|
|
0
|
|
|
my $o = shift; |
15611
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
15612
|
|
|
|
|
|
|
|
15613
|
0
|
|
|
|
|
|
my @children = @{$record->{children}}; |
|
0
|
|
|
|
|
|
|
15614
|
0
|
0
|
|
|
|
|
return if ! scalar @children; |
15615
|
0
|
|
|
|
|
|
my $lastChild = pop @children; |
15616
|
0
|
|
|
|
|
|
for my $child (@children) { $o->writeNode($child, 1); } |
|
0
|
|
|
|
|
|
|
15617
|
0
|
|
|
|
|
|
$o->writeNode($lastChild, 0); |
15618
|
|
|
|
|
|
|
} |
15619
|
|
|
|
|
|
|
|
15620
|
|
|
|
|
|
|
sub writeNode { |
15621
|
0
|
|
|
0
|
|
|
my $o = shift; |
15622
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
15623
|
0
|
|
|
|
|
|
my $hasMoreSiblings = shift; |
15624
|
|
|
|
|
|
|
|
15625
|
|
|
|
|
|
|
# Flags |
15626
|
0
|
|
|
|
|
|
my $byteLength = length $record->{bytes}; |
15627
|
0
|
0
|
|
|
|
|
my $flags = $byteLength < 30 ? $byteLength : $byteLength < 286 ? 30 : 31; |
|
|
0
|
|
|
|
|
|
15628
|
0
|
0
|
|
|
|
|
$flags |= 0x20 if defined $record->{hash}; |
15629
|
0
|
|
|
|
|
|
my $countChildren = scalar @{$record->{children}}; |
|
0
|
|
|
|
|
|
|
15630
|
0
|
0
|
|
|
|
|
$flags |= 0x40 if $countChildren; |
15631
|
0
|
0
|
|
|
|
|
$flags |= 0x80 if $hasMoreSiblings; |
15632
|
0
|
|
|
|
|
|
$o->writeUnsigned8($flags); |
15633
|
|
|
|
|
|
|
|
15634
|
|
|
|
|
|
|
# Data |
15635
|
0
|
0
|
|
|
|
|
$o->writeUnsigned8($byteLength - 30) if ($flags & 0x1f) == 30; |
15636
|
0
|
0
|
|
|
|
|
$o->writeUnsigned64($byteLength) if ($flags & 0x1f) == 31; |
15637
|
0
|
|
|
|
|
|
$o->writeBytes($record->{bytes}); |
15638
|
0
|
0
|
|
|
|
|
$o->writeUnsigned32($o->addHash($record->{hash})) if $flags & 0x20; |
15639
|
|
|
|
|
|
|
|
15640
|
|
|
|
|
|
|
# Children |
15641
|
0
|
|
|
|
|
|
$o->writeChildren($record); |
15642
|
|
|
|
|
|
|
} |
15643
|
|
|
|
|
|
|
|
15644
|
|
|
|
|
|
|
sub writeUnsigned8 { |
15645
|
0
|
|
|
0
|
|
|
my $o = shift; |
15646
|
0
|
|
|
|
|
|
my $value = shift; |
15647
|
0
|
|
|
|
|
|
$o->{data} .= pack('C', $value) } |
15648
|
|
|
|
|
|
|
sub writeUnsigned32 { |
15649
|
0
|
|
|
0
|
|
|
my $o = shift; |
15650
|
0
|
|
|
|
|
|
my $value = shift; |
15651
|
0
|
|
|
|
|
|
$o->{data} .= pack('L>', $value) } |
15652
|
|
|
|
|
|
|
sub writeUnsigned64 { |
15653
|
0
|
|
|
0
|
|
|
my $o = shift; |
15654
|
0
|
|
|
|
|
|
my $value = shift; |
15655
|
0
|
|
|
|
|
|
$o->{data} .= pack('Q>', $value) } |
15656
|
|
|
|
|
|
|
|
15657
|
|
|
|
|
|
|
sub writeBytes { |
15658
|
0
|
|
|
0
|
|
|
my $o = shift; |
15659
|
0
|
|
|
|
|
|
my $bytes = shift; |
15660
|
|
|
|
|
|
|
|
15661
|
0
|
0
|
|
|
|
|
warn $bytes.' is a utf8 string, not a byte string.' if utf8::is_utf8($bytes); |
15662
|
0
|
|
|
|
|
|
$o->{data} .= $bytes; |
15663
|
|
|
|
|
|
|
} |
15664
|
|
|
|
|
|
|
|
15665
|
|
|
|
|
|
|
sub addHash { |
15666
|
0
|
|
|
0
|
|
|
my $o = shift; |
15667
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15668
|
|
|
|
|
|
|
|
15669
|
0
|
|
|
|
|
|
my $index = $o->{hashesCount}; |
15670
|
0
|
|
|
|
|
|
$o->{hashes} .= $hash->bytes; |
15671
|
0
|
|
|
|
|
|
$o->{hashesCount} += 1; |
15672
|
0
|
|
|
|
|
|
return $index; |
15673
|
|
|
|
|
|
|
} |
15674
|
|
|
|
|
|
|
|
15675
|
|
|
|
|
|
|
package CDS::RootDocument; |
15676
|
|
|
|
|
|
|
|
15677
|
1
|
|
|
1
|
|
13517
|
use parent -norequire, 'CDS::Document'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
8
|
|
15678
|
|
|
|
|
|
|
|
15679
|
|
|
|
|
|
|
sub new { |
15680
|
0
|
|
|
0
|
|
|
my $class = shift; |
15681
|
0
|
|
|
|
|
|
my $privateRoot = shift; |
15682
|
0
|
|
|
|
|
|
my $label = shift; |
15683
|
|
|
|
|
|
|
|
15684
|
0
|
|
|
|
|
|
my $o = $class->SUPER::new($privateRoot->privateBoxReader->keyPair, $privateRoot->unsaved); |
15685
|
0
|
|
|
|
|
|
$o->{privateRoot} = $privateRoot; |
15686
|
0
|
|
|
|
|
|
$o->{label} = $label; |
15687
|
0
|
|
|
|
|
|
$privateRoot->addDataHandler($label, $o); |
15688
|
|
|
|
|
|
|
|
15689
|
|
|
|
|
|
|
# State |
15690
|
0
|
|
|
|
|
|
$o->{dataSharingMessage} = undef; |
15691
|
0
|
|
|
|
|
|
return $o; |
15692
|
|
|
|
|
|
|
} |
15693
|
|
|
|
|
|
|
|
15694
|
0
|
|
|
0
|
|
|
sub privateRoot { shift->{privateRoot} } |
15695
|
0
|
|
|
0
|
|
|
sub label { shift->{label} } |
15696
|
|
|
|
|
|
|
|
15697
|
|
|
|
|
|
|
sub savingDone { |
15698
|
0
|
|
|
0
|
|
|
my $o = shift; |
15699
|
0
|
|
|
|
|
|
my $revision = shift; |
15700
|
0
|
|
|
|
|
|
my $newPart = shift; |
15701
|
0
|
|
|
|
|
|
my $obsoleteParts = shift; |
15702
|
|
|
|
|
|
|
|
15703
|
0
|
|
|
|
|
|
$o->{privateRoot}->unsaved->state->merge($o->{unsaved}->savingState); |
15704
|
0
|
|
|
|
|
|
$o->{unsaved}->savingDone; |
15705
|
0
|
0
|
0
|
|
|
|
$o->{privateRoot}->dataChanged if $newPart || scalar @$obsoleteParts; |
15706
|
|
|
|
|
|
|
} |
15707
|
|
|
|
|
|
|
|
15708
|
|
|
|
|
|
|
sub addDataTo { |
15709
|
0
|
|
|
0
|
|
|
my $o = shift; |
15710
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
15711
|
|
|
|
|
|
|
|
15712
|
0
|
|
|
|
|
|
for my $part (sort { $a->{hashAndKey}->hash->bytes cmp $b->{hashAndKey}->hash->bytes } values %{$o->{parts}}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
15713
|
0
|
|
|
|
|
|
$record->addHashAndKey($part->{hashAndKey}); |
15714
|
|
|
|
|
|
|
} |
15715
|
|
|
|
|
|
|
} |
15716
|
|
|
|
|
|
|
sub mergeData { |
15717
|
0
|
|
|
0
|
|
|
my $o = shift; |
15718
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
15719
|
|
|
|
|
|
|
|
15720
|
0
|
|
|
|
|
|
my @hashesAndKeys; |
15721
|
0
|
|
|
|
|
|
for my $child ($record->children) { |
15722
|
0
|
|
0
|
|
|
|
push @hashesAndKeys, $child->asHashAndKey // next; |
15723
|
|
|
|
|
|
|
} |
15724
|
|
|
|
|
|
|
|
15725
|
0
|
|
|
|
|
|
$o->merge(@hashesAndKeys); |
15726
|
|
|
|
|
|
|
} |
15727
|
|
|
|
|
|
|
|
15728
|
|
|
|
|
|
|
sub mergeExternalData { |
15729
|
0
|
|
|
0
|
|
|
my $o = shift; |
15730
|
0
|
|
|
|
|
|
my $store = shift; |
15731
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
15732
|
0
|
0
|
0
|
|
|
|
my $source = shift; die 'wrong type '.ref($source).' for $source' if defined $source && ref $source ne 'CDS::Source'; |
|
0
|
|
|
|
|
|
|
15733
|
|
|
|
|
|
|
|
15734
|
0
|
|
|
|
|
|
my @hashes; |
15735
|
|
|
|
|
|
|
my @hashesAndKeys; |
15736
|
0
|
|
|
|
|
|
for my $child ($record->children) { |
15737
|
0
|
|
0
|
|
|
|
my $hashAndKey = $child->asHashAndKey // next; |
15738
|
0
|
0
|
|
|
|
|
next if $o->{parts}->{$hashAndKey->hash->bytes}; |
15739
|
0
|
|
|
|
|
|
push @hashes, $hashAndKey->hash; |
15740
|
0
|
|
|
|
|
|
push @hashesAndKeys, $hashAndKey; |
15741
|
|
|
|
|
|
|
} |
15742
|
|
|
|
|
|
|
|
15743
|
0
|
|
|
|
|
|
my ($missing, $transferStore, $storeError) = $o->{keyPair}->transfer([@hashes], $store, $o->{privateRoot}->unsaved); |
15744
|
0
|
0
|
|
|
|
|
return if defined $storeError; |
15745
|
0
|
0
|
|
|
|
|
return if $missing; |
15746
|
|
|
|
|
|
|
|
15747
|
0
|
0
|
|
|
|
|
if ($source) { |
15748
|
0
|
|
|
|
|
|
$source->keep; |
15749
|
0
|
|
|
|
|
|
$o->{privateRoot}->unsaved->state->addMergedSource($source); |
15750
|
|
|
|
|
|
|
} |
15751
|
|
|
|
|
|
|
|
15752
|
0
|
|
|
|
|
|
$o->merge(@hashesAndKeys); |
15753
|
0
|
|
|
|
|
|
return 1; |
15754
|
|
|
|
|
|
|
} |
15755
|
|
|
|
|
|
|
|
15756
|
|
|
|
|
|
|
package CDS::Selector; |
15757
|
|
|
|
|
|
|
|
15758
|
|
|
|
|
|
|
sub root { |
15759
|
0
|
|
|
0
|
|
|
my $class = shift; |
15760
|
0
|
|
|
|
|
|
my $document = shift; |
15761
|
|
|
|
|
|
|
|
15762
|
0
|
|
|
|
|
|
return bless {document => $document, id => 'ROOT', label => ''}; |
15763
|
|
|
|
|
|
|
} |
15764
|
|
|
|
|
|
|
|
15765
|
0
|
|
|
0
|
|
|
sub document { shift->{document} } |
15766
|
0
|
|
|
0
|
|
|
sub parent { shift->{parent} } |
15767
|
0
|
|
|
0
|
|
|
sub label { shift->{label} } |
15768
|
|
|
|
|
|
|
|
15769
|
|
|
|
|
|
|
sub child { |
15770
|
0
|
|
|
0
|
|
|
my $o = shift; |
15771
|
0
|
|
|
|
|
|
my $label = shift; |
15772
|
|
|
|
|
|
|
|
15773
|
|
|
|
|
|
|
return bless { |
15774
|
|
|
|
|
|
|
document => $o->{document}, |
15775
|
0
|
|
|
|
|
|
id => $o->{id}.'/'.unpack('H*', $label), |
15776
|
|
|
|
|
|
|
parent => $o, |
15777
|
|
|
|
|
|
|
label => $label, |
15778
|
|
|
|
|
|
|
}; |
15779
|
|
|
|
|
|
|
} |
15780
|
|
|
|
|
|
|
|
15781
|
|
|
|
|
|
|
sub childWithText { |
15782
|
0
|
|
|
0
|
|
|
my $o = shift; |
15783
|
0
|
|
|
|
|
|
my $label = shift; |
15784
|
|
|
|
|
|
|
|
15785
|
0
|
|
0
|
|
|
|
return $o->child(Encode::encode_utf8($label // '')); |
15786
|
|
|
|
|
|
|
} |
15787
|
|
|
|
|
|
|
|
15788
|
|
|
|
|
|
|
sub children { |
15789
|
0
|
|
|
0
|
|
|
my $o = shift; |
15790
|
|
|
|
|
|
|
|
15791
|
0
|
|
0
|
|
|
|
my $item = $o->{document}->get($o) // return; |
15792
|
0
|
|
|
|
|
|
return map { $_->{selector} } @{$item->{children}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
15793
|
|
|
|
|
|
|
} |
15794
|
|
|
|
|
|
|
|
15795
|
|
|
|
|
|
|
# Value |
15796
|
|
|
|
|
|
|
|
15797
|
|
|
|
|
|
|
sub revision { |
15798
|
0
|
|
|
0
|
|
|
my $o = shift; |
15799
|
|
|
|
|
|
|
|
15800
|
0
|
|
0
|
|
|
|
my $item = $o->{document}->get($o) // return 0; |
15801
|
0
|
|
|
|
|
|
return $item->{revision}; |
15802
|
|
|
|
|
|
|
} |
15803
|
|
|
|
|
|
|
|
15804
|
|
|
|
|
|
|
sub isSet { |
15805
|
0
|
|
|
0
|
|
|
my $o = shift; |
15806
|
|
|
|
|
|
|
|
15807
|
0
|
|
0
|
|
|
|
my $item = $o->{document}->get($o) // return; |
15808
|
0
|
|
|
|
|
|
return scalar $item->{record}->children > 0; |
15809
|
|
|
|
|
|
|
} |
15810
|
|
|
|
|
|
|
|
15811
|
|
|
|
|
|
|
sub record { |
15812
|
0
|
|
|
0
|
|
|
my $o = shift; |
15813
|
|
|
|
|
|
|
|
15814
|
0
|
|
0
|
|
|
|
my $item = $o->{document}->get($o) // return CDS::Record->new; |
15815
|
0
|
|
|
|
|
|
return $item->{record}; |
15816
|
|
|
|
|
|
|
} |
15817
|
|
|
|
|
|
|
|
15818
|
|
|
|
|
|
|
sub set { |
15819
|
0
|
|
|
0
|
|
|
my $o = shift; |
15820
|
0
|
0
|
0
|
|
|
|
my $record = shift // return; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
0
|
|
|
|
|
15821
|
|
|
|
|
|
|
|
15822
|
0
|
|
|
|
|
|
my $now = CDS->now; |
15823
|
0
|
|
|
|
|
|
my $item = $o->{document}->getOrCreate($o); |
15824
|
0
|
0
|
|
|
|
|
$item->mergeValue($o->{document}->{changes}, $item->{revision} >= $now ? $item->{revision} + 1 : $now, $record); |
15825
|
|
|
|
|
|
|
} |
15826
|
|
|
|
|
|
|
|
15827
|
|
|
|
|
|
|
sub merge { |
15828
|
0
|
|
|
0
|
|
|
my $o = shift; |
15829
|
0
|
|
|
|
|
|
my $revision = shift; |
15830
|
0
|
0
|
0
|
|
|
|
my $record = shift // return; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
0
|
|
|
|
|
15831
|
|
|
|
|
|
|
|
15832
|
0
|
|
|
|
|
|
my $item = $o->{document}->getOrCreate($o); |
15833
|
0
|
|
|
|
|
|
return $item->mergeValue($o->{document}->{changes}, $revision, $record); |
15834
|
|
|
|
|
|
|
} |
15835
|
|
|
|
|
|
|
|
15836
|
|
|
|
|
|
|
sub clear { |
15837
|
0
|
|
|
0
|
|
|
my $o = shift; |
15838
|
0
|
|
|
|
|
|
$o->set(CDS::Record->new) } |
15839
|
|
|
|
|
|
|
|
15840
|
|
|
|
|
|
|
sub clearInThePast { |
15841
|
0
|
|
|
0
|
|
|
my $o = shift; |
15842
|
|
|
|
|
|
|
|
15843
|
0
|
0
|
|
|
|
|
$o->merge($o->revision + 1, CDS::Record->new) if $o->isSet; |
15844
|
|
|
|
|
|
|
} |
15845
|
|
|
|
|
|
|
|
15846
|
|
|
|
|
|
|
sub forget { |
15847
|
0
|
|
|
0
|
|
|
my $o = shift; |
15848
|
|
|
|
|
|
|
|
15849
|
0
|
|
0
|
|
|
|
my $item = $o->{document}->get($o) // return; |
15850
|
0
|
|
|
|
|
|
$item->forget; |
15851
|
|
|
|
|
|
|
} |
15852
|
|
|
|
|
|
|
|
15853
|
|
|
|
|
|
|
sub forgetBranch { |
15854
|
0
|
|
|
0
|
|
|
my $o = shift; |
15855
|
|
|
|
|
|
|
|
15856
|
0
|
|
|
|
|
|
for my $child ($o->children) { $child->forgetBranch; } |
|
0
|
|
|
|
|
|
|
15857
|
0
|
|
|
|
|
|
$o->forget; |
15858
|
|
|
|
|
|
|
} |
15859
|
|
|
|
|
|
|
|
15860
|
|
|
|
|
|
|
# Convenience methods (simple interface) |
15861
|
|
|
|
|
|
|
|
15862
|
|
|
|
|
|
|
sub firstValue { |
15863
|
0
|
|
|
0
|
|
|
my $o = shift; |
15864
|
|
|
|
|
|
|
|
15865
|
0
|
|
0
|
|
|
|
my $item = $o->{document}->get($o) // return CDS::Record->new; |
15866
|
0
|
|
|
|
|
|
return $item->{record}->firstChild; |
15867
|
|
|
|
|
|
|
} |
15868
|
|
|
|
|
|
|
|
15869
|
|
|
|
|
|
|
sub bytesValue { |
15870
|
0
|
|
|
0
|
|
|
my $o = shift; |
15871
|
0
|
|
|
|
|
|
$o->firstValue->bytes } |
15872
|
|
|
|
|
|
|
sub hashValue { |
15873
|
0
|
|
|
0
|
|
|
my $o = shift; |
15874
|
0
|
|
|
|
|
|
$o->firstValue->hash } |
15875
|
|
|
|
|
|
|
sub textValue { |
15876
|
0
|
|
|
0
|
|
|
my $o = shift; |
15877
|
0
|
|
|
|
|
|
$o->firstValue->asText } |
15878
|
|
|
|
|
|
|
sub booleanValue { |
15879
|
0
|
|
|
0
|
|
|
my $o = shift; |
15880
|
0
|
|
|
|
|
|
$o->firstValue->asBoolean } |
15881
|
|
|
|
|
|
|
sub integerValue { |
15882
|
0
|
|
|
0
|
|
|
my $o = shift; |
15883
|
0
|
|
|
|
|
|
$o->firstValue->asInteger } |
15884
|
|
|
|
|
|
|
sub unsignedValue { |
15885
|
0
|
|
|
0
|
|
|
my $o = shift; |
15886
|
0
|
|
|
|
|
|
$o->firstValue->asUnsigned } |
15887
|
|
|
|
|
|
|
sub floatValue { |
15888
|
0
|
|
|
0
|
|
|
my $o = shift; |
15889
|
0
|
|
|
|
|
|
$o->firstValue->asFloat } |
15890
|
|
|
|
|
|
|
sub hashAndKeyValue { |
15891
|
0
|
|
|
0
|
|
|
my $o = shift; |
15892
|
0
|
|
|
|
|
|
$o->firstValue->asHashAndKey } |
15893
|
|
|
|
|
|
|
|
15894
|
|
|
|
|
|
|
# Sets a new value unless the node has that value already. |
15895
|
|
|
|
|
|
|
sub setBytes { |
15896
|
0
|
|
|
0
|
|
|
my $o = shift; |
15897
|
0
|
|
|
|
|
|
my $bytes = shift; |
15898
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15899
|
|
|
|
|
|
|
|
15900
|
0
|
|
|
|
|
|
my $record = CDS::Record->new; |
15901
|
0
|
|
|
|
|
|
$record->add($bytes, $hash); |
15902
|
0
|
|
|
|
|
|
$o->set($record); |
15903
|
|
|
|
|
|
|
} |
15904
|
|
|
|
|
|
|
|
15905
|
|
|
|
|
|
|
sub setHash { |
15906
|
0
|
|
|
0
|
|
|
my $o = shift; |
15907
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15908
|
0
|
|
|
|
|
|
$o->setBytes('', $hash); }; |
15909
|
|
|
|
|
|
|
sub setText { |
15910
|
0
|
|
|
0
|
|
|
my $o = shift; |
15911
|
0
|
|
|
|
|
|
my $value = shift; |
15912
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15913
|
0
|
|
|
|
|
|
$o->setBytes(Encode::encode_utf8($value), $hash); }; |
15914
|
|
|
|
|
|
|
sub setBoolean { |
15915
|
0
|
|
|
0
|
|
|
my $o = shift; |
15916
|
0
|
|
|
|
|
|
my $value = shift; |
15917
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15918
|
0
|
|
|
|
|
|
$o->setBytes(CDS->bytesFromBoolean($value), $hash); }; |
15919
|
|
|
|
|
|
|
sub setInteger { |
15920
|
0
|
|
|
0
|
|
|
my $o = shift; |
15921
|
0
|
|
|
|
|
|
my $value = shift; |
15922
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15923
|
0
|
|
|
|
|
|
$o->setBytes(CDS->bytesFromInteger($value), $hash); }; |
15924
|
|
|
|
|
|
|
sub setUnsigned { |
15925
|
0
|
|
|
0
|
|
|
my $o = shift; |
15926
|
0
|
|
|
|
|
|
my $value = shift; |
15927
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15928
|
0
|
|
|
|
|
|
$o->setBytes(CDS->bytesFromUnsigned($value), $hash); }; |
15929
|
|
|
|
|
|
|
sub setFloat32 { |
15930
|
0
|
|
|
0
|
|
|
my $o = shift; |
15931
|
0
|
|
|
|
|
|
my $value = shift; |
15932
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15933
|
0
|
|
|
|
|
|
$o->setBytes(CDS->bytesFromFloat32($value), $hash); }; |
15934
|
|
|
|
|
|
|
sub setFloat64 { |
15935
|
0
|
|
|
0
|
|
|
my $o = shift; |
15936
|
0
|
|
|
|
|
|
my $value = shift; |
15937
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15938
|
0
|
|
|
|
|
|
$o->setBytes(CDS->bytesFromFloat64($value), $hash); }; |
15939
|
|
|
|
|
|
|
sub setHashAndKey { |
15940
|
0
|
|
|
0
|
|
|
my $o = shift; |
15941
|
0
|
0
|
0
|
|
|
|
my $hashAndKey = shift; die 'wrong type '.ref($hashAndKey).' for $hashAndKey' if defined $hashAndKey && ref $hashAndKey ne 'CDS::HashAndKey'; |
|
0
|
|
|
|
|
|
|
15942
|
0
|
|
|
|
|
|
$o->setBytes($hashAndKey->key, $hashAndKey->hash); }; |
15943
|
|
|
|
|
|
|
|
15944
|
|
|
|
|
|
|
# Adding objects and merged sources |
15945
|
|
|
|
|
|
|
|
15946
|
|
|
|
|
|
|
sub addObject { |
15947
|
0
|
|
|
0
|
|
|
my $o = shift; |
15948
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15949
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
15950
|
|
|
|
|
|
|
|
15951
|
0
|
|
|
|
|
|
$o->{document}->{unsaved}->state->addObject($hash, $object); |
15952
|
|
|
|
|
|
|
} |
15953
|
|
|
|
|
|
|
|
15954
|
|
|
|
|
|
|
sub addMergedSource { |
15955
|
0
|
|
|
0
|
|
|
my $o = shift; |
15956
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15957
|
|
|
|
|
|
|
|
15958
|
0
|
|
|
|
|
|
$o->{document}->{unsaved}->state->addMergedSource($hash); |
15959
|
|
|
|
|
|
|
} |
15960
|
|
|
|
|
|
|
|
15961
|
|
|
|
|
|
|
package CDS::SentItem; |
15962
|
|
|
|
|
|
|
|
15963
|
1
|
|
|
1
|
|
2144
|
use parent -norequire, 'CDS::UnionList::Item'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
15964
|
|
|
|
|
|
|
|
15965
|
|
|
|
|
|
|
sub new { |
15966
|
0
|
|
|
0
|
|
|
my $class = shift; |
15967
|
0
|
|
|
|
|
|
my $unionList = shift; |
15968
|
0
|
|
|
|
|
|
my $id = shift; |
15969
|
|
|
|
|
|
|
|
15970
|
0
|
|
|
|
|
|
my $o = $class->SUPER::new($unionList, $id); |
15971
|
0
|
|
|
|
|
|
$o->{validUntil} = 0; |
15972
|
0
|
|
|
|
|
|
$o->{message} = CDS::Record->new; |
15973
|
0
|
|
|
|
|
|
return $o; |
15974
|
|
|
|
|
|
|
} |
15975
|
|
|
|
|
|
|
|
15976
|
0
|
|
|
0
|
|
|
sub validUntil { shift->{validUntil} } |
15977
|
|
|
|
|
|
|
sub envelopeHash { |
15978
|
0
|
|
|
0
|
|
|
my $o = shift; |
15979
|
0
|
|
|
|
|
|
CDS::Hash->fromBytes($o->{message}->bytes) } |
15980
|
|
|
|
|
|
|
sub envelopeHashBytes { |
15981
|
0
|
|
|
0
|
|
|
my $o = shift; |
15982
|
0
|
|
|
|
|
|
$o->{message}->bytes } |
15983
|
0
|
|
|
0
|
|
|
sub message { shift->{message} } |
15984
|
|
|
|
|
|
|
|
15985
|
|
|
|
|
|
|
sub addToRecord { |
15986
|
0
|
|
|
0
|
|
|
my $o = shift; |
15987
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
15988
|
|
|
|
|
|
|
|
15989
|
0
|
|
|
|
|
|
$record->add($o->{id})->addInteger($o->{validUntil})->addRecord($o->{message}); |
15990
|
|
|
|
|
|
|
} |
15991
|
|
|
|
|
|
|
|
15992
|
|
|
|
|
|
|
sub set { |
15993
|
0
|
|
|
0
|
|
|
my $o = shift; |
15994
|
0
|
|
|
|
|
|
my $validUntil = shift; |
15995
|
0
|
0
|
0
|
|
|
|
my $envelopeHash = shift; die 'wrong type '.ref($envelopeHash).' for $envelopeHash' if defined $envelopeHash && ref $envelopeHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
15996
|
0
|
0
|
0
|
|
|
|
my $messageRecord = shift; die 'wrong type '.ref($messageRecord).' for $messageRecord' if defined $messageRecord && ref $messageRecord ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
15997
|
|
|
|
|
|
|
|
15998
|
0
|
|
|
|
|
|
my $message = CDS::Record->new($envelopeHash->bytes); |
15999
|
0
|
|
|
|
|
|
$message->addRecord($messageRecord->children); |
16000
|
0
|
|
|
|
|
|
$o->merge($o->{unionList}->{changes}, CDS->max($validUntil, $o->{validUntil} + 1), $message); |
16001
|
|
|
|
|
|
|
} |
16002
|
|
|
|
|
|
|
|
16003
|
|
|
|
|
|
|
sub clear { |
16004
|
0
|
|
|
0
|
|
|
my $o = shift; |
16005
|
0
|
|
|
|
|
|
my $validUntil = shift; |
16006
|
|
|
|
|
|
|
|
16007
|
0
|
|
|
|
|
|
$o->merge($o->{unionList}->{changes}, CDS->max($validUntil, $o->{validUntil} + 1), CDS::Record->new); |
16008
|
|
|
|
|
|
|
} |
16009
|
|
|
|
|
|
|
|
16010
|
|
|
|
|
|
|
sub merge { |
16011
|
0
|
|
|
0
|
|
|
my $o = shift; |
16012
|
0
|
|
|
|
|
|
my $part = shift; |
16013
|
0
|
|
|
|
|
|
my $validUntil = shift; |
16014
|
0
|
|
|
|
|
|
my $message = shift; |
16015
|
|
|
|
|
|
|
|
16016
|
0
|
0
|
|
|
|
|
return if $o->{validUntil} > $validUntil; |
16017
|
0
|
0
|
0
|
|
|
|
return if $o->{validUntil} == $validUntil && $part->{size} < $o->{part}->{size}; |
16018
|
0
|
|
|
|
|
|
$o->{validUntil} = $validUntil; |
16019
|
0
|
|
|
|
|
|
$o->{message} = $message; |
16020
|
0
|
|
|
|
|
|
$o->setPart($part); |
16021
|
|
|
|
|
|
|
} |
16022
|
|
|
|
|
|
|
|
16023
|
|
|
|
|
|
|
package CDS::SentList; |
16024
|
|
|
|
|
|
|
|
16025
|
1
|
|
|
1
|
|
507
|
use parent -norequire, 'CDS::UnionList'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
16026
|
|
|
|
|
|
|
|
16027
|
|
|
|
|
|
|
sub new { |
16028
|
0
|
|
|
0
|
|
|
my $class = shift; |
16029
|
0
|
|
|
|
|
|
my $privateRoot = shift; |
16030
|
|
|
|
|
|
|
|
16031
|
0
|
|
|
|
|
|
return $class->SUPER::new($privateRoot, 'sent list'); |
16032
|
|
|
|
|
|
|
} |
16033
|
|
|
|
|
|
|
|
16034
|
|
|
|
|
|
|
sub createItem { |
16035
|
0
|
|
|
0
|
|
|
my $o = shift; |
16036
|
0
|
|
|
|
|
|
my $id = shift; |
16037
|
|
|
|
|
|
|
|
16038
|
0
|
|
|
|
|
|
return CDS::SentItem->new($o, $id); |
16039
|
|
|
|
|
|
|
} |
16040
|
|
|
|
|
|
|
|
16041
|
|
|
|
|
|
|
sub mergeRecord { |
16042
|
0
|
|
|
0
|
|
|
my $o = shift; |
16043
|
0
|
|
|
|
|
|
my $part = shift; |
16044
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
16045
|
|
|
|
|
|
|
|
16046
|
0
|
|
|
|
|
|
my $item = $o->getOrCreate($record->bytes); |
16047
|
0
|
|
|
|
|
|
for my $child ($record->children) { |
16048
|
0
|
|
|
|
|
|
my $validUntil = $child->asInteger; |
16049
|
0
|
|
|
|
|
|
my $message = $child->firstChild; |
16050
|
0
|
|
|
|
|
|
$item->merge($part, $validUntil, $message); |
16051
|
|
|
|
|
|
|
} |
16052
|
|
|
|
|
|
|
} |
16053
|
|
|
|
|
|
|
|
16054
|
|
|
|
|
|
|
sub forgetObsoleteItems { |
16055
|
0
|
|
|
0
|
|
|
my $o = shift; |
16056
|
|
|
|
|
|
|
|
16057
|
0
|
|
|
|
|
|
my $now = CDS->now; |
16058
|
0
|
|
|
|
|
|
my $toDelete = []; |
16059
|
0
|
|
|
|
|
|
for my $item (values %{$o->{items}}) { |
|
0
|
|
|
|
|
|
|
16060
|
0
|
0
|
|
|
|
|
next if $item->{validUntil} >= $now; |
16061
|
0
|
|
|
|
|
|
$o->forgetItem($item); |
16062
|
|
|
|
|
|
|
} |
16063
|
|
|
|
|
|
|
} |
16064
|
|
|
|
|
|
|
|
16065
|
|
|
|
|
|
|
package CDS::Source; |
16066
|
|
|
|
|
|
|
|
16067
|
|
|
|
|
|
|
sub new { |
16068
|
0
|
|
|
0
|
|
|
my $class = shift; |
16069
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
16070
|
0
|
0
|
0
|
|
|
|
my $actorOnStore = shift; die 'wrong type '.ref($actorOnStore).' for $actorOnStore' if defined $actorOnStore && ref $actorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
|
|
16071
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
16072
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16073
|
|
|
|
|
|
|
|
16074
|
0
|
|
|
|
|
|
return bless { |
16075
|
|
|
|
|
|
|
keyPair => $keyPair, |
16076
|
|
|
|
|
|
|
actorOnStore => $actorOnStore, |
16077
|
|
|
|
|
|
|
boxLabel => $boxLabel, |
16078
|
|
|
|
|
|
|
hash => $hash, |
16079
|
|
|
|
|
|
|
referenceCount => 1, |
16080
|
|
|
|
|
|
|
}; |
16081
|
|
|
|
|
|
|
} |
16082
|
|
|
|
|
|
|
|
16083
|
0
|
|
|
0
|
|
|
sub keyPair { shift->{keyPair} } |
16084
|
0
|
|
|
0
|
|
|
sub actorOnStore { shift->{actorOnStore} } |
16085
|
0
|
|
|
0
|
|
|
sub boxLabel { shift->{boxLabel} } |
16086
|
0
|
|
|
0
|
|
|
sub hash { shift->{hash} } |
16087
|
0
|
|
|
0
|
|
|
sub referenceCount { shift->{referenceCount} } |
16088
|
|
|
|
|
|
|
|
16089
|
|
|
|
|
|
|
sub keep { |
16090
|
0
|
|
|
0
|
|
|
my $o = shift; |
16091
|
|
|
|
|
|
|
|
16092
|
0
|
0
|
|
|
|
|
if ($o->{referenceCount} < 1) { |
16093
|
0
|
|
|
|
|
|
warn 'The source '.$o->{actorOnStore}->publicKey->hash->hex.'/'.$o->{boxLabel}.'/'.$o->{hash}->hex.' has already been discarded, and cannot be kept any more.'; |
16094
|
0
|
|
|
|
|
|
return; |
16095
|
|
|
|
|
|
|
} |
16096
|
|
|
|
|
|
|
|
16097
|
0
|
|
|
|
|
|
$o->{referenceCount} += 1; |
16098
|
|
|
|
|
|
|
} |
16099
|
|
|
|
|
|
|
|
16100
|
|
|
|
|
|
|
sub discard { |
16101
|
0
|
|
|
0
|
|
|
my $o = shift; |
16102
|
|
|
|
|
|
|
|
16103
|
0
|
0
|
|
|
|
|
if ($o->{referenceCount} < 1) { |
16104
|
0
|
|
|
|
|
|
warn 'The source '.$o->{actorOnStore}->publicKey->hash->hex.'/'.$o->{boxLabel}.'/'.$o->{hash}->hex.' has already been discarded, and cannot be discarded again.'; |
16105
|
0
|
|
|
|
|
|
return; |
16106
|
|
|
|
|
|
|
} |
16107
|
|
|
|
|
|
|
|
16108
|
0
|
|
|
|
|
|
$o->{referenceCount} -= 1; |
16109
|
0
|
0
|
|
|
|
|
return if $o->{referenceCount} > 0; |
16110
|
|
|
|
|
|
|
|
16111
|
0
|
|
|
|
|
|
$o->{actorOnStore}->store->remove($o->{actorOnStore}->publicKey->hash, $o->{boxLabel}, $o->{hash}, $o->{keyPair}); |
16112
|
|
|
|
|
|
|
} |
16113
|
|
|
|
|
|
|
|
16114
|
|
|
|
|
|
|
# A store mapping objects and accounts to a group of stores. |
16115
|
|
|
|
|
|
|
package CDS::SplitStore; |
16116
|
|
|
|
|
|
|
|
16117
|
1
|
|
|
1
|
|
670
|
use parent -norequire, 'CDS::Store'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
16118
|
|
|
|
|
|
|
|
16119
|
|
|
|
|
|
|
sub new { |
16120
|
0
|
|
|
0
|
|
|
my $class = shift; |
16121
|
0
|
|
|
|
|
|
my $key = shift; |
16122
|
|
|
|
|
|
|
|
16123
|
0
|
|
|
|
|
|
return bless { |
16124
|
|
|
|
|
|
|
id => 'Split Store\n'.unpack('H*', CDS::C::aesCrypt(CDS->zeroCTR, $key, CDS->zeroCTR)), |
16125
|
|
|
|
|
|
|
key => $key, |
16126
|
|
|
|
|
|
|
accountStores => [], |
16127
|
|
|
|
|
|
|
objectStores => [], |
16128
|
|
|
|
|
|
|
}; |
16129
|
|
|
|
|
|
|
} |
16130
|
|
|
|
|
|
|
|
16131
|
0
|
|
|
0
|
|
|
sub id { shift->{id} } |
16132
|
|
|
|
|
|
|
|
16133
|
|
|
|
|
|
|
### Store configuration |
16134
|
|
|
|
|
|
|
|
16135
|
|
|
|
|
|
|
sub assignAccounts { |
16136
|
0
|
|
|
0
|
|
|
my $o = shift; |
16137
|
0
|
|
|
|
|
|
my $fromIndex = shift; |
16138
|
0
|
|
|
|
|
|
my $toIndex = shift; |
16139
|
0
|
|
|
|
|
|
my $store = shift; |
16140
|
|
|
|
|
|
|
|
16141
|
0
|
|
|
|
|
|
for my $i ($fromIndex .. $toIndex) { |
16142
|
0
|
|
|
|
|
|
$o->{accountStores}->[$i] = $store; |
16143
|
|
|
|
|
|
|
} |
16144
|
|
|
|
|
|
|
} |
16145
|
|
|
|
|
|
|
|
16146
|
|
|
|
|
|
|
sub assignObjects { |
16147
|
0
|
|
|
0
|
|
|
my $o = shift; |
16148
|
0
|
|
|
|
|
|
my $fromIndex = shift; |
16149
|
0
|
|
|
|
|
|
my $toIndex = shift; |
16150
|
0
|
|
|
|
|
|
my $store = shift; |
16151
|
|
|
|
|
|
|
|
16152
|
0
|
|
|
|
|
|
for my $i ($fromIndex .. $toIndex) { |
16153
|
0
|
|
|
|
|
|
$o->{objectStores}->[$i] = $store; |
16154
|
|
|
|
|
|
|
} |
16155
|
|
|
|
|
|
|
} |
16156
|
|
|
|
|
|
|
|
16157
|
|
|
|
|
|
|
sub objectStore { |
16158
|
0
|
|
|
0
|
|
|
my $o = shift; |
16159
|
0
|
|
|
|
|
|
my $index = shift; |
16160
|
0
|
|
|
|
|
|
$o->{objectStores}->[$index] } |
16161
|
|
|
|
|
|
|
sub accountStore { |
16162
|
0
|
|
|
0
|
|
|
my $o = shift; |
16163
|
0
|
|
|
|
|
|
my $index = shift; |
16164
|
0
|
|
|
|
|
|
$o->{accountStores}->[$index] } |
16165
|
|
|
|
|
|
|
|
16166
|
|
|
|
|
|
|
### Hash encryption |
16167
|
|
|
|
|
|
|
|
16168
|
|
|
|
|
|
|
our $zeroCounter = "\0" x 16; |
16169
|
|
|
|
|
|
|
|
16170
|
|
|
|
|
|
|
sub storeIndex { |
16171
|
0
|
|
|
0
|
|
|
my $o = shift; |
16172
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16173
|
|
|
|
|
|
|
|
16174
|
|
|
|
|
|
|
# To avoid attacks on a single store, the hash is encrypted with a key known to the operator only |
16175
|
0
|
|
|
|
|
|
my $encryptedBytes = CDS::C::aesCrypt(substr($hash->bytes, 0, 16), $o->{key}, $zeroCounter); |
16176
|
|
|
|
|
|
|
|
16177
|
|
|
|
|
|
|
# Use the first byte as store index |
16178
|
0
|
|
|
|
|
|
return ord(substr($encryptedBytes, 0, 1)); |
16179
|
|
|
|
|
|
|
} |
16180
|
|
|
|
|
|
|
|
16181
|
|
|
|
|
|
|
### Store interface |
16182
|
|
|
|
|
|
|
|
16183
|
|
|
|
|
|
|
sub get { |
16184
|
0
|
|
|
0
|
|
|
my $o = shift; |
16185
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16186
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
16187
|
|
|
|
|
|
|
|
16188
|
0
|
|
0
|
|
|
|
my $store = $o->objectStore($o->storeIndex($hash)) // return undef, 'No store assigned.'; |
16189
|
0
|
|
|
|
|
|
return $store->get($hash, $keyPair); |
16190
|
|
|
|
|
|
|
} |
16191
|
|
|
|
|
|
|
|
16192
|
|
|
|
|
|
|
sub put { |
16193
|
0
|
|
|
0
|
|
|
my $o = shift; |
16194
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16195
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
16196
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
16197
|
|
|
|
|
|
|
|
16198
|
0
|
|
0
|
|
|
|
my $store = $o->objectStore($o->storeIndex($hash)) // return undef, 'No store assigned.'; |
16199
|
0
|
|
|
|
|
|
return $store->put($hash, $object, $keyPair); |
16200
|
|
|
|
|
|
|
} |
16201
|
|
|
|
|
|
|
|
16202
|
|
|
|
|
|
|
sub book { |
16203
|
0
|
|
|
0
|
|
|
my $o = shift; |
16204
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16205
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
16206
|
|
|
|
|
|
|
|
16207
|
0
|
|
0
|
|
|
|
my $store = $o->objectStore($o->storeIndex($hash)) // return undef, 'No store assigned.'; |
16208
|
0
|
|
|
|
|
|
return $store->book($hash, $keyPair); |
16209
|
|
|
|
|
|
|
} |
16210
|
|
|
|
|
|
|
|
16211
|
|
|
|
|
|
|
sub list { |
16212
|
0
|
|
|
0
|
|
|
my $o = shift; |
16213
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16214
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
16215
|
0
|
|
|
|
|
|
my $timeout = shift; |
16216
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
16217
|
|
|
|
|
|
|
|
16218
|
0
|
|
0
|
|
|
|
my $store = $o->accountStore($o->storeIndex($accountHash)) // return undef, 'No store assigned.'; |
16219
|
0
|
|
|
|
|
|
return $store->list($accountHash, $boxLabel, $timeout, $keyPair); |
16220
|
|
|
|
|
|
|
} |
16221
|
|
|
|
|
|
|
|
16222
|
|
|
|
|
|
|
sub add { |
16223
|
0
|
|
|
0
|
|
|
my $o = shift; |
16224
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16225
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
16226
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16227
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
16228
|
|
|
|
|
|
|
|
16229
|
0
|
|
0
|
|
|
|
my $store = $o->accountStore($o->storeIndex($accountHash)) // return 'No store assigned.'; |
16230
|
0
|
|
|
|
|
|
return $store->add($accountHash, $boxLabel, $hash, $keyPair); |
16231
|
|
|
|
|
|
|
} |
16232
|
|
|
|
|
|
|
|
16233
|
|
|
|
|
|
|
sub remove { |
16234
|
0
|
|
|
0
|
|
|
my $o = shift; |
16235
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16236
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
16237
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16238
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
16239
|
|
|
|
|
|
|
|
16240
|
0
|
|
0
|
|
|
|
my $store = $o->accountStore($o->storeIndex($accountHash)) // return 'No store assigned.'; |
16241
|
0
|
|
|
|
|
|
return $store->remove($accountHash, $boxLabel, $hash, $keyPair); |
16242
|
|
|
|
|
|
|
} |
16243
|
|
|
|
|
|
|
|
16244
|
|
|
|
|
|
|
sub modify { |
16245
|
0
|
|
|
0
|
|
|
my $o = shift; |
16246
|
0
|
|
|
|
|
|
my $modifications = shift; |
16247
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
16248
|
|
|
|
|
|
|
|
16249
|
|
|
|
|
|
|
# Put objects |
16250
|
0
|
|
|
|
|
|
my %objectsByStoreId; |
16251
|
0
|
|
|
|
|
|
for my $entry (values %{$modifications->objects}) { |
|
0
|
|
|
|
|
|
|
16252
|
0
|
|
|
|
|
|
my $store = $o->objectStore($o->storeIndex($entry->{hash})); |
16253
|
0
|
|
|
|
|
|
my $target = $objectsByStoreId{$store->id}; |
16254
|
0
|
|
|
|
|
|
$objectsByStoreId{$store->id} = $target = {store => $store, modifications => CDS::StoreModifications->new}; |
16255
|
0
|
|
|
|
|
|
$target->modifications->put($entry->{hash}, $entry->{object}); |
16256
|
|
|
|
|
|
|
} |
16257
|
|
|
|
|
|
|
|
16258
|
0
|
|
|
|
|
|
for my $item (values %objectsByStoreId) { |
16259
|
0
|
|
|
|
|
|
my $error = $item->{store}->modify($item->{modifications}, $keyPair); |
16260
|
0
|
0
|
|
|
|
|
return $error if $error; |
16261
|
|
|
|
|
|
|
} |
16262
|
|
|
|
|
|
|
|
16263
|
|
|
|
|
|
|
# Add box entries |
16264
|
0
|
|
|
|
|
|
my %additionsByStoreId; |
16265
|
0
|
|
|
|
|
|
for my $operation (@{$modifications->additions}) { |
|
0
|
|
|
|
|
|
|
16266
|
0
|
|
|
|
|
|
my $store = $o->accountStore($o->storeIndex($operation->{accountHash})); |
16267
|
0
|
|
|
|
|
|
my $target = $additionsByStoreId{$store->id}; |
16268
|
0
|
|
|
|
|
|
$additionsByStoreId{$store->id} = $target = {store => $store, modifications => CDS::StoreModifications->new}; |
16269
|
0
|
|
|
|
|
|
$target->modifications->add($operation->{accountHash}, $operation->{boxLabel}, $operation->{hash}); |
16270
|
|
|
|
|
|
|
} |
16271
|
|
|
|
|
|
|
|
16272
|
0
|
|
|
|
|
|
for my $item (values %additionsByStoreId) { |
16273
|
0
|
|
|
|
|
|
my $error = $item->{store}->modify($item->{modifications}, $keyPair); |
16274
|
0
|
0
|
|
|
|
|
return $error if $error; |
16275
|
|
|
|
|
|
|
} |
16276
|
|
|
|
|
|
|
|
16277
|
|
|
|
|
|
|
# Remove box entries (but ignore errors) |
16278
|
0
|
|
|
|
|
|
my %removalsByStoreId; |
16279
|
0
|
|
|
|
|
|
for my $operation (@$modifications->removals) { |
16280
|
0
|
|
|
|
|
|
my $store = $o->accountStore($o->storeIndex($operation->{accountHash})); |
16281
|
0
|
|
|
|
|
|
my $target = $removalsByStoreId{$store->id}; |
16282
|
0
|
|
|
|
|
|
$removalsByStoreId{$store->id} = $target = {store => $store, modifications => CDS::StoreModifications->new}; |
16283
|
0
|
|
|
|
|
|
$target->modifications->add($operation->{accountHash}, $operation->{boxLabel}, $operation->{hash}); |
16284
|
|
|
|
|
|
|
} |
16285
|
|
|
|
|
|
|
|
16286
|
0
|
|
|
|
|
|
for my $item (values %removalsByStoreId) { |
16287
|
0
|
|
|
|
|
|
$item->{store}->modify($item->{modifications}, $keyPair); |
16288
|
|
|
|
|
|
|
} |
16289
|
|
|
|
|
|
|
|
16290
|
0
|
|
|
|
|
|
return; |
16291
|
|
|
|
|
|
|
} |
16292
|
|
|
|
|
|
|
|
16293
|
|
|
|
|
|
|
# General |
16294
|
|
|
|
|
|
|
# sub id($o) # () => String |
16295
|
|
|
|
|
|
|
package CDS::Store; |
16296
|
|
|
|
|
|
|
|
16297
|
|
|
|
|
|
|
# Object store functions |
16298
|
|
|
|
|
|
|
# sub get($o, $hash, $keyPair) # Hash, KeyPair? => Object?, String? |
16299
|
|
|
|
|
|
|
# sub put($o, $hash, $object, $keyPair) # Hash, Object, KeyPair? => String? |
16300
|
|
|
|
|
|
|
# sub book($o, $hash, $keyPair) # Hash, KeyPair? => 1?, String? |
16301
|
|
|
|
|
|
|
|
16302
|
|
|
|
|
|
|
# Account store functions |
16303
|
|
|
|
|
|
|
# sub list($o, $accountHash, $boxLabel, $timeout, $keyPair) # Hash, String, Duration, KeyPair? => @$Hash, String? |
16304
|
|
|
|
|
|
|
# sub add($o, $accountHash, $boxLabel, $hash, $keyPair) # Hash, String, Hash, KeyPair? => String? |
16305
|
|
|
|
|
|
|
# sub remove($o, $accountHash, $boxLabel, $hash, $keyPair) # Hash, String, Hash, KeyPair? => String? |
16306
|
|
|
|
|
|
|
# sub modify($o, $storeModifications, $keyPair) # StoreModifications, KeyPair? => String? |
16307
|
|
|
|
|
|
|
|
16308
|
|
|
|
|
|
|
package CDS::StoreModifications; |
16309
|
|
|
|
|
|
|
|
16310
|
|
|
|
|
|
|
sub new { |
16311
|
0
|
|
|
0
|
|
|
my $class = shift; |
16312
|
|
|
|
|
|
|
|
16313
|
0
|
|
|
|
|
|
return bless { |
16314
|
|
|
|
|
|
|
objects => {}, |
16315
|
|
|
|
|
|
|
additions => [], |
16316
|
|
|
|
|
|
|
removals => [], |
16317
|
|
|
|
|
|
|
}; |
16318
|
|
|
|
|
|
|
} |
16319
|
|
|
|
|
|
|
|
16320
|
0
|
|
|
0
|
|
|
sub objects { shift->{objects} } |
16321
|
0
|
|
|
0
|
|
|
sub additions { shift->{additions} } |
16322
|
0
|
|
|
0
|
|
|
sub removals { shift->{removals} } |
16323
|
|
|
|
|
|
|
|
16324
|
|
|
|
|
|
|
sub isEmpty { |
16325
|
0
|
|
|
0
|
|
|
my $o = shift; |
16326
|
|
|
|
|
|
|
|
16327
|
0
|
0
|
|
|
|
|
return if scalar keys %{$o->{objects}}; |
|
0
|
|
|
|
|
|
|
16328
|
0
|
0
|
|
|
|
|
return if scalar @{$o->{additions}}; |
|
0
|
|
|
|
|
|
|
16329
|
0
|
0
|
|
|
|
|
return if scalar @{$o->{removals}}; |
|
0
|
|
|
|
|
|
|
16330
|
0
|
|
|
|
|
|
return 1; |
16331
|
|
|
|
|
|
|
} |
16332
|
|
|
|
|
|
|
|
16333
|
|
|
|
|
|
|
sub put { |
16334
|
0
|
|
|
0
|
|
|
my $o = shift; |
16335
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16336
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
16337
|
|
|
|
|
|
|
|
16338
|
0
|
|
|
|
|
|
$o->{objects}->{$hash->bytes} = {hash => $hash, object => $object}; |
16339
|
|
|
|
|
|
|
} |
16340
|
|
|
|
|
|
|
|
16341
|
|
|
|
|
|
|
sub add { |
16342
|
0
|
|
|
0
|
|
|
my $o = shift; |
16343
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16344
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
16345
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16346
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
16347
|
|
|
|
|
|
|
|
16348
|
0
|
0
|
|
|
|
|
$o->put($hash, $object) if $object; |
16349
|
0
|
|
|
|
|
|
push @{$o->{additions}}, {accountHash => $accountHash, boxLabel => $boxLabel, hash => $hash}; |
|
0
|
|
|
|
|
|
|
16350
|
|
|
|
|
|
|
} |
16351
|
|
|
|
|
|
|
|
16352
|
|
|
|
|
|
|
sub remove { |
16353
|
0
|
|
|
0
|
|
|
my $o = shift; |
16354
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16355
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
16356
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16357
|
|
|
|
|
|
|
|
16358
|
0
|
|
|
|
|
|
push @{$o->{removals}}, {accountHash => $accountHash, boxLabel => $boxLabel, hash => $hash}; |
|
0
|
|
|
|
|
|
|
16359
|
|
|
|
|
|
|
} |
16360
|
|
|
|
|
|
|
|
16361
|
|
|
|
|
|
|
sub executeIndividually { |
16362
|
0
|
|
|
0
|
|
|
my $o = shift; |
16363
|
0
|
|
|
|
|
|
my $store = shift; |
16364
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
16365
|
|
|
|
|
|
|
|
16366
|
|
|
|
|
|
|
# Process objects |
16367
|
0
|
|
|
|
|
|
for my $entry (values %{$o->{objects}}) { |
|
0
|
|
|
|
|
|
|
16368
|
0
|
|
|
|
|
|
my $error = $store->put($entry->{hash}, $entry->{object}, $keyPair); |
16369
|
0
|
0
|
|
|
|
|
return $error if $error; |
16370
|
|
|
|
|
|
|
} |
16371
|
|
|
|
|
|
|
|
16372
|
|
|
|
|
|
|
# Process additions |
16373
|
0
|
|
|
|
|
|
for my $entry (@{$o->{additions}}) { |
|
0
|
|
|
|
|
|
|
16374
|
0
|
|
|
|
|
|
my $error = $store->add($entry->{accountHash}, $entry->{boxLabel}, $entry->{hash}, $keyPair); |
16375
|
0
|
0
|
|
|
|
|
return $error if $error; |
16376
|
|
|
|
|
|
|
} |
16377
|
|
|
|
|
|
|
|
16378
|
|
|
|
|
|
|
# Process removals (and ignore errors) |
16379
|
0
|
|
|
|
|
|
for my $entry (@{$o->{removals}}) { |
|
0
|
|
|
|
|
|
|
16380
|
0
|
|
|
|
|
|
$store->remove($entry->{accountHash}, $entry->{boxLabel}, $entry->{hash}, $keyPair); |
16381
|
|
|
|
|
|
|
} |
16382
|
|
|
|
|
|
|
|
16383
|
0
|
|
|
|
|
|
return; |
16384
|
|
|
|
|
|
|
} |
16385
|
|
|
|
|
|
|
|
16386
|
|
|
|
|
|
|
# Returns a text representation of box additions and removals. |
16387
|
|
|
|
|
|
|
sub toRecord { |
16388
|
0
|
|
|
0
|
|
|
my $o = shift; |
16389
|
|
|
|
|
|
|
|
16390
|
0
|
|
|
|
|
|
my $record = CDS::Record->new; |
16391
|
|
|
|
|
|
|
|
16392
|
|
|
|
|
|
|
# Objects |
16393
|
0
|
|
|
|
|
|
my $objectsRecord = $record->add('put'); |
16394
|
0
|
|
|
|
|
|
for my $entry (values %{$o->{objects}}) { |
|
0
|
|
|
|
|
|
|
16395
|
0
|
|
|
|
|
|
$objectsRecord->add($entry->{hash}->bytes)->add($entry->{object}->bytes); |
16396
|
|
|
|
|
|
|
} |
16397
|
|
|
|
|
|
|
|
16398
|
|
|
|
|
|
|
# Box additions and removals |
16399
|
0
|
|
|
|
|
|
&addEntriesToRecord($o->{additions}, $record->add('add')); |
16400
|
0
|
|
|
|
|
|
&addEntriesToRecord($o->{removals}, $record->add('remove')); |
16401
|
|
|
|
|
|
|
|
16402
|
0
|
|
|
|
|
|
return $record; |
16403
|
|
|
|
|
|
|
} |
16404
|
|
|
|
|
|
|
|
16405
|
|
|
|
|
|
|
sub addEntriesToRecord { |
16406
|
0
|
|
|
0
|
|
|
my $unsortedEntries = shift; |
16407
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
16408
|
|
|
|
|
|
|
# private |
16409
|
0
|
0
|
|
|
|
|
my @additions = sort { ($a->{accountHash}->bytes cmp $b->{accountHash}->bytes) || ($a->{boxLabel} cmp $b->{boxLabel}) } @$unsortedEntries; |
|
0
|
|
|
|
|
|
|
16410
|
0
|
|
|
|
|
|
my $entry = shift @additions; |
16411
|
0
|
|
|
|
|
|
while (defined $entry) { |
16412
|
0
|
|
|
|
|
|
my $accountHash = $entry->{accountHash}; |
16413
|
0
|
|
|
|
|
|
my $accountRecord = $record->add($accountHash->bytes); |
16414
|
|
|
|
|
|
|
|
16415
|
0
|
|
0
|
|
|
|
while (defined $entry && $entry->{accountHash}->bytes eq $accountHash->bytes) { |
16416
|
0
|
|
|
|
|
|
my $boxLabel = $entry->{boxLabel}; |
16417
|
0
|
|
|
|
|
|
my $boxRecord = $accountRecord->add($boxLabel); |
16418
|
|
|
|
|
|
|
|
16419
|
0
|
|
0
|
|
|
|
while (defined $entry && $entry->{boxLabel} eq $boxLabel) { |
16420
|
0
|
|
|
|
|
|
$boxRecord->add($entry->{hash}->bytes); |
16421
|
0
|
|
|
|
|
|
$entry = shift @additions; |
16422
|
|
|
|
|
|
|
} |
16423
|
|
|
|
|
|
|
} |
16424
|
|
|
|
|
|
|
} |
16425
|
|
|
|
|
|
|
} |
16426
|
|
|
|
|
|
|
|
16427
|
|
|
|
|
|
|
sub fromBytes { |
16428
|
0
|
|
|
0
|
|
|
my $class = shift; |
16429
|
0
|
|
|
|
|
|
my $bytes = shift; |
16430
|
|
|
|
|
|
|
|
16431
|
0
|
|
0
|
|
|
|
my $object = CDS::Object->fromBytes($bytes) // return; |
16432
|
0
|
|
0
|
|
|
|
my $record = CDS::Record->fromObject($object) // return; |
16433
|
0
|
|
|
|
|
|
return $class->fromRecord($record); |
16434
|
|
|
|
|
|
|
} |
16435
|
|
|
|
|
|
|
|
16436
|
|
|
|
|
|
|
sub fromRecord { |
16437
|
0
|
|
|
0
|
|
|
my $class = shift; |
16438
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
16439
|
|
|
|
|
|
|
|
16440
|
0
|
|
|
|
|
|
my $modifications = $class->new; |
16441
|
|
|
|
|
|
|
|
16442
|
|
|
|
|
|
|
# Read objects (and "envelopes" entries used before 2022-01) |
16443
|
0
|
|
|
|
|
|
for my $objectRecord ($record->child('put')->children, $record->child('envelopes')->children) { |
16444
|
0
|
|
0
|
|
|
|
my $hash = CDS::Hash->fromBytes($objectRecord->bytes) // return; |
16445
|
0
|
|
0
|
|
|
|
my $object = CDS::Object->fromBytes($objectRecord->firstChild->bytes) // return; |
16446
|
|
|
|
|
|
|
#return if $o->{checkEnvelopeHash} && ! $object->calculateHash->equals($hash); |
16447
|
0
|
|
|
|
|
|
$modifications->put($hash, $object); |
16448
|
|
|
|
|
|
|
} |
16449
|
|
|
|
|
|
|
|
16450
|
|
|
|
|
|
|
# Read additions and removals |
16451
|
0
|
|
0
|
|
|
|
&readEntriesFromRecord($modifications->{additions}, $record->child('add')) // return; |
16452
|
0
|
|
0
|
|
|
|
&readEntriesFromRecord($modifications->{removals}, $record->child('remove')) // return; |
16453
|
|
|
|
|
|
|
|
16454
|
0
|
|
|
|
|
|
return $modifications; |
16455
|
|
|
|
|
|
|
} |
16456
|
|
|
|
|
|
|
|
16457
|
|
|
|
|
|
|
sub readEntriesFromRecord { |
16458
|
0
|
|
|
0
|
|
|
my $entries = shift; |
16459
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
16460
|
|
|
|
|
|
|
# private |
16461
|
0
|
|
|
|
|
|
for my $accountHashRecord ($record->children) { |
16462
|
0
|
|
0
|
|
|
|
my $accountHash = CDS::Hash->fromBytes($accountHashRecord->bytes) // return; |
16463
|
0
|
|
|
|
|
|
for my $boxLabelRecord ($accountHashRecord->children) { |
16464
|
0
|
|
|
|
|
|
my $boxLabel = $boxLabelRecord->bytes; |
16465
|
0
|
0
|
|
|
|
|
return if ! CDS->isValidBoxLabel($boxLabel); |
16466
|
|
|
|
|
|
|
|
16467
|
0
|
|
|
|
|
|
for my $hashRecord ($boxLabelRecord->children) { |
16468
|
0
|
|
0
|
|
|
|
my $hash = CDS::Hash->fromBytes($hashRecord->bytes) // return; |
16469
|
0
|
|
|
|
|
|
push @$entries, {accountHash => $accountHash, boxLabel => $boxLabel, hash => $hash}; |
16470
|
|
|
|
|
|
|
} |
16471
|
|
|
|
|
|
|
} |
16472
|
|
|
|
|
|
|
} |
16473
|
|
|
|
|
|
|
|
16474
|
0
|
|
|
|
|
|
return 1; |
16475
|
|
|
|
|
|
|
} |
16476
|
|
|
|
|
|
|
|
16477
|
|
|
|
|
|
|
package CDS::StreamCache; |
16478
|
|
|
|
|
|
|
|
16479
|
|
|
|
|
|
|
sub new { |
16480
|
0
|
|
|
0
|
|
|
my $class = shift; |
16481
|
0
|
|
|
|
|
|
my $pool = shift; |
16482
|
0
|
0
|
0
|
|
|
|
my $actorOnStore = shift; die 'wrong type '.ref($actorOnStore).' for $actorOnStore' if defined $actorOnStore && ref $actorOnStore ne 'CDS::ActorOnStore'; |
|
0
|
|
|
|
|
|
|
16483
|
0
|
|
|
|
|
|
my $timeout = shift; |
16484
|
|
|
|
|
|
|
|
16485
|
0
|
|
|
|
|
|
return bless { |
16486
|
|
|
|
|
|
|
pool => $pool, |
16487
|
|
|
|
|
|
|
actorOnStore => $actorOnStore, |
16488
|
|
|
|
|
|
|
timeout => $timeout, |
16489
|
|
|
|
|
|
|
cache => {}, |
16490
|
|
|
|
|
|
|
}; |
16491
|
|
|
|
|
|
|
} |
16492
|
|
|
|
|
|
|
|
16493
|
0
|
|
|
0
|
|
|
sub messageBoxReader { shift->{messageBoxReader} } |
16494
|
|
|
|
|
|
|
|
16495
|
|
|
|
|
|
|
sub removeObsolete { |
16496
|
0
|
|
|
0
|
|
|
my $o = shift; |
16497
|
|
|
|
|
|
|
|
16498
|
0
|
|
|
|
|
|
my $limit = CDS->now - $o->{timeout}; |
16499
|
0
|
|
|
|
|
|
for my $key (%{$o->{knownStreamHeads}}) { |
|
0
|
|
|
|
|
|
|
16500
|
0
|
|
0
|
|
|
|
my $streamHead = $o->{knownStreamHeads}->{$key} // next; |
16501
|
0
|
0
|
|
|
|
|
next if $streamHead->lastUsed < $limit; |
16502
|
0
|
|
|
|
|
|
delete $o->{knownStreamHeads}->{$key}; |
16503
|
|
|
|
|
|
|
} |
16504
|
|
|
|
|
|
|
} |
16505
|
|
|
|
|
|
|
|
16506
|
|
|
|
|
|
|
sub readStreamHead { |
16507
|
0
|
|
|
0
|
|
|
my $o = shift; |
16508
|
0
|
|
|
|
|
|
my $head = shift; |
16509
|
|
|
|
|
|
|
|
16510
|
0
|
|
|
|
|
|
my $streamHead = $o->{knownStreamHeads}->{$head->hex}; |
16511
|
0
|
0
|
|
|
|
|
if ($streamHead) { |
16512
|
0
|
|
|
|
|
|
$streamHead->stillInUse; |
16513
|
0
|
|
|
|
|
|
return $streamHead; |
16514
|
|
|
|
|
|
|
} |
16515
|
|
|
|
|
|
|
|
16516
|
|
|
|
|
|
|
# Retrieve the head envelope |
16517
|
0
|
|
|
|
|
|
my ($object, $getError) = $o->{actorOnStore}->store->get($head, $o->{pool}->{keyPair}); |
16518
|
0
|
0
|
|
|
|
|
return if defined $getError; |
16519
|
|
|
|
|
|
|
|
16520
|
|
|
|
|
|
|
# Parse the head envelope |
16521
|
0
|
|
|
|
|
|
my $envelope = CDS::Record->fromObject($object); |
16522
|
0
|
0
|
|
|
|
|
return $o->invalid($head, 'Not a record.') if ! $envelope; |
16523
|
|
|
|
|
|
|
|
16524
|
|
|
|
|
|
|
# Read the embedded content object |
16525
|
0
|
|
|
|
|
|
my $encryptedBytes = $envelope->child('content')->bytesValue; |
16526
|
0
|
0
|
|
|
|
|
return $o->invalid($head, 'Missing content object.') if ! length $encryptedBytes; |
16527
|
|
|
|
|
|
|
|
16528
|
|
|
|
|
|
|
# Decrypt the key |
16529
|
0
|
|
|
|
|
|
my $aesKey = $o->{pool}->{keyPair}->decryptKeyOnEnvelope($envelope); |
16530
|
0
|
0
|
|
|
|
|
return $o->invalid($head, 'Not encrypted for us.') if ! $aesKey; |
16531
|
|
|
|
|
|
|
|
16532
|
|
|
|
|
|
|
# Decrypt the content |
16533
|
0
|
|
|
|
|
|
my $contentObject = CDS::Object->fromBytes(CDS::C::aesCrypt($encryptedBytes, $aesKey, CDS->zeroCTR)); |
16534
|
0
|
0
|
|
|
|
|
return $o->invalid($head, 'Invalid content object.') if ! $contentObject; |
16535
|
|
|
|
|
|
|
|
16536
|
0
|
|
|
|
|
|
my $content = CDS::Record->fromObject($contentObject); |
16537
|
0
|
0
|
|
|
|
|
return $o->invalid($head, 'Content object is not a record.') if ! $content; |
16538
|
|
|
|
|
|
|
|
16539
|
|
|
|
|
|
|
# Verify the sender hash |
16540
|
0
|
|
|
|
|
|
my $senderHash = $content->child('sender')->hashValue; |
16541
|
0
|
0
|
|
|
|
|
return $o->invalid($head, 'Missing sender hash.') if ! $senderHash; |
16542
|
|
|
|
|
|
|
|
16543
|
|
|
|
|
|
|
# Verify the sender store |
16544
|
0
|
|
|
|
|
|
my $storeRecord = $content->child('store'); |
16545
|
0
|
0
|
|
|
|
|
return $o->invalid($head, 'Missing sender store.') if ! scalar $storeRecord->children; |
16546
|
|
|
|
|
|
|
|
16547
|
0
|
|
|
|
|
|
my $senderStoreUrl = $storeRecord->textValue; |
16548
|
0
|
|
|
|
|
|
my $senderStore = $o->{pool}->{delegate}->onMessageBoxVerifyStore($senderStoreUrl, $head, $envelope, $senderHash); |
16549
|
0
|
0
|
|
|
|
|
return $o->invalid($head, 'Invalid sender store.') if ! $senderStore; |
16550
|
|
|
|
|
|
|
|
16551
|
|
|
|
|
|
|
# Retrieve the sender's public key |
16552
|
0
|
|
|
|
|
|
my ($senderPublicKey, $invalidReason, $publicKeyStoreError) = $o->getPublicKey($senderHash, $senderStore); |
16553
|
0
|
0
|
|
|
|
|
return if defined $publicKeyStoreError; |
16554
|
0
|
0
|
|
|
|
|
return $o->invalid($head, 'Failed to retrieve the sender\'s public key: '.$invalidReason) if defined $invalidReason; |
16555
|
|
|
|
|
|
|
|
16556
|
|
|
|
|
|
|
# Verify the signature |
16557
|
0
|
|
|
|
|
|
my $signedHash = CDS::Hash->calculateFor($encryptedBytes); |
16558
|
0
|
0
|
|
|
|
|
return $o->invalid($head, 'Invalid signature.') if ! CDS->verifyEnvelopeSignature($envelope, $senderPublicKey, $signedHash); |
16559
|
|
|
|
|
|
|
|
16560
|
|
|
|
|
|
|
# The envelope is valid |
16561
|
0
|
|
|
|
|
|
my $sender = CDS::ActorOnStore->new($senderPublicKey, $senderStore); |
16562
|
0
|
|
|
|
|
|
my $newStreamHead = CDS::StreamHead->new($head, $envelope, $senderStoreUrl, $sender, $aesKey, $content); |
16563
|
0
|
|
|
|
|
|
$o->{knownStreamHeads}->{$head->hex} = $newStreamHead; |
16564
|
0
|
|
|
|
|
|
return $newStreamHead; |
16565
|
|
|
|
|
|
|
} |
16566
|
|
|
|
|
|
|
|
16567
|
|
|
|
|
|
|
sub invalid { |
16568
|
0
|
|
|
0
|
|
|
my $o = shift; |
16569
|
0
|
|
|
|
|
|
my $head = shift; |
16570
|
0
|
|
|
|
|
|
my $reason = shift; |
16571
|
|
|
|
|
|
|
# private |
16572
|
0
|
|
|
|
|
|
my $newStreamHead = CDS::StreamHead->new($head, undef, undef, undef, undef, undef, $reason); |
16573
|
0
|
|
|
|
|
|
$o->{knownStreamHeads}->{$head->hex} = $newStreamHead; |
16574
|
0
|
|
|
|
|
|
return $newStreamHead; |
16575
|
|
|
|
|
|
|
} |
16576
|
|
|
|
|
|
|
|
16577
|
|
|
|
|
|
|
package CDS::StreamHead; |
16578
|
|
|
|
|
|
|
|
16579
|
|
|
|
|
|
|
sub new { |
16580
|
0
|
|
|
0
|
|
|
my $class = shift; |
16581
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
16582
|
0
|
0
|
0
|
|
|
|
my $envelope = shift; die 'wrong type '.ref($envelope).' for $envelope' if defined $envelope && ref $envelope ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
16583
|
0
|
|
|
|
|
|
my $senderStoreUrl = shift; |
16584
|
0
|
|
|
|
|
|
my $sender = shift; |
16585
|
0
|
|
|
|
|
|
my $content = shift; |
16586
|
0
|
|
|
|
|
|
my $error = shift; |
16587
|
|
|
|
|
|
|
|
16588
|
0
|
|
|
|
|
|
return bless { |
16589
|
|
|
|
|
|
|
hash => $hash, |
16590
|
|
|
|
|
|
|
envelope => $envelope, |
16591
|
|
|
|
|
|
|
senderStoreUrl => $senderStoreUrl, |
16592
|
|
|
|
|
|
|
sender => $sender, |
16593
|
|
|
|
|
|
|
content => $content, |
16594
|
|
|
|
|
|
|
error => $error, |
16595
|
|
|
|
|
|
|
lastUsed => CDS->now, |
16596
|
|
|
|
|
|
|
}; |
16597
|
|
|
|
|
|
|
} |
16598
|
|
|
|
|
|
|
|
16599
|
0
|
|
|
0
|
|
|
sub hash { shift->{hash} } |
16600
|
0
|
|
|
0
|
|
|
sub envelope { shift->{envelope} } |
16601
|
0
|
|
|
0
|
|
|
sub senderStoreUrl { shift->{senderStoreUrl} } |
16602
|
0
|
|
|
0
|
|
|
sub sender { shift->{sender} } |
16603
|
0
|
|
|
0
|
|
|
sub content { shift->{content} } |
16604
|
0
|
|
|
0
|
|
|
sub error { shift->{error} } |
16605
|
|
|
|
|
|
|
sub isValid { |
16606
|
0
|
|
|
0
|
|
|
my $o = shift; |
16607
|
0
|
|
|
|
|
|
! defined $o->{error} } |
16608
|
0
|
|
|
0
|
|
|
sub lastUsed { shift->{lastUsed} } |
16609
|
|
|
|
|
|
|
|
16610
|
|
|
|
|
|
|
sub stillInUse { |
16611
|
0
|
|
|
0
|
|
|
my $o = shift; |
16612
|
|
|
|
|
|
|
|
16613
|
0
|
|
|
|
|
|
$o->{lastUsed} = CDS->now; |
16614
|
|
|
|
|
|
|
} |
16615
|
|
|
|
|
|
|
|
16616
|
|
|
|
|
|
|
package CDS::SubDocument; |
16617
|
|
|
|
|
|
|
|
16618
|
1
|
|
|
1
|
|
3451
|
use parent -norequire, 'CDS::Document'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
16619
|
|
|
|
|
|
|
|
16620
|
|
|
|
|
|
|
sub new { |
16621
|
0
|
|
|
0
|
|
|
my $class = shift; |
16622
|
0
|
0
|
0
|
|
|
|
my $parentSelector = shift; die 'wrong type '.ref($parentSelector).' for $parentSelector' if defined $parentSelector && ref $parentSelector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
|
|
16623
|
|
|
|
|
|
|
|
16624
|
0
|
|
|
|
|
|
my $o = $class->SUPER::new($parentSelector->document->keyPair, $parentSelector->document->unsaved); |
16625
|
0
|
|
|
|
|
|
$o->{parentSelector} = $parentSelector; |
16626
|
0
|
|
|
|
|
|
return $o; |
16627
|
|
|
|
|
|
|
} |
16628
|
|
|
|
|
|
|
|
16629
|
0
|
|
|
0
|
|
|
sub parentSelector { shift->{parentSelector} } |
16630
|
|
|
|
|
|
|
|
16631
|
|
|
|
|
|
|
sub partSelector { |
16632
|
0
|
|
|
0
|
|
|
my $o = shift; |
16633
|
0
|
0
|
0
|
|
|
|
my $hashAndKey = shift; die 'wrong type '.ref($hashAndKey).' for $hashAndKey' if defined $hashAndKey && ref $hashAndKey ne 'CDS::HashAndKey'; |
|
0
|
|
|
|
|
|
|
16634
|
|
|
|
|
|
|
|
16635
|
0
|
|
|
|
|
|
$o->{parentSelector}->child(substr($hashAndKey->hash->bytes, 0, 16)); |
16636
|
|
|
|
|
|
|
} |
16637
|
|
|
|
|
|
|
|
16638
|
|
|
|
|
|
|
sub read { |
16639
|
0
|
|
|
0
|
|
|
my $o = shift; |
16640
|
|
|
|
|
|
|
|
16641
|
0
|
|
|
|
|
|
$o->merge(map { $_->hashAndKeyValue } $o->{parentSelector}->children); |
|
0
|
|
|
|
|
|
|
16642
|
0
|
|
|
|
|
|
return $o->SUPER::read; |
16643
|
|
|
|
|
|
|
} |
16644
|
|
|
|
|
|
|
|
16645
|
|
|
|
|
|
|
sub savingDone { |
16646
|
0
|
|
|
0
|
|
|
my $o = shift; |
16647
|
0
|
|
|
|
|
|
my $revision = shift; |
16648
|
0
|
|
|
|
|
|
my $newPart = shift; |
16649
|
0
|
|
|
|
|
|
my $obsoleteParts = shift; |
16650
|
|
|
|
|
|
|
|
16651
|
0
|
|
|
|
|
|
$o->{parentSelector}->document->unsaved->state->merge($o->{unsaved}->savingState); |
16652
|
|
|
|
|
|
|
|
16653
|
|
|
|
|
|
|
# Remove obsolete parts |
16654
|
0
|
|
|
|
|
|
for my $part (@$obsoleteParts) { |
16655
|
0
|
|
|
|
|
|
$o->partSelector($part->{hashAndKey})->merge($revision, CDS::Record->new); |
16656
|
|
|
|
|
|
|
} |
16657
|
|
|
|
|
|
|
|
16658
|
|
|
|
|
|
|
# Add the new part |
16659
|
0
|
0
|
|
|
|
|
if ($newPart) { |
16660
|
0
|
|
|
|
|
|
my $record = CDS::Record->new; |
16661
|
0
|
|
|
|
|
|
$record->addHashAndKey($newPart->{hashAndKey}); |
16662
|
0
|
|
|
|
|
|
$o->partSelector($newPart->{hashAndKey})->merge($revision, $record); |
16663
|
|
|
|
|
|
|
} |
16664
|
|
|
|
|
|
|
|
16665
|
0
|
|
|
|
|
|
$o->{unsaved}->savingDone; |
16666
|
|
|
|
|
|
|
} |
16667
|
|
|
|
|
|
|
|
16668
|
|
|
|
|
|
|
# Useful functions to display textual information on the terminal |
16669
|
|
|
|
|
|
|
package CDS::UI; |
16670
|
|
|
|
|
|
|
|
16671
|
|
|
|
|
|
|
sub new { |
16672
|
0
|
|
|
0
|
|
|
my $class = shift; |
16673
|
0
|
|
0
|
|
|
|
my $fileHandle = shift // *STDOUT; |
16674
|
0
|
|
|
|
|
|
my $pure = shift; |
16675
|
|
|
|
|
|
|
|
16676
|
0
|
|
|
|
|
|
binmode $fileHandle, ":utf8"; |
16677
|
0
|
|
|
|
|
|
return bless { |
16678
|
|
|
|
|
|
|
fileHandle => $fileHandle, |
16679
|
|
|
|
|
|
|
pure => $pure, |
16680
|
|
|
|
|
|
|
indentCount => 0, |
16681
|
|
|
|
|
|
|
indent => '', |
16682
|
|
|
|
|
|
|
valueIndent => 16, |
16683
|
|
|
|
|
|
|
hasSpace => 0, |
16684
|
|
|
|
|
|
|
hasError => 0, |
16685
|
|
|
|
|
|
|
hasWarning => 0, |
16686
|
|
|
|
|
|
|
}; |
16687
|
|
|
|
|
|
|
} |
16688
|
|
|
|
|
|
|
|
16689
|
0
|
|
|
0
|
|
|
sub fileHandle { shift->{fileHandle} } |
16690
|
|
|
|
|
|
|
|
16691
|
|
|
|
|
|
|
### Indent |
16692
|
|
|
|
|
|
|
|
16693
|
|
|
|
|
|
|
sub pushIndent { |
16694
|
0
|
|
|
0
|
|
|
my $o = shift; |
16695
|
|
|
|
|
|
|
|
16696
|
0
|
|
|
|
|
|
$o->{indentCount} += 1; |
16697
|
0
|
|
|
|
|
|
$o->{indent} = ' ' x $o->{indentCount}; |
16698
|
0
|
|
|
|
|
|
return; |
16699
|
|
|
|
|
|
|
} |
16700
|
|
|
|
|
|
|
|
16701
|
|
|
|
|
|
|
sub popIndent { |
16702
|
0
|
|
|
0
|
|
|
my $o = shift; |
16703
|
|
|
|
|
|
|
|
16704
|
0
|
|
|
|
|
|
$o->{indentCount} -= 1; |
16705
|
0
|
|
|
|
|
|
$o->{indent} = ' ' x $o->{indentCount}; |
16706
|
0
|
|
|
|
|
|
return; |
16707
|
|
|
|
|
|
|
} |
16708
|
|
|
|
|
|
|
|
16709
|
|
|
|
|
|
|
sub valueIndent { |
16710
|
0
|
|
|
0
|
|
|
my $o = shift; |
16711
|
0
|
|
|
|
|
|
my $width = shift; |
16712
|
|
|
|
|
|
|
|
16713
|
0
|
|
|
|
|
|
$o->{valueIndent} = $width; |
16714
|
|
|
|
|
|
|
} |
16715
|
|
|
|
|
|
|
|
16716
|
|
|
|
|
|
|
### Low-level (non-semantic) output |
16717
|
|
|
|
|
|
|
|
16718
|
|
|
|
|
|
|
sub print { |
16719
|
0
|
|
|
0
|
|
|
my $o = shift; |
16720
|
|
|
|
|
|
|
|
16721
|
0
|
|
0
|
|
|
|
my $fh = $o->{fileHandle} // return; |
16722
|
0
|
|
|
|
|
|
print $fh @_; |
16723
|
|
|
|
|
|
|
} |
16724
|
|
|
|
|
|
|
|
16725
|
|
|
|
|
|
|
sub raw { |
16726
|
0
|
|
|
0
|
|
|
my $o = shift; |
16727
|
|
|
|
|
|
|
|
16728
|
0
|
|
|
|
|
|
$o->removeProgress; |
16729
|
0
|
|
0
|
|
|
|
my $fh = $o->{fileHandle} // return; |
16730
|
0
|
|
|
|
|
|
binmode $fh, ":bytes"; |
16731
|
0
|
|
|
|
|
|
print $fh @_; |
16732
|
0
|
|
|
|
|
|
binmode $fh, ":utf8"; |
16733
|
0
|
|
|
|
|
|
$o->{hasSpace} = 0; |
16734
|
0
|
|
|
|
|
|
return; |
16735
|
|
|
|
|
|
|
} |
16736
|
|
|
|
|
|
|
|
16737
|
|
|
|
|
|
|
sub space { |
16738
|
0
|
|
|
0
|
|
|
my $o = shift; |
16739
|
|
|
|
|
|
|
|
16740
|
0
|
|
|
|
|
|
$o->removeProgress; |
16741
|
0
|
0
|
|
|
|
|
return if $o->{hasSpace}; |
16742
|
0
|
|
|
|
|
|
$o->{hasSpace} = 1; |
16743
|
0
|
|
|
|
|
|
$o->print("\n"); |
16744
|
0
|
|
|
|
|
|
return; |
16745
|
|
|
|
|
|
|
} |
16746
|
|
|
|
|
|
|
|
16747
|
|
|
|
|
|
|
# A line of text (without word-wrap). |
16748
|
|
|
|
|
|
|
sub line { |
16749
|
0
|
|
|
0
|
|
|
my $o = shift; |
16750
|
|
|
|
|
|
|
|
16751
|
0
|
|
|
|
|
|
$o->removeProgress; |
16752
|
0
|
|
|
|
|
|
my $span = CDS::UI::Span->new(@_); |
16753
|
0
|
|
|
|
|
|
$o->print($o->{indent}); |
16754
|
0
|
|
|
|
|
|
$span->printTo($o); |
16755
|
0
|
|
|
|
|
|
$o->print(chr(0x1b), '[0m', "\n"); |
16756
|
0
|
|
|
|
|
|
$o->{hasSpace} = 0; |
16757
|
0
|
|
|
|
|
|
return; |
16758
|
|
|
|
|
|
|
} |
16759
|
|
|
|
|
|
|
|
16760
|
|
|
|
|
|
|
# A line of word-wrapped text. |
16761
|
|
|
|
|
|
|
sub p { |
16762
|
0
|
|
|
0
|
|
|
my $o = shift; |
16763
|
|
|
|
|
|
|
|
16764
|
0
|
|
|
|
|
|
$o->removeProgress; |
16765
|
0
|
|
|
|
|
|
my $span = CDS::UI::Span->new(@_); |
16766
|
0
|
|
|
|
|
|
$span->wordWrap({lineLength => 0, maxLength => 100 - length $o->{indent}, indent => $o->{indent}}); |
16767
|
0
|
|
|
|
|
|
$o->print($o->{indent}); |
16768
|
0
|
|
|
|
|
|
$span->printTo($o); |
16769
|
0
|
|
|
|
|
|
$o->print(chr(0x1b), '[0m', "\n"); |
16770
|
0
|
|
|
|
|
|
$o->{hasSpace} = 0; |
16771
|
0
|
|
|
|
|
|
return; |
16772
|
|
|
|
|
|
|
} |
16773
|
|
|
|
|
|
|
|
16774
|
|
|
|
|
|
|
# Line showing the progress. |
16775
|
|
|
|
|
|
|
sub progress { |
16776
|
0
|
|
|
0
|
|
|
my $o = shift; |
16777
|
|
|
|
|
|
|
|
16778
|
0
|
0
|
|
|
|
|
return if $o->{pure}; |
16779
|
0
|
|
|
|
|
|
$| = 1; |
16780
|
0
|
|
|
|
|
|
$o->{hasProgress} = 1; |
16781
|
0
|
|
|
|
|
|
my $text = ' '.join('', @_); |
16782
|
0
|
0
|
|
|
|
|
$text = substr($text, 0, 79).'…' if length $text > 80; |
16783
|
0
|
0
|
|
|
|
|
$text .= ' ' x (80 - length $text) if length $text < 80; |
16784
|
0
|
|
|
|
|
|
$o->print($text, "\r"); |
16785
|
|
|
|
|
|
|
} |
16786
|
|
|
|
|
|
|
|
16787
|
|
|
|
|
|
|
# Progress line removal. |
16788
|
|
|
|
|
|
|
sub removeProgress { |
16789
|
0
|
|
|
0
|
|
|
my $o = shift; |
16790
|
|
|
|
|
|
|
|
16791
|
0
|
0
|
|
|
|
|
return if $o->{pure}; |
16792
|
0
|
0
|
|
|
|
|
return if ! $o->{hasProgress}; |
16793
|
0
|
|
|
|
|
|
$o->print(' ' x 80, "\r"); |
16794
|
0
|
|
|
|
|
|
$o->{hasProgress} = 0; |
16795
|
0
|
|
|
|
|
|
$| = 0; |
16796
|
|
|
|
|
|
|
} |
16797
|
|
|
|
|
|
|
|
16798
|
|
|
|
|
|
|
### Low-level (non-semantic) formatting |
16799
|
|
|
|
|
|
|
|
16800
|
|
|
|
|
|
|
sub span { |
16801
|
0
|
|
|
0
|
|
|
my $o = shift; |
16802
|
0
|
|
|
|
|
|
CDS::UI::Span->new(@_) } |
16803
|
|
|
|
|
|
|
|
16804
|
|
|
|
|
|
|
sub bold { |
16805
|
0
|
|
|
0
|
|
|
my $o = shift; |
16806
|
|
|
|
|
|
|
|
16807
|
0
|
|
|
|
|
|
my $span = CDS::UI::Span->new(@_); |
16808
|
0
|
|
|
|
|
|
$span->{bold} = 1; |
16809
|
0
|
|
|
|
|
|
return $span; |
16810
|
|
|
|
|
|
|
} |
16811
|
|
|
|
|
|
|
|
16812
|
|
|
|
|
|
|
sub underlined { |
16813
|
0
|
|
|
0
|
|
|
my $o = shift; |
16814
|
|
|
|
|
|
|
|
16815
|
0
|
|
|
|
|
|
my $span = CDS::UI::Span->new(@_); |
16816
|
0
|
|
|
|
|
|
$span->{underlined} = 1; |
16817
|
0
|
|
|
|
|
|
return $span; |
16818
|
|
|
|
|
|
|
} |
16819
|
|
|
|
|
|
|
|
16820
|
|
|
|
|
|
|
sub foreground { |
16821
|
0
|
|
|
0
|
|
|
my $o = shift; |
16822
|
0
|
|
|
|
|
|
my $foreground = shift; |
16823
|
|
|
|
|
|
|
|
16824
|
0
|
|
|
|
|
|
my $span = CDS::UI::Span->new(@_); |
16825
|
0
|
|
|
|
|
|
$span->{foreground} = $foreground; |
16826
|
0
|
|
|
|
|
|
return $span; |
16827
|
|
|
|
|
|
|
} |
16828
|
|
|
|
|
|
|
|
16829
|
|
|
|
|
|
|
sub background { |
16830
|
0
|
|
|
0
|
|
|
my $o = shift; |
16831
|
0
|
|
|
|
|
|
my $background = shift; |
16832
|
|
|
|
|
|
|
|
16833
|
0
|
|
|
|
|
|
my $span = CDS::UI::Span->new(@_); |
16834
|
0
|
|
|
|
|
|
$span->{background} = $background; |
16835
|
0
|
|
|
|
|
|
return $span; |
16836
|
|
|
|
|
|
|
} |
16837
|
|
|
|
|
|
|
|
16838
|
|
|
|
|
|
|
sub red { |
16839
|
0
|
|
|
0
|
|
|
my $o = shift; |
16840
|
0
|
|
|
|
|
|
$o->foreground(196, @_) } # for failure |
16841
|
|
|
|
|
|
|
sub green { |
16842
|
0
|
|
|
0
|
|
|
my $o = shift; |
16843
|
0
|
|
|
|
|
|
$o->foreground(40, @_) } # for success |
16844
|
|
|
|
|
|
|
sub orange { |
16845
|
0
|
|
|
0
|
|
|
my $o = shift; |
16846
|
0
|
|
|
|
|
|
$o->foreground(166, @_) } # for warnings |
16847
|
|
|
|
|
|
|
sub blue { |
16848
|
0
|
|
|
0
|
|
|
my $o = shift; |
16849
|
0
|
|
|
|
|
|
$o->foreground(33, @_) } # to highlight something (selection) |
16850
|
|
|
|
|
|
|
sub violet { |
16851
|
0
|
|
|
0
|
|
|
my $o = shift; |
16852
|
0
|
|
|
|
|
|
$o->foreground(93, @_) } # to highlight something (selection) |
16853
|
|
|
|
|
|
|
sub gold { |
16854
|
0
|
|
|
0
|
|
|
my $o = shift; |
16855
|
0
|
|
|
|
|
|
$o->foreground(238, @_) } # for commands that can be executed |
16856
|
|
|
|
|
|
|
sub gray { |
16857
|
0
|
|
|
0
|
|
|
my $o = shift; |
16858
|
0
|
|
|
|
|
|
$o->foreground(246, @_) } # for additional (less important) information |
16859
|
|
|
|
|
|
|
|
16860
|
|
|
|
|
|
|
sub darkBold { |
16861
|
0
|
|
|
0
|
|
|
my $o = shift; |
16862
|
|
|
|
|
|
|
|
16863
|
0
|
|
|
|
|
|
my $span = CDS::UI::Span->new(@_); |
16864
|
0
|
|
|
|
|
|
$span->{bold} = 1; |
16865
|
0
|
|
|
|
|
|
$span->{foreground} = 240; |
16866
|
0
|
|
|
|
|
|
return $span; |
16867
|
|
|
|
|
|
|
} |
16868
|
|
|
|
|
|
|
|
16869
|
|
|
|
|
|
|
### Semantic output |
16870
|
|
|
|
|
|
|
|
16871
|
|
|
|
|
|
|
sub title { |
16872
|
0
|
|
|
0
|
|
|
my $o = shift; |
16873
|
0
|
|
|
|
|
|
$o->line($o->bold(@_)) } |
16874
|
|
|
|
|
|
|
|
16875
|
|
|
|
|
|
|
sub left { |
16876
|
0
|
|
|
0
|
|
|
my $o = shift; |
16877
|
0
|
|
|
|
|
|
my $width = shift; |
16878
|
0
|
|
|
|
|
|
my $text = shift; |
16879
|
|
|
|
|
|
|
|
16880
|
0
|
0
|
|
|
|
|
return substr($text, 0, $width - 1).'…' if length $text > $width; |
16881
|
0
|
|
|
|
|
|
return $text . ' ' x ($width - length $text); |
16882
|
|
|
|
|
|
|
} |
16883
|
|
|
|
|
|
|
|
16884
|
|
|
|
|
|
|
sub right { |
16885
|
0
|
|
|
0
|
|
|
my $o = shift; |
16886
|
0
|
|
|
|
|
|
my $width = shift; |
16887
|
0
|
|
|
|
|
|
my $text = shift; |
16888
|
|
|
|
|
|
|
|
16889
|
0
|
0
|
|
|
|
|
return substr($text, 0, $width - 1).'…' if length $text > $width; |
16890
|
0
|
|
|
|
|
|
return ' ' x ($width - length $text) . $text; |
16891
|
|
|
|
|
|
|
} |
16892
|
|
|
|
|
|
|
|
16893
|
|
|
|
|
|
|
sub keyValue { |
16894
|
0
|
|
|
0
|
|
|
my $o = shift; |
16895
|
0
|
|
|
|
|
|
my $key = shift; |
16896
|
0
|
|
|
|
|
|
my $firstLine = shift; |
16897
|
|
|
|
|
|
|
|
16898
|
0
|
|
|
|
|
|
my $indent = $o->{valueIndent} - length $o->{indent}; |
16899
|
0
|
0
|
0
|
|
|
|
$key = substr($key, 0, $indent - 2).'…' if defined $firstLine && length $key >= $indent; |
16900
|
0
|
|
|
|
|
|
$key .= ' ' x ($indent - length $key); |
16901
|
0
|
|
|
|
|
|
$o->line($o->gray($key), $firstLine); |
16902
|
0
|
|
|
|
|
|
my $noKey = ' ' x $indent; |
16903
|
0
|
|
|
|
|
|
for my $line (@_) { $o->line($noKey, $line); } |
|
0
|
|
|
|
|
|
|
16904
|
0
|
|
|
|
|
|
return; |
16905
|
|
|
|
|
|
|
} |
16906
|
|
|
|
|
|
|
|
16907
|
|
|
|
|
|
|
sub command { |
16908
|
0
|
|
|
0
|
|
|
my $o = shift; |
16909
|
0
|
|
|
|
|
|
$o->line($o->bold(@_)) } |
16910
|
|
|
|
|
|
|
|
16911
|
|
|
|
|
|
|
sub verbose { |
16912
|
0
|
|
|
0
|
|
|
my $o = shift; |
16913
|
0
|
0
|
|
|
|
|
$o->line($o->foreground(45, @_)) if $o->{verbose} } |
16914
|
|
|
|
|
|
|
|
16915
|
|
|
|
|
|
|
sub pGreen { |
16916
|
0
|
|
|
0
|
|
|
my $o = shift; |
16917
|
|
|
|
|
|
|
|
16918
|
0
|
|
|
|
|
|
$o->p($o->green(@_)); |
16919
|
0
|
|
|
|
|
|
return; |
16920
|
|
|
|
|
|
|
} |
16921
|
|
|
|
|
|
|
|
16922
|
|
|
|
|
|
|
sub pOrange { |
16923
|
0
|
|
|
0
|
|
|
my $o = shift; |
16924
|
|
|
|
|
|
|
|
16925
|
0
|
|
|
|
|
|
$o->p($o->orange(@_)); |
16926
|
0
|
|
|
|
|
|
return; |
16927
|
|
|
|
|
|
|
} |
16928
|
|
|
|
|
|
|
|
16929
|
|
|
|
|
|
|
sub pRed { |
16930
|
0
|
|
|
0
|
|
|
my $o = shift; |
16931
|
|
|
|
|
|
|
|
16932
|
0
|
|
|
|
|
|
$o->p($o->red(@_)); |
16933
|
0
|
|
|
|
|
|
return; |
16934
|
|
|
|
|
|
|
} |
16935
|
|
|
|
|
|
|
|
16936
|
|
|
|
|
|
|
### Warnings and errors |
16937
|
|
|
|
|
|
|
|
16938
|
0
|
|
|
0
|
|
|
sub hasWarning { shift->{hasWarning} } |
16939
|
0
|
|
|
0
|
|
|
sub hasError { shift->{hasError} } |
16940
|
|
|
|
|
|
|
|
16941
|
|
|
|
|
|
|
sub warning { |
16942
|
0
|
|
|
0
|
|
|
my $o = shift; |
16943
|
|
|
|
|
|
|
|
16944
|
0
|
|
|
|
|
|
$o->{hasWarning} = 1; |
16945
|
0
|
|
|
|
|
|
$o->p($o->orange(@_)); |
16946
|
0
|
|
|
|
|
|
return; |
16947
|
|
|
|
|
|
|
} |
16948
|
|
|
|
|
|
|
|
16949
|
|
|
|
|
|
|
sub error { |
16950
|
0
|
|
|
0
|
|
|
my $o = shift; |
16951
|
|
|
|
|
|
|
|
16952
|
0
|
|
|
|
|
|
$o->{hasError} = 1; |
16953
|
0
|
|
|
|
|
|
my $span = CDS::UI::Span->new(@_); |
16954
|
0
|
|
|
|
|
|
$span->{background} = 196; |
16955
|
0
|
|
|
|
|
|
$span->{foreground} = 15; |
16956
|
0
|
|
|
|
|
|
$span->{bold} = 1; |
16957
|
0
|
|
|
|
|
|
$o->line($span); |
16958
|
0
|
|
|
|
|
|
return; |
16959
|
|
|
|
|
|
|
} |
16960
|
|
|
|
|
|
|
|
16961
|
|
|
|
|
|
|
### Semantic formatting |
16962
|
|
|
|
|
|
|
|
16963
|
|
|
|
|
|
|
sub a { |
16964
|
0
|
|
|
0
|
|
|
my $o = shift; |
16965
|
0
|
|
|
|
|
|
$o->underlined(@_) } |
16966
|
|
|
|
|
|
|
|
16967
|
|
|
|
|
|
|
### Human readable formats |
16968
|
|
|
|
|
|
|
|
16969
|
|
|
|
|
|
|
sub niceBytes { |
16970
|
0
|
|
|
0
|
|
|
my $o = shift; |
16971
|
0
|
|
|
|
|
|
my $bytes = shift; |
16972
|
0
|
|
|
|
|
|
my $maxLength = shift; |
16973
|
|
|
|
|
|
|
|
16974
|
0
|
|
|
|
|
|
my $length = length $bytes; |
16975
|
0
|
0
|
0
|
|
|
|
my $text = defined $maxLength && $length > $maxLength ? substr($bytes, 0, $maxLength - 1).'…' : $bytes; |
16976
|
0
|
|
|
|
|
|
$text =~ s/[\x00-\x1f\x7f-\xff]/./g; |
16977
|
0
|
|
|
|
|
|
return $text; |
16978
|
|
|
|
|
|
|
} |
16979
|
|
|
|
|
|
|
|
16980
|
|
|
|
|
|
|
sub niceFileSize { |
16981
|
0
|
|
|
0
|
|
|
my $o = shift; |
16982
|
0
|
|
|
|
|
|
my $fileSize = shift; |
16983
|
|
|
|
|
|
|
|
16984
|
0
|
0
|
|
|
|
|
return $fileSize.' bytes' if $fileSize < 1000; |
16985
|
0
|
0
|
|
|
|
|
return sprintf('%0.1f', $fileSize / 1000).' KB' if $fileSize < 10000; |
16986
|
0
|
0
|
|
|
|
|
return sprintf('%0.0f', $fileSize / 1000).' KB' if $fileSize < 1000000; |
16987
|
0
|
0
|
|
|
|
|
return sprintf('%0.1f', $fileSize / 1000000).' MB' if $fileSize < 10000000; |
16988
|
0
|
0
|
|
|
|
|
return sprintf('%0.0f', $fileSize / 1000000).' MB' if $fileSize < 1000000000; |
16989
|
0
|
0
|
|
|
|
|
return sprintf('%0.1f', $fileSize / 1000000000).' GB' if $fileSize < 10000000000; |
16990
|
0
|
|
|
|
|
|
return sprintf('%0.0f', $fileSize / 1000000000).' GB'; |
16991
|
|
|
|
|
|
|
} |
16992
|
|
|
|
|
|
|
|
16993
|
|
|
|
|
|
|
sub niceDateTimeLocal { |
16994
|
0
|
|
|
0
|
|
|
my $o = shift; |
16995
|
0
|
|
0
|
|
|
|
my $time = shift // time() * 1000; |
16996
|
|
|
|
|
|
|
|
16997
|
0
|
|
|
|
|
|
my @t = localtime($time / 1000); |
16998
|
0
|
|
|
|
|
|
return sprintf('%04d-%02d-%02d %02d:%02d:%02d', $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]); |
16999
|
|
|
|
|
|
|
} |
17000
|
|
|
|
|
|
|
|
17001
|
|
|
|
|
|
|
sub niceDateTime { |
17002
|
0
|
|
|
0
|
|
|
my $o = shift; |
17003
|
0
|
|
0
|
|
|
|
my $time = shift // time() * 1000; |
17004
|
|
|
|
|
|
|
|
17005
|
0
|
|
|
|
|
|
my @t = gmtime($time / 1000); |
17006
|
0
|
|
|
|
|
|
return sprintf('%04d-%02d-%02d %02d:%02d:%02d UTC', $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0]); |
17007
|
|
|
|
|
|
|
} |
17008
|
|
|
|
|
|
|
|
17009
|
|
|
|
|
|
|
sub niceDate { |
17010
|
0
|
|
|
0
|
|
|
my $o = shift; |
17011
|
0
|
|
0
|
|
|
|
my $time = shift // time() * 1000; |
17012
|
|
|
|
|
|
|
|
17013
|
0
|
|
|
|
|
|
my @t = gmtime($time / 1000); |
17014
|
0
|
|
|
|
|
|
return sprintf('%04d-%02d-%02d', $t[5] + 1900, $t[4] + 1, $t[3]); |
17015
|
|
|
|
|
|
|
} |
17016
|
|
|
|
|
|
|
|
17017
|
|
|
|
|
|
|
sub niceTime { |
17018
|
0
|
|
|
0
|
|
|
my $o = shift; |
17019
|
0
|
|
0
|
|
|
|
my $time = shift // time() * 1000; |
17020
|
|
|
|
|
|
|
|
17021
|
0
|
|
|
|
|
|
my @t = gmtime($time / 1000); |
17022
|
0
|
|
|
|
|
|
return sprintf('%02d:%02d:%02d UTC', $t[2], $t[1], $t[0]); |
17023
|
|
|
|
|
|
|
} |
17024
|
|
|
|
|
|
|
|
17025
|
|
|
|
|
|
|
### Special output |
17026
|
|
|
|
|
|
|
|
17027
|
|
|
|
|
|
|
sub record { |
17028
|
0
|
|
|
0
|
|
|
my $o = shift; |
17029
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
17030
|
0
|
|
|
|
|
|
my $storeUrl = shift; |
17031
|
0
|
|
|
|
|
|
CDS::UI::Record->display($o, $record, $storeUrl) } |
17032
|
|
|
|
|
|
|
|
17033
|
|
|
|
|
|
|
sub recordChildren { |
17034
|
0
|
|
|
0
|
|
|
my $o = shift; |
17035
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
17036
|
0
|
|
|
|
|
|
my $storeUrl = shift; |
17037
|
|
|
|
|
|
|
|
17038
|
0
|
|
|
|
|
|
for my $child ($record->children) { |
17039
|
0
|
|
|
|
|
|
CDS::UI::Record->display($o, $child, $storeUrl); |
17040
|
|
|
|
|
|
|
} |
17041
|
|
|
|
|
|
|
} |
17042
|
|
|
|
|
|
|
|
17043
|
|
|
|
|
|
|
sub selector { |
17044
|
0
|
|
|
0
|
|
|
my $o = shift; |
17045
|
0
|
0
|
0
|
|
|
|
my $selector = shift; die 'wrong type '.ref($selector).' for $selector' if defined $selector && ref $selector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
|
|
17046
|
0
|
|
|
|
|
|
my $rootLabel = shift; |
17047
|
|
|
|
|
|
|
|
17048
|
0
|
|
|
|
|
|
my $item = $selector->document->get($selector); |
17049
|
0
|
0
|
|
|
|
|
my $revision = $item->{revision} ? $o->green(' ', $o->niceDateTime($item->{revision})) : ''; |
17050
|
|
|
|
|
|
|
|
17051
|
0
|
0
|
|
|
|
|
if ($selector->{id} eq 'ROOT') { |
17052
|
0
|
|
0
|
|
|
|
$o->line($o->bold($rootLabel // 'Data tree'), $revision); |
17053
|
0
|
|
|
|
|
|
$o->recordChildren($selector->record); |
17054
|
0
|
|
|
|
|
|
$o->selectorChildren($selector); |
17055
|
|
|
|
|
|
|
} else { |
17056
|
0
|
|
|
|
|
|
my $label = $selector->label; |
17057
|
0
|
0
|
|
|
|
|
my $labelText = length $label > 64 ? substr($label, 0, 64).'…' : $label; |
17058
|
0
|
|
|
|
|
|
$labelText =~ s/[\x00-\x1f\x7f-\xff]/·/g; |
17059
|
0
|
|
|
|
|
|
$o->line($o->blue($labelText), $revision); |
17060
|
|
|
|
|
|
|
|
17061
|
0
|
|
|
|
|
|
$o->pushIndent; |
17062
|
0
|
|
|
|
|
|
$o->recordChildren($selector->record); |
17063
|
0
|
|
|
|
|
|
$o->selectorChildren($selector); |
17064
|
0
|
|
|
|
|
|
$o->popIndent; |
17065
|
|
|
|
|
|
|
} |
17066
|
|
|
|
|
|
|
} |
17067
|
|
|
|
|
|
|
|
17068
|
|
|
|
|
|
|
sub selectorChildren { |
17069
|
0
|
|
|
0
|
|
|
my $o = shift; |
17070
|
0
|
0
|
0
|
|
|
|
my $selector = shift; die 'wrong type '.ref($selector).' for $selector' if defined $selector && ref $selector ne 'CDS::Selector'; |
|
0
|
|
|
|
|
|
|
17071
|
|
|
|
|
|
|
|
17072
|
0
|
|
|
|
|
|
for my $child (sort { $a->{id} cmp $b->{id} } $selector->children) { |
|
0
|
|
|
|
|
|
|
17073
|
0
|
|
|
|
|
|
$o->selector($child); |
17074
|
|
|
|
|
|
|
} |
17075
|
|
|
|
|
|
|
} |
17076
|
|
|
|
|
|
|
|
17077
|
|
|
|
|
|
|
sub hexDump { |
17078
|
0
|
|
|
0
|
|
|
my $o = shift; |
17079
|
0
|
|
|
|
|
|
my $bytes = shift; |
17080
|
0
|
|
|
|
|
|
CDS::UI::HexDump->new($o, $bytes) } |
17081
|
|
|
|
|
|
|
|
17082
|
|
|
|
|
|
|
package CDS::UI::HexDump; |
17083
|
|
|
|
|
|
|
|
17084
|
|
|
|
|
|
|
sub new { |
17085
|
0
|
|
|
0
|
|
|
my $class = shift; |
17086
|
0
|
|
|
|
|
|
my $ui = shift; |
17087
|
0
|
|
|
|
|
|
my $bytes = shift; |
17088
|
|
|
|
|
|
|
|
17089
|
0
|
|
|
|
|
|
return bless {ui => $ui, bytes => $bytes, styleChanges => [], }; |
17090
|
|
|
|
|
|
|
} |
17091
|
|
|
|
|
|
|
|
17092
|
0
|
|
|
0
|
|
|
sub reset { chr(0x1b).'[0m' } |
17093
|
|
|
|
|
|
|
sub foreground { |
17094
|
0
|
|
|
0
|
|
|
my $o = shift; |
17095
|
0
|
|
|
|
|
|
my $color = shift; |
17096
|
0
|
|
|
|
|
|
chr(0x1b).'[0;38;5;'.$color.'m' } |
17097
|
|
|
|
|
|
|
|
17098
|
|
|
|
|
|
|
sub changeStyle { |
17099
|
0
|
|
|
0
|
|
|
my $o = shift; |
17100
|
|
|
|
|
|
|
|
17101
|
0
|
|
|
|
|
|
push @{$o->{styleChanges}}, @_; |
|
0
|
|
|
|
|
|
|
17102
|
|
|
|
|
|
|
} |
17103
|
|
|
|
|
|
|
|
17104
|
|
|
|
|
|
|
sub styleHashList { |
17105
|
0
|
|
|
0
|
|
|
my $o = shift; |
17106
|
0
|
|
|
|
|
|
my $offset = shift; |
17107
|
|
|
|
|
|
|
|
17108
|
0
|
|
|
|
|
|
my $hashesCount = unpack('L>', substr($o->{bytes}, $offset, 4)); |
17109
|
0
|
|
|
|
|
|
my $dataStart = $offset + 4 + $hashesCount * 32; |
17110
|
0
|
0
|
|
|
|
|
return $offset if $dataStart > length $o->{bytes}; |
17111
|
|
|
|
|
|
|
|
17112
|
|
|
|
|
|
|
# Styles |
17113
|
0
|
|
|
|
|
|
my $darkGreen = $o->foreground(28); |
17114
|
0
|
|
|
|
|
|
my $green0 = $o->foreground(40); |
17115
|
0
|
|
|
|
|
|
my $green1 = $o->foreground(34); |
17116
|
|
|
|
|
|
|
|
17117
|
|
|
|
|
|
|
# Color the hash count |
17118
|
0
|
|
|
|
|
|
my $pos = $offset; |
17119
|
0
|
|
|
|
|
|
$o->changeStyle({at => $pos, style => $darkGreen, breakBefore => 1}); |
17120
|
0
|
|
|
|
|
|
$pos += 4; |
17121
|
|
|
|
|
|
|
|
17122
|
|
|
|
|
|
|
# Color the hashes |
17123
|
0
|
|
|
|
|
|
my $alternate = 0; |
17124
|
0
|
|
|
|
|
|
while ($hashesCount) { |
17125
|
0
|
0
|
|
|
|
|
$o->changeStyle({at => $pos, style => $alternate ? $green1 : $green0, breakBefore => 1}); |
17126
|
0
|
|
|
|
|
|
$pos += 32; |
17127
|
0
|
|
|
|
|
|
$alternate = 1 - $alternate; |
17128
|
0
|
|
|
|
|
|
$hashesCount -= 1; |
17129
|
|
|
|
|
|
|
} |
17130
|
|
|
|
|
|
|
|
17131
|
0
|
|
|
|
|
|
return $dataStart; |
17132
|
|
|
|
|
|
|
} |
17133
|
|
|
|
|
|
|
|
17134
|
|
|
|
|
|
|
sub styleRecord { |
17135
|
0
|
|
|
0
|
|
|
my $o = shift; |
17136
|
0
|
|
|
|
|
|
my $offset = shift; |
17137
|
|
|
|
|
|
|
|
17138
|
|
|
|
|
|
|
# Styles |
17139
|
0
|
|
|
|
|
|
my $blue = $o->foreground(33); |
17140
|
0
|
|
|
|
|
|
my $black = $o->reset; |
17141
|
0
|
|
|
|
|
|
my $violet = $o->foreground(93); |
17142
|
0
|
|
|
|
|
|
my @styleChanges; |
17143
|
|
|
|
|
|
|
|
17144
|
|
|
|
|
|
|
# Prepare |
17145
|
0
|
|
|
|
|
|
my $pos = $offset; |
17146
|
0
|
|
|
|
|
|
my $hasError = 0; |
17147
|
0
|
|
|
|
|
|
my $level = 0; |
17148
|
|
|
|
|
|
|
|
17149
|
0
|
|
|
0
|
|
|
my $use = sub { my $length = shift; |
17150
|
0
|
|
|
|
|
|
my $start = $pos; |
17151
|
0
|
|
|
|
|
|
$pos += $length; |
17152
|
0
|
0
|
|
|
|
|
return substr($o->{bytes}, $start, $length) if $pos <= length $o->{bytes}; |
17153
|
0
|
|
|
|
|
|
$hasError = 1; |
17154
|
0
|
|
|
|
|
|
return; |
17155
|
0
|
|
|
|
|
|
}; |
17156
|
|
|
|
|
|
|
|
17157
|
0
|
|
0
|
0
|
|
|
my $readUnsigned8 = sub { unpack('C', &$use(1) // return) }; |
|
0
|
|
|
|
|
|
|
17158
|
0
|
|
0
|
0
|
|
|
my $readUnsigned32 = sub { unpack('L>', &$use(4) // return) }; |
|
0
|
|
|
|
|
|
|
17159
|
0
|
|
0
|
0
|
|
|
my $readUnsigned64 = sub { unpack('Q>', &$use(8) // return) }; |
|
0
|
|
|
|
|
|
|
17160
|
|
|
|
|
|
|
|
17161
|
|
|
|
|
|
|
# Parse all record nodes |
17162
|
0
|
|
|
|
|
|
while ($level >= 0) { |
17163
|
|
|
|
|
|
|
# Flags |
17164
|
0
|
|
|
|
|
|
push @styleChanges, {at => $pos, style => $blue, breakBefore => 1}; |
17165
|
0
|
|
0
|
|
|
|
my $flags = &$readUnsigned8 // last; |
17166
|
|
|
|
|
|
|
|
17167
|
|
|
|
|
|
|
# Data |
17168
|
0
|
|
|
|
|
|
my $length = $flags & 0x1f; |
17169
|
0
|
0
|
0
|
|
|
|
my $byteLength = $length == 30 ? 30 + (&$readUnsigned8 // last) : $length == 31 ? (&$readUnsigned64 // last) : $length; |
|
|
0
|
0
|
|
|
|
|
17170
|
|
|
|
|
|
|
|
17171
|
0
|
0
|
|
|
|
|
if ($byteLength) { |
17172
|
0
|
|
|
|
|
|
push @styleChanges, {at => $pos, style => $black}; |
17173
|
0
|
|
0
|
|
|
|
&$use($byteLength) // last; |
17174
|
|
|
|
|
|
|
} |
17175
|
|
|
|
|
|
|
|
17176
|
0
|
0
|
|
|
|
|
if ($flags & 0x20) { |
17177
|
0
|
|
|
|
|
|
push @styleChanges, {at => $pos, style => $violet}; |
17178
|
0
|
|
0
|
|
|
|
&$readUnsigned32 // last; |
17179
|
|
|
|
|
|
|
} |
17180
|
|
|
|
|
|
|
|
17181
|
|
|
|
|
|
|
# Children |
17182
|
0
|
0
|
|
|
|
|
$level += 1 if $flags & 0x40; |
17183
|
0
|
0
|
|
|
|
|
$level -= 1 if ! ($flags & 0x80); |
17184
|
|
|
|
|
|
|
} |
17185
|
|
|
|
|
|
|
|
17186
|
|
|
|
|
|
|
# Don't apply any styles if there are errors |
17187
|
0
|
0
|
|
|
|
|
$hasError = 1 if $pos != length $o->{bytes}; |
17188
|
0
|
0
|
|
|
|
|
return $offset if $hasError; |
17189
|
|
|
|
|
|
|
|
17190
|
0
|
|
|
|
|
|
$o->changeStyle(@styleChanges); |
17191
|
0
|
|
|
|
|
|
return $pos; |
17192
|
|
|
|
|
|
|
} |
17193
|
|
|
|
|
|
|
|
17194
|
|
|
|
|
|
|
sub display { |
17195
|
0
|
|
|
0
|
|
|
my $o = shift; |
17196
|
|
|
|
|
|
|
|
17197
|
0
|
|
|
|
|
|
$o->{ui}->valueIndent(8); |
17198
|
|
|
|
|
|
|
|
17199
|
0
|
|
|
|
|
|
my $resetStyle = chr(0x1b).'[0m'; |
17200
|
0
|
|
|
|
|
|
my $length = length($o->{bytes}); |
17201
|
0
|
|
|
|
|
|
my $lineStart = 0; |
17202
|
0
|
|
|
|
|
|
my $currentStyle = ''; |
17203
|
|
|
|
|
|
|
|
17204
|
0
|
|
|
|
|
|
my @styleChanges = sort { $a->{at} <=> $b->{at} } @{$o->{styleChanges}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
17205
|
0
|
|
|
|
|
|
push @styleChanges, {at => $length}; |
17206
|
0
|
|
|
|
|
|
my $nextChange = shift(@styleChanges); |
17207
|
|
|
|
|
|
|
|
17208
|
0
|
|
|
|
|
|
$o->{ui}->line($o->{ui}->gray('···· 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef')); |
17209
|
0
|
|
|
|
|
|
while ($lineStart < $length) { |
17210
|
0
|
|
|
|
|
|
my $hexLine = $currentStyle; |
17211
|
0
|
|
|
|
|
|
my $textLine = $currentStyle; |
17212
|
|
|
|
|
|
|
|
17213
|
0
|
|
|
|
|
|
my $k = 0; |
17214
|
0
|
|
|
|
|
|
while ($k < 16) { |
17215
|
0
|
|
|
|
|
|
my $index = $lineStart + $k; |
17216
|
0
|
0
|
|
|
|
|
last if $index >= $length; |
17217
|
|
|
|
|
|
|
|
17218
|
0
|
|
|
|
|
|
my $break = 0; |
17219
|
0
|
|
|
|
|
|
while ($index >= $nextChange->{at}) { |
17220
|
0
|
|
|
|
|
|
$currentStyle = $nextChange->{style}; |
17221
|
0
|
|
0
|
|
|
|
$break = $nextChange->{breakBefore} && $k > 0; |
17222
|
0
|
|
|
|
|
|
$hexLine .= $currentStyle; |
17223
|
0
|
|
|
|
|
|
$textLine .= $currentStyle; |
17224
|
0
|
|
|
|
|
|
$nextChange = shift @styleChanges; |
17225
|
0
|
0
|
|
|
|
|
last if $break; |
17226
|
|
|
|
|
|
|
} |
17227
|
|
|
|
|
|
|
|
17228
|
0
|
0
|
|
|
|
|
last if $break; |
17229
|
|
|
|
|
|
|
|
17230
|
0
|
|
|
|
|
|
my $byte = substr($o->{bytes}, $lineStart + $k, 1); |
17231
|
0
|
|
|
|
|
|
$hexLine .= ' '.unpack('H*', $byte); |
17232
|
|
|
|
|
|
|
|
17233
|
0
|
|
|
|
|
|
my $code = ord($byte); |
17234
|
0
|
0
|
0
|
|
|
|
$textLine .= $code >= 32 && $code <= 126 ? $byte : '·'; |
17235
|
|
|
|
|
|
|
|
17236
|
0
|
|
|
|
|
|
$k += 1; |
17237
|
|
|
|
|
|
|
} |
17238
|
|
|
|
|
|
|
|
17239
|
0
|
|
|
|
|
|
$hexLine .= ' ' x (16 - $k); |
17240
|
0
|
|
|
|
|
|
$textLine .= ' ' x (16 - $k); |
17241
|
0
|
|
|
|
|
|
$o->{ui}->line($o->{ui}->gray(unpack('H4', pack('S>', $lineStart))), ' ', $hexLine, $resetStyle, ' ', $textLine, $resetStyle); |
17242
|
|
|
|
|
|
|
|
17243
|
0
|
|
|
|
|
|
$lineStart += $k; |
17244
|
|
|
|
|
|
|
} |
17245
|
|
|
|
|
|
|
} |
17246
|
|
|
|
|
|
|
|
17247
|
|
|
|
|
|
|
package CDS::UI::ProgressStore; |
17248
|
|
|
|
|
|
|
|
17249
|
1
|
|
|
1
|
|
4786
|
use parent -norequire, 'CDS::Store'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
11
|
|
17250
|
|
|
|
|
|
|
|
17251
|
|
|
|
|
|
|
sub new { |
17252
|
0
|
|
|
0
|
|
|
my $class = shift; |
17253
|
0
|
|
|
|
|
|
my $store = shift; |
17254
|
0
|
|
|
|
|
|
my $url = shift; |
17255
|
0
|
|
|
|
|
|
my $ui = shift; |
17256
|
|
|
|
|
|
|
|
17257
|
0
|
|
|
|
|
|
return bless { |
17258
|
|
|
|
|
|
|
store => $store, |
17259
|
|
|
|
|
|
|
url => $url, |
17260
|
|
|
|
|
|
|
ui => $ui, |
17261
|
|
|
|
|
|
|
} |
17262
|
|
|
|
|
|
|
} |
17263
|
|
|
|
|
|
|
|
17264
|
0
|
|
|
0
|
|
|
sub store { shift->{store} } |
17265
|
0
|
|
|
0
|
|
|
sub url { shift->{url} } |
17266
|
0
|
|
|
0
|
|
|
sub ui { shift->{ui} } |
17267
|
|
|
|
|
|
|
|
17268
|
|
|
|
|
|
|
sub id { |
17269
|
0
|
|
|
0
|
|
|
my $o = shift; |
17270
|
0
|
|
|
|
|
|
'Progress'."\n ".$o->{store}->id } |
17271
|
|
|
|
|
|
|
|
17272
|
|
|
|
|
|
|
### Object store functions |
17273
|
|
|
|
|
|
|
|
17274
|
|
|
|
|
|
|
sub get { |
17275
|
0
|
|
|
0
|
|
|
my $o = shift; |
17276
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17277
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17278
|
|
|
|
|
|
|
|
17279
|
0
|
|
|
|
|
|
$o->{ui}->progress('GET ', $hash->shortHex, ' on ', $o->{url}); |
17280
|
0
|
|
|
|
|
|
return $o->{store}->get($hash, $keyPair); |
17281
|
|
|
|
|
|
|
} |
17282
|
|
|
|
|
|
|
|
17283
|
|
|
|
|
|
|
sub book { |
17284
|
0
|
|
|
0
|
|
|
my $o = shift; |
17285
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17286
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17287
|
|
|
|
|
|
|
|
17288
|
0
|
|
|
|
|
|
$o->{ui}->progress('BOOK ', $hash->shortHex, ' on ', $o->{url}); |
17289
|
0
|
|
|
|
|
|
return $o->{store}->book($hash, $keyPair); |
17290
|
|
|
|
|
|
|
} |
17291
|
|
|
|
|
|
|
|
17292
|
|
|
|
|
|
|
sub put { |
17293
|
0
|
|
|
0
|
|
|
my $o = shift; |
17294
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17295
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
17296
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17297
|
|
|
|
|
|
|
|
17298
|
0
|
|
|
|
|
|
$o->{ui}->progress('PUT ', $hash->shortHex, ' (', $o->{ui}->niceFileSize($object->byteLength), ') on ', $o->{url}); |
17299
|
0
|
|
|
|
|
|
return $o->{store}->put($hash, $object, $keyPair); |
17300
|
|
|
|
|
|
|
} |
17301
|
|
|
|
|
|
|
|
17302
|
|
|
|
|
|
|
### Account store functions |
17303
|
|
|
|
|
|
|
|
17304
|
|
|
|
|
|
|
sub list { |
17305
|
0
|
|
|
0
|
|
|
my $o = shift; |
17306
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17307
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
17308
|
0
|
|
|
|
|
|
my $timeout = shift; |
17309
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17310
|
|
|
|
|
|
|
|
17311
|
0
|
0
|
|
|
|
|
$o->{ui}->progress($timeout == 0 ? 'LIST ' : 'WATCH ', $boxLabel, ' of ', $accountHash->shortHex, ' on ', $o->{url}); |
17312
|
0
|
|
|
|
|
|
return $o->{store}->list($accountHash, $boxLabel, $timeout, $keyPair); |
17313
|
|
|
|
|
|
|
} |
17314
|
|
|
|
|
|
|
|
17315
|
|
|
|
|
|
|
sub add { |
17316
|
0
|
|
|
0
|
|
|
my $o = shift; |
17317
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17318
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
17319
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17320
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17321
|
|
|
|
|
|
|
|
17322
|
0
|
|
|
|
|
|
$o->{ui}->progress('ADD ', $accountHash->shortHex, ' ', $boxLabel, ' ', $hash->shortHex, ' on ', $o->{url}); |
17323
|
0
|
|
|
|
|
|
return $o->{store}->add($accountHash, $boxLabel, $hash, $keyPair); |
17324
|
|
|
|
|
|
|
} |
17325
|
|
|
|
|
|
|
|
17326
|
|
|
|
|
|
|
sub remove { |
17327
|
0
|
|
|
0
|
|
|
my $o = shift; |
17328
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17329
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
17330
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17331
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17332
|
|
|
|
|
|
|
|
17333
|
0
|
|
|
|
|
|
$o->{ui}->progress('REMOVE ', $accountHash->shortHex, ' ', $boxLabel, ' ', $hash->shortHex, ' on ', $o->{url}); |
17334
|
0
|
|
|
|
|
|
return $o->{store}->remove($accountHash, $boxLabel, $hash, $keyPair); |
17335
|
|
|
|
|
|
|
} |
17336
|
|
|
|
|
|
|
|
17337
|
|
|
|
|
|
|
sub modify { |
17338
|
0
|
|
|
0
|
|
|
my $o = shift; |
17339
|
0
|
|
|
|
|
|
my $modifications = shift; |
17340
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17341
|
|
|
|
|
|
|
|
17342
|
0
|
|
|
|
|
|
$o->{ui}->progress('MODIFY +', scalar @{$modifications->additions}, ' -', scalar @{$modifications->removals}, ' on ', $o->{url}); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
17343
|
0
|
|
|
|
|
|
return $o->{store}->modify($modifications, $keyPair); |
17344
|
|
|
|
|
|
|
} |
17345
|
|
|
|
|
|
|
|
17346
|
|
|
|
|
|
|
# Displays a record, and tries to guess the byte interpretation |
17347
|
|
|
|
|
|
|
package CDS::UI::Record; |
17348
|
|
|
|
|
|
|
|
17349
|
|
|
|
|
|
|
sub display { |
17350
|
0
|
|
|
0
|
|
|
my $class = shift; |
17351
|
0
|
|
|
|
|
|
my $ui = shift; |
17352
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
17353
|
0
|
|
|
|
|
|
my $storeUrl = shift; |
17354
|
|
|
|
|
|
|
|
17355
|
0
|
0
|
|
|
|
|
my $o = bless { |
17356
|
|
|
|
|
|
|
ui => $ui, |
17357
|
|
|
|
|
|
|
onStore => defined $storeUrl ? $ui->gray(' on ', $storeUrl) : '', |
17358
|
|
|
|
|
|
|
}; |
17359
|
|
|
|
|
|
|
|
17360
|
0
|
|
|
|
|
|
$o->record($record, ''); |
17361
|
|
|
|
|
|
|
} |
17362
|
|
|
|
|
|
|
|
17363
|
|
|
|
|
|
|
sub record { |
17364
|
0
|
|
|
0
|
|
|
my $o = shift; |
17365
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
17366
|
0
|
|
|
|
|
|
my $context = shift; |
17367
|
|
|
|
|
|
|
|
17368
|
0
|
|
|
|
|
|
my $bytes = $record->bytes; |
17369
|
0
|
|
|
|
|
|
my $hash = $record->hash; |
17370
|
0
|
|
|
|
|
|
my @children = $record->children; |
17371
|
|
|
|
|
|
|
|
17372
|
|
|
|
|
|
|
# Try to interpret the key / value pair with a set of heuristic rules |
17373
|
|
|
|
|
|
|
my @value = |
17374
|
|
|
|
|
|
|
! length $bytes && $hash ? ($o->{ui}->gold('cds show record '), $hash->hex, $o->{onStore}) : |
17375
|
|
|
|
|
|
|
! length $bytes ? $o->{ui}->gray('empty') : |
17376
|
0
|
0
|
0
|
|
|
|
length $bytes == 32 && $hash ? ($o->{ui}->gold('cds show record '), $hash->hex, $o->{onStore}, $o->{ui}->gold(' decrypted with ', unpack('H*', $bytes))) : |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
17377
|
|
|
|
|
|
|
$context eq 'e' ? $o->hexValue($bytes) : |
17378
|
|
|
|
|
|
|
$context eq 'n' ? $o->hexValue($bytes) : |
17379
|
|
|
|
|
|
|
$context eq 'p' ? $o->hexValue($bytes) : |
17380
|
|
|
|
|
|
|
$context eq 'q' ? $o->hexValue($bytes) : |
17381
|
|
|
|
|
|
|
$context eq 'encrypted for' ? $o->hexValue($bytes) : |
17382
|
|
|
|
|
|
|
$context eq 'updated by' ? $o->hexValue($bytes) : |
17383
|
|
|
|
|
|
|
$context =~ /(^| )id( |$)/ ? $o->hexValue($bytes) : |
17384
|
|
|
|
|
|
|
$context =~ /(^| )key( |$)/ ? $o->hexValue($bytes) : |
17385
|
|
|
|
|
|
|
$context =~ /(^| )signature( |$)/ ? $o->hexValue($bytes) : |
17386
|
|
|
|
|
|
|
$context =~ /(^| )revision( |$)/ ? $o->revisionValue($bytes) : |
17387
|
|
|
|
|
|
|
$context =~ /(^| )date( |$)/ ? $o->dateValue($bytes) : |
17388
|
|
|
|
|
|
|
$context =~ /(^| )expires( |$)/ ? $o->dateValue($bytes) : |
17389
|
|
|
|
|
|
|
$o->guessValue($bytes); |
17390
|
|
|
|
|
|
|
|
17391
|
0
|
0
|
0
|
|
|
|
push @value, ' ', $o->{ui}->blue($hash->hex), $o->{onStore} if $hash && ($bytes && length $bytes != 32); |
|
|
|
0
|
|
|
|
|
17392
|
0
|
|
|
|
|
|
$o->{ui}->line(@value); |
17393
|
|
|
|
|
|
|
|
17394
|
|
|
|
|
|
|
# Children |
17395
|
0
|
|
|
|
|
|
$o->{ui}->pushIndent; |
17396
|
0
|
|
|
|
|
|
for my $child (@children) { $o->record($child, $bytes); } |
|
0
|
|
|
|
|
|
|
17397
|
0
|
|
|
|
|
|
$o->{ui}->popIndent; |
17398
|
|
|
|
|
|
|
} |
17399
|
|
|
|
|
|
|
|
17400
|
|
|
|
|
|
|
sub hexValue { |
17401
|
0
|
|
|
0
|
|
|
my $o = shift; |
17402
|
0
|
|
|
|
|
|
my $bytes = shift; |
17403
|
|
|
|
|
|
|
|
17404
|
0
|
|
|
|
|
|
my $length = length $bytes; |
17405
|
0
|
0
|
|
|
|
|
return '#'.unpack('H*', substr($bytes, 0, $length)) if $length <= 64; |
17406
|
0
|
|
|
|
|
|
return '#'.unpack('H*', substr($bytes, 0, 64)), '…', $o->{ui}->gray(' (', $length, ' bytes)'); |
17407
|
|
|
|
|
|
|
} |
17408
|
|
|
|
|
|
|
|
17409
|
|
|
|
|
|
|
sub guessValue { |
17410
|
0
|
|
|
0
|
|
|
my $o = shift; |
17411
|
0
|
|
|
|
|
|
my $bytes = shift; |
17412
|
|
|
|
|
|
|
|
17413
|
0
|
|
|
|
|
|
my $length = length $bytes; |
17414
|
0
|
0
|
|
|
|
|
my $text = $length > 64 ? substr($bytes, 0, 64).'…' : $bytes; |
17415
|
0
|
|
|
|
|
|
$text =~ s/[\x00-\x1f\x7f-\xff]/·/g; |
17416
|
0
|
|
|
|
|
|
my @value = ($text); |
17417
|
|
|
|
|
|
|
|
17418
|
0
|
0
|
|
|
|
|
if ($length <= 8) { |
17419
|
0
|
|
|
|
|
|
my $integer = CDS->integerFromBytes($bytes); |
17420
|
0
|
0
|
|
|
|
|
push @value, $o->{ui}->gray(' = ', $integer, $o->looksLikeTimestamp($integer) ? ' = '.$o->{ui}->niceDateTime($integer).' = '.$o->{ui}->niceDateTimeLocal($integer) : ''); |
17421
|
|
|
|
|
|
|
} |
17422
|
|
|
|
|
|
|
|
17423
|
0
|
0
|
0
|
|
|
|
push @value, $o->{ui}->gray(' = ', CDS->floatFromBytes($bytes)) if $length == 4 || $length == 8; |
17424
|
0
|
0
|
|
|
|
|
push @value, $o->{ui}->gray(' = ', CDS::Hash->fromBytes($bytes)->hex) if $length == 32; |
17425
|
0
|
0
|
|
|
|
|
push @value, $o->{ui}->gray(' (', length $bytes, ' bytes)') if length $bytes > 64; |
17426
|
0
|
|
|
|
|
|
return @value; |
17427
|
|
|
|
|
|
|
} |
17428
|
|
|
|
|
|
|
|
17429
|
|
|
|
|
|
|
sub dateValue { |
17430
|
0
|
|
|
0
|
|
|
my $o = shift; |
17431
|
0
|
|
|
|
|
|
my $bytes = shift; |
17432
|
|
|
|
|
|
|
|
17433
|
0
|
|
|
|
|
|
my $integer = CDS->integerFromBytes($bytes); |
17434
|
0
|
0
|
|
|
|
|
return $integer if ! $o->looksLikeTimestamp($integer); |
17435
|
0
|
|
|
|
|
|
return $o->{ui}->niceDateTime($integer), ' ', $o->{ui}->gray($o->{ui}->niceDateTimeLocal($integer)); |
17436
|
|
|
|
|
|
|
} |
17437
|
|
|
|
|
|
|
|
17438
|
|
|
|
|
|
|
sub revisionValue { |
17439
|
0
|
|
|
0
|
|
|
my $o = shift; |
17440
|
0
|
|
|
|
|
|
my $bytes = shift; |
17441
|
|
|
|
|
|
|
|
17442
|
0
|
|
|
|
|
|
my $integer = CDS->integerFromBytes($bytes); |
17443
|
0
|
0
|
|
|
|
|
return $integer if ! $o->looksLikeTimestamp($integer); |
17444
|
0
|
|
|
|
|
|
return $o->{ui}->niceDateTime($integer); |
17445
|
|
|
|
|
|
|
} |
17446
|
|
|
|
|
|
|
|
17447
|
|
|
|
|
|
|
sub looksLikeTimestamp { |
17448
|
0
|
|
|
0
|
|
|
my $o = shift; |
17449
|
0
|
|
|
|
|
|
my $integer = shift; |
17450
|
|
|
|
|
|
|
|
17451
|
0
|
|
0
|
|
|
|
return $integer > 100000000000 && $integer < 10000000000000; |
17452
|
|
|
|
|
|
|
} |
17453
|
|
|
|
|
|
|
|
17454
|
|
|
|
|
|
|
package CDS::UI::Span; |
17455
|
|
|
|
|
|
|
|
17456
|
|
|
|
|
|
|
sub new { |
17457
|
0
|
|
|
0
|
|
|
my $class = shift; |
17458
|
|
|
|
|
|
|
|
17459
|
0
|
|
|
|
|
|
return bless { |
17460
|
|
|
|
|
|
|
text => [@_], |
17461
|
|
|
|
|
|
|
}; |
17462
|
|
|
|
|
|
|
} |
17463
|
|
|
|
|
|
|
|
17464
|
|
|
|
|
|
|
sub printTo { |
17465
|
0
|
|
|
0
|
|
|
my $o = shift; |
17466
|
0
|
|
|
|
|
|
my $ui = shift; |
17467
|
0
|
|
|
|
|
|
my $parent = shift; |
17468
|
|
|
|
|
|
|
|
17469
|
0
|
0
|
|
|
|
|
if ($parent) { |
17470
|
0
|
|
0
|
|
|
|
$o->{appliedForeground} = $o->{foreground} // $parent->{appliedForeground}; |
17471
|
0
|
|
0
|
|
|
|
$o->{appliedBackground} = $o->{background} // $parent->{appliedBackground}; |
17472
|
0
|
|
0
|
|
|
|
$o->{appliedBold} = $o->{bold} // $parent->{appliedBold} // 0; |
|
|
|
0
|
|
|
|
|
17473
|
0
|
|
0
|
|
|
|
$o->{appliedUnderlined} = $o->{underlined} // $parent->{appliedUnderlined} // 0; |
|
|
|
0
|
|
|
|
|
17474
|
|
|
|
|
|
|
} else { |
17475
|
0
|
|
|
|
|
|
$o->{appliedForeground} = $o->{foreground}; |
17476
|
0
|
|
|
|
|
|
$o->{appliedBackground} = $o->{background}; |
17477
|
0
|
|
0
|
|
|
|
$o->{appliedBold} = $o->{bold} // 0; |
17478
|
0
|
|
0
|
|
|
|
$o->{appliedUnderlined} = $o->{underlined} // 0; |
17479
|
|
|
|
|
|
|
} |
17480
|
|
|
|
|
|
|
|
17481
|
0
|
|
|
|
|
|
my $style = chr(0x1b).'[0'; |
17482
|
0
|
0
|
|
|
|
|
$style .= ';1' if $o->{appliedBold}; |
17483
|
0
|
0
|
|
|
|
|
$style .= ';4' if $o->{appliedUnderlined}; |
17484
|
0
|
0
|
|
|
|
|
$style .= ';38;5;'.$o->{appliedForeground} if defined $o->{appliedForeground}; |
17485
|
0
|
0
|
|
|
|
|
$style .= ';48;5;'.$o->{appliedBackground} if defined $o->{appliedBackground}; |
17486
|
0
|
|
|
|
|
|
$style .= 'm'; |
17487
|
|
|
|
|
|
|
|
17488
|
0
|
|
|
|
|
|
my $needStyle = 1; |
17489
|
0
|
|
|
|
|
|
for my $child (@{$o->{text}}) { |
|
0
|
|
|
|
|
|
|
17490
|
0
|
|
|
|
|
|
my $ref = ref $child; |
17491
|
0
|
0
|
|
|
|
|
if ($ref eq 'CDS::UI::Span') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
17492
|
0
|
|
|
|
|
|
$child->printTo($ui, $o); |
17493
|
0
|
|
|
|
|
|
$needStyle = 1; |
17494
|
0
|
|
|
|
|
|
next; |
17495
|
|
|
|
|
|
|
} elsif (length $ref) { |
17496
|
0
|
|
|
|
|
|
warn 'Printing REF'; |
17497
|
0
|
|
|
|
|
|
$child = $ref; |
17498
|
|
|
|
|
|
|
} elsif (! defined $child) { |
17499
|
0
|
|
|
|
|
|
warn 'Printing UNDEF'; |
17500
|
0
|
|
|
|
|
|
$child = 'UNDEF'; |
17501
|
|
|
|
|
|
|
} |
17502
|
|
|
|
|
|
|
|
17503
|
0
|
0
|
|
|
|
|
if ($needStyle) { |
17504
|
0
|
|
|
|
|
|
$ui->print($style); |
17505
|
0
|
|
|
|
|
|
$needStyle = 0; |
17506
|
|
|
|
|
|
|
} |
17507
|
|
|
|
|
|
|
|
17508
|
0
|
|
|
|
|
|
$ui->print($child); |
17509
|
|
|
|
|
|
|
} |
17510
|
|
|
|
|
|
|
} |
17511
|
|
|
|
|
|
|
|
17512
|
|
|
|
|
|
|
sub wordWrap { |
17513
|
0
|
|
|
0
|
|
|
my $o = shift; |
17514
|
0
|
|
|
|
|
|
my $state = shift; |
17515
|
|
|
|
|
|
|
|
17516
|
0
|
|
|
|
|
|
my $index = -1; |
17517
|
0
|
|
|
|
|
|
for my $child (@{$o->{text}}) { |
|
0
|
|
|
|
|
|
|
17518
|
0
|
|
|
|
|
|
$index += 1; |
17519
|
|
|
|
|
|
|
|
17520
|
0
|
0
|
|
|
|
|
next if ! defined $child; |
17521
|
|
|
|
|
|
|
|
17522
|
0
|
|
|
|
|
|
my $ref = ref $child; |
17523
|
0
|
0
|
|
|
|
|
if ($ref eq 'CDS::UI::Span') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
17524
|
0
|
|
|
|
|
|
$child->wordWrap($state); |
17525
|
0
|
|
|
|
|
|
next; |
17526
|
|
|
|
|
|
|
} elsif (length $ref) { |
17527
|
0
|
|
|
|
|
|
warn 'Printing REF'; |
17528
|
0
|
|
|
|
|
|
$child = $ref; |
17529
|
|
|
|
|
|
|
} elsif (! defined $child) { |
17530
|
0
|
|
|
|
|
|
warn 'Printing UNDEF'; |
17531
|
0
|
|
|
|
|
|
$child = 'UNDEF'; |
17532
|
|
|
|
|
|
|
} |
17533
|
|
|
|
|
|
|
|
17534
|
0
|
|
|
|
|
|
my $position = -1; |
17535
|
0
|
|
|
|
|
|
for my $char (split //, $child) { |
17536
|
0
|
|
|
|
|
|
$position += 1; |
17537
|
0
|
|
|
|
|
|
$state->{lineLength} += 1; |
17538
|
0
|
0
|
0
|
|
|
|
if ($char eq ' ' || $char eq "\t") { |
|
|
0
|
0
|
|
|
|
|
17539
|
0
|
|
|
|
|
|
$state->{wrapSpan} = $o; |
17540
|
0
|
|
|
|
|
|
$state->{wrapIndex} = $index; |
17541
|
0
|
|
|
|
|
|
$state->{wrapPosition} = $position; |
17542
|
0
|
|
|
|
|
|
$state->{wrapReturn} = $state->{lineLength}; |
17543
|
|
|
|
|
|
|
} elsif ($state->{wrapSpan} && $state->{lineLength} > $state->{maxLength}) { |
17544
|
0
|
|
|
|
|
|
my $text = $state->{wrapSpan}->{text}->[$state->{wrapIndex}]; |
17545
|
0
|
|
|
|
|
|
$text = substr($text, 0, $state->{wrapPosition})."\n".$state->{indent}.substr($text, $state->{wrapPosition} + 1); |
17546
|
0
|
|
|
|
|
|
$state->{wrapSpan}->{text}->[$state->{wrapIndex}] = $text; |
17547
|
0
|
|
|
|
|
|
$state->{lineLength} -= $state->{wrapReturn}; |
17548
|
0
|
0
|
0
|
|
|
|
$position += length $state->{indent} if $state->{wrapSpan} == $o && $state->{wrapIndex} == $index; |
17549
|
0
|
|
|
|
|
|
$state->{wrapSpan} = undef; |
17550
|
|
|
|
|
|
|
} |
17551
|
|
|
|
|
|
|
} |
17552
|
|
|
|
|
|
|
} |
17553
|
|
|
|
|
|
|
} |
17554
|
|
|
|
|
|
|
|
17555
|
|
|
|
|
|
|
package CDS::UnionList; |
17556
|
|
|
|
|
|
|
|
17557
|
|
|
|
|
|
|
sub new { |
17558
|
0
|
|
|
0
|
|
|
my $class = shift; |
17559
|
0
|
|
|
|
|
|
my $privateRoot = shift; |
17560
|
0
|
|
|
|
|
|
my $label = shift; |
17561
|
|
|
|
|
|
|
|
17562
|
0
|
|
|
|
|
|
my $o = bless { |
17563
|
|
|
|
|
|
|
privateRoot => $privateRoot, |
17564
|
|
|
|
|
|
|
label => $label, |
17565
|
|
|
|
|
|
|
unsaved => CDS::Unsaved->new($privateRoot->unsaved), |
17566
|
|
|
|
|
|
|
items => {}, |
17567
|
|
|
|
|
|
|
parts => {}, |
17568
|
|
|
|
|
|
|
hasPartsToMerge => 0, |
17569
|
|
|
|
|
|
|
}, $class; |
17570
|
|
|
|
|
|
|
|
17571
|
0
|
|
|
|
|
|
$o->{unused} = CDS::UnionList::Part->new; |
17572
|
0
|
|
|
|
|
|
$o->{changes} = CDS::UnionList::Part->new; |
17573
|
0
|
|
|
|
|
|
$privateRoot->addDataHandler($label, $o); |
17574
|
0
|
|
|
|
|
|
return $o; |
17575
|
|
|
|
|
|
|
} |
17576
|
|
|
|
|
|
|
|
17577
|
0
|
|
|
0
|
|
|
sub privateRoot { shift->{privateRoot} } |
17578
|
0
|
|
|
0
|
|
|
sub unsaved { shift->{unsaved} } |
17579
|
|
|
|
|
|
|
sub items { |
17580
|
0
|
|
|
0
|
|
|
my $o = shift; |
17581
|
0
|
|
|
|
|
|
values %{$o->{items}} } |
|
0
|
|
|
|
|
|
|
17582
|
|
|
|
|
|
|
sub parts { |
17583
|
0
|
|
|
0
|
|
|
my $o = shift; |
17584
|
0
|
|
|
|
|
|
values %{$o->{parts}} } |
|
0
|
|
|
|
|
|
|
17585
|
|
|
|
|
|
|
|
17586
|
|
|
|
|
|
|
sub get { |
17587
|
0
|
|
|
0
|
|
|
my $o = shift; |
17588
|
0
|
|
|
|
|
|
my $id = shift; |
17589
|
0
|
|
|
|
|
|
$o->{items}->{$id} } |
17590
|
|
|
|
|
|
|
|
17591
|
|
|
|
|
|
|
sub getOrCreate { |
17592
|
0
|
|
|
0
|
|
|
my $o = shift; |
17593
|
0
|
|
|
|
|
|
my $id = shift; |
17594
|
|
|
|
|
|
|
|
17595
|
0
|
|
|
|
|
|
my $item = $o->{items}->{$id}; |
17596
|
0
|
0
|
|
|
|
|
return $item if $item; |
17597
|
0
|
|
|
|
|
|
my $newItem = $o->createItem($id); |
17598
|
0
|
|
|
|
|
|
$o->{items}->{$id} = $newItem; |
17599
|
0
|
|
|
|
|
|
return $newItem; |
17600
|
|
|
|
|
|
|
} |
17601
|
|
|
|
|
|
|
|
17602
|
|
|
|
|
|
|
# abstract sub createItem($o, $id) |
17603
|
|
|
|
|
|
|
# abstract sub forgetObsoleteItems($o) |
17604
|
|
|
|
|
|
|
|
17605
|
|
|
|
|
|
|
sub forget { |
17606
|
0
|
|
|
0
|
|
|
my $o = shift; |
17607
|
0
|
|
|
|
|
|
my $id = shift; |
17608
|
|
|
|
|
|
|
|
17609
|
0
|
|
0
|
|
|
|
my $item = $o->{items}->{$id} // return; |
17610
|
0
|
|
|
|
|
|
$item->{part}->{count} -= 1; |
17611
|
0
|
|
|
|
|
|
delete $o->{items}->{$id}; |
17612
|
|
|
|
|
|
|
} |
17613
|
|
|
|
|
|
|
|
17614
|
|
|
|
|
|
|
sub forgetItem { |
17615
|
0
|
|
|
0
|
|
|
my $o = shift; |
17616
|
0
|
|
|
|
|
|
my $item = shift; |
17617
|
|
|
|
|
|
|
|
17618
|
0
|
|
|
|
|
|
$item->{part}->{count} -= 1; |
17619
|
0
|
|
|
|
|
|
delete $o->{items}->{$item->id}; |
17620
|
|
|
|
|
|
|
} |
17621
|
|
|
|
|
|
|
|
17622
|
|
|
|
|
|
|
# *** MergeableData interface |
17623
|
|
|
|
|
|
|
|
17624
|
|
|
|
|
|
|
sub addDataTo { |
17625
|
0
|
|
|
0
|
|
|
my $o = shift; |
17626
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
17627
|
|
|
|
|
|
|
|
17628
|
0
|
|
|
|
|
|
for my $part (sort { $a->{hashAndKey}->hash->bytes cmp $b->{hashAndKey}->hash->bytes } values %{$o->{parts}}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
17629
|
0
|
|
|
|
|
|
$record->addHashAndKey($part->{hashAndKey}); |
17630
|
|
|
|
|
|
|
} |
17631
|
|
|
|
|
|
|
} |
17632
|
|
|
|
|
|
|
|
17633
|
|
|
|
|
|
|
sub mergeData { |
17634
|
0
|
|
|
0
|
|
|
my $o = shift; |
17635
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
17636
|
|
|
|
|
|
|
|
17637
|
0
|
|
|
|
|
|
my @hashesAndKeys; |
17638
|
0
|
|
|
|
|
|
for my $child ($record->children) { |
17639
|
0
|
|
0
|
|
|
|
push @hashesAndKeys, $child->asHashAndKey // next; |
17640
|
|
|
|
|
|
|
} |
17641
|
|
|
|
|
|
|
|
17642
|
0
|
|
|
|
|
|
$o->merge(@hashesAndKeys); |
17643
|
|
|
|
|
|
|
} |
17644
|
|
|
|
|
|
|
|
17645
|
|
|
|
|
|
|
sub mergeExternalData { |
17646
|
0
|
|
|
0
|
|
|
my $o = shift; |
17647
|
0
|
|
|
|
|
|
my $store = shift; |
17648
|
0
|
0
|
0
|
|
|
|
my $record = shift; die 'wrong type '.ref($record).' for $record' if defined $record && ref $record ne 'CDS::Record'; |
|
0
|
|
|
|
|
|
|
17649
|
0
|
0
|
0
|
|
|
|
my $source = shift; die 'wrong type '.ref($source).' for $source' if defined $source && ref $source ne 'CDS::Source'; |
|
0
|
|
|
|
|
|
|
17650
|
|
|
|
|
|
|
|
17651
|
0
|
|
|
|
|
|
my @hashes; |
17652
|
|
|
|
|
|
|
my @hashesAndKeys; |
17653
|
0
|
|
|
|
|
|
for my $child ($record->children) { |
17654
|
0
|
|
0
|
|
|
|
my $hashAndKey = $child->asHashAndKey // next; |
17655
|
0
|
0
|
|
|
|
|
next if $o->{parts}->{$hashAndKey->hash->bytes}; |
17656
|
0
|
|
|
|
|
|
push @hashes, $hashAndKey->hash; |
17657
|
0
|
|
|
|
|
|
push @hashesAndKeys, $hashAndKey; |
17658
|
|
|
|
|
|
|
} |
17659
|
|
|
|
|
|
|
|
17660
|
0
|
|
|
|
|
|
my $keyPair = $o->{privateRoot}->privateBoxReader->keyPair; |
17661
|
0
|
|
|
|
|
|
my ($missing, $transferStore, $storeError) = $keyPair->transfer([@hashes], $store, $o->{privateRoot}->unsaved); |
17662
|
0
|
0
|
|
|
|
|
return if defined $storeError; |
17663
|
0
|
0
|
|
|
|
|
return if $missing; |
17664
|
|
|
|
|
|
|
|
17665
|
0
|
0
|
|
|
|
|
if ($source) { |
17666
|
0
|
|
|
|
|
|
$source->keep; |
17667
|
0
|
|
|
|
|
|
$o->{privateRoot}->unsaved->state->addMergedSource($source); |
17668
|
|
|
|
|
|
|
} |
17669
|
|
|
|
|
|
|
|
17670
|
0
|
|
|
|
|
|
$o->merge(@hashesAndKeys); |
17671
|
0
|
|
|
|
|
|
return 1; |
17672
|
|
|
|
|
|
|
} |
17673
|
|
|
|
|
|
|
|
17674
|
|
|
|
|
|
|
sub merge { |
17675
|
0
|
|
|
0
|
|
|
my $o = shift; |
17676
|
|
|
|
|
|
|
|
17677
|
0
|
|
|
|
|
|
for my $hashAndKey (@_) { |
17678
|
0
|
0
|
|
|
|
|
next if ! $hashAndKey; |
17679
|
0
|
0
|
|
|
|
|
next if $o->{parts}->{$hashAndKey->hash->bytes}; |
17680
|
0
|
|
|
|
|
|
my $part = CDS::UnionList::Part->new; |
17681
|
0
|
|
|
|
|
|
$part->{hashAndKey} = $hashAndKey; |
17682
|
0
|
|
|
|
|
|
$o->{parts}->{$hashAndKey->hash->bytes} = $part; |
17683
|
0
|
|
|
|
|
|
$o->{hasPartsToMerge} = 1; |
17684
|
|
|
|
|
|
|
} |
17685
|
|
|
|
|
|
|
} |
17686
|
|
|
|
|
|
|
|
17687
|
|
|
|
|
|
|
# *** Reading |
17688
|
|
|
|
|
|
|
|
17689
|
|
|
|
|
|
|
sub read { |
17690
|
0
|
|
|
0
|
|
|
my $o = shift; |
17691
|
|
|
|
|
|
|
|
17692
|
0
|
0
|
|
|
|
|
return 1 if ! $o->{hasPartsToMerge}; |
17693
|
|
|
|
|
|
|
|
17694
|
|
|
|
|
|
|
# Load the parts |
17695
|
0
|
|
|
|
|
|
for my $part (values %{$o->{parts}}) { |
|
0
|
|
|
|
|
|
|
17696
|
0
|
0
|
|
|
|
|
next if $part->{isMerged}; |
17697
|
0
|
0
|
|
|
|
|
next if $part->{loadedRecord}; |
17698
|
|
|
|
|
|
|
|
17699
|
0
|
|
|
|
|
|
my ($record, $object, $invalidReason, $storeError) = $o->{privateRoot}->privateBoxReader->keyPair->getAndDecryptRecord($part->{hashAndKey}, $o->{privateRoot}->unsaved); |
17700
|
0
|
0
|
|
|
|
|
return if defined $storeError; |
17701
|
|
|
|
|
|
|
|
17702
|
0
|
0
|
|
|
|
|
delete $o->{parts}->{$part->{hashAndKey}->hash->bytes} if defined $invalidReason; |
17703
|
0
|
|
|
|
|
|
$part->{loadedRecord} = $record; |
17704
|
|
|
|
|
|
|
} |
17705
|
|
|
|
|
|
|
|
17706
|
|
|
|
|
|
|
# Merge the loaded parts |
17707
|
0
|
|
|
|
|
|
for my $part (values %{$o->{parts}}) { |
|
0
|
|
|
|
|
|
|
17708
|
0
|
0
|
|
|
|
|
next if $part->{isMerged}; |
17709
|
0
|
0
|
|
|
|
|
next if ! $part->{loadedRecord}; |
17710
|
|
|
|
|
|
|
|
17711
|
|
|
|
|
|
|
# Merge |
17712
|
0
|
|
|
|
|
|
for my $child ($part->{loadedRecord}->children) { |
17713
|
0
|
|
|
|
|
|
$o->mergeRecord($part, $child); |
17714
|
|
|
|
|
|
|
} |
17715
|
|
|
|
|
|
|
|
17716
|
0
|
|
|
|
|
|
delete $part->{loadedRecord}; |
17717
|
0
|
|
|
|
|
|
$part->{isMerged} = 1; |
17718
|
|
|
|
|
|
|
} |
17719
|
|
|
|
|
|
|
|
17720
|
0
|
|
|
|
|
|
$o->{hasPartsToMerge} = 0; |
17721
|
0
|
|
|
|
|
|
return 1; |
17722
|
|
|
|
|
|
|
} |
17723
|
|
|
|
|
|
|
|
17724
|
|
|
|
|
|
|
# abstract sub mergeRecord($o, $part, $record) |
17725
|
|
|
|
|
|
|
|
17726
|
|
|
|
|
|
|
# *** Saving |
17727
|
|
|
|
|
|
|
|
17728
|
|
|
|
|
|
|
sub hasChanges { |
17729
|
0
|
|
|
0
|
|
|
my $o = shift; |
17730
|
0
|
|
|
|
|
|
$o->{changes}->{count} > 0 } |
17731
|
|
|
|
|
|
|
|
17732
|
|
|
|
|
|
|
sub save { |
17733
|
0
|
|
|
0
|
|
|
my $o = shift; |
17734
|
|
|
|
|
|
|
|
17735
|
0
|
|
|
|
|
|
$o->forgetObsoleteItems; |
17736
|
0
|
|
|
|
|
|
$o->{unsaved}->startSaving; |
17737
|
|
|
|
|
|
|
|
17738
|
0
|
0
|
|
|
|
|
if ($o->{changes}->{count}) { |
17739
|
|
|
|
|
|
|
# Take the changes |
17740
|
0
|
|
|
|
|
|
my $newPart = $o->{changes}; |
17741
|
0
|
|
|
|
|
|
$o->{changes} = CDS::UnionList::Part->new; |
17742
|
|
|
|
|
|
|
|
17743
|
|
|
|
|
|
|
# Add all changes |
17744
|
0
|
|
|
|
|
|
my $record = CDS::Record->new; |
17745
|
0
|
|
|
|
|
|
for my $item (values %{$o->{items}}) { |
|
0
|
|
|
|
|
|
|
17746
|
0
|
0
|
|
|
|
|
next if $item->{part} != $newPart; |
17747
|
0
|
|
|
|
|
|
$item->addToRecord($record); |
17748
|
|
|
|
|
|
|
} |
17749
|
|
|
|
|
|
|
|
17750
|
|
|
|
|
|
|
# Select all parts smaller than 2 * count elements |
17751
|
0
|
|
|
|
|
|
my $count = $newPart->{count}; |
17752
|
0
|
|
|
|
|
|
while (1) { |
17753
|
0
|
|
|
|
|
|
my $addedPart = 0; |
17754
|
0
|
|
|
|
|
|
for my $part (values %{$o->{parts}}) { |
|
0
|
|
|
|
|
|
|
17755
|
0
|
0
|
0
|
|
|
|
next if ! $part->{isMerged} || $part->{selected} || $part->{count} >= $count * 2; |
|
|
|
0
|
|
|
|
|
17756
|
0
|
|
|
|
|
|
$count += $part->{count}; |
17757
|
0
|
|
|
|
|
|
$part->{selected} = 1; |
17758
|
0
|
|
|
|
|
|
$addedPart = 1; |
17759
|
|
|
|
|
|
|
} |
17760
|
|
|
|
|
|
|
|
17761
|
0
|
0
|
|
|
|
|
last if ! $addedPart; |
17762
|
|
|
|
|
|
|
} |
17763
|
|
|
|
|
|
|
|
17764
|
|
|
|
|
|
|
# Include the selected items |
17765
|
0
|
|
|
|
|
|
for my $item (values %{$o->{items}}) { |
|
0
|
|
|
|
|
|
|
17766
|
0
|
0
|
|
|
|
|
next if ! $item->{part}->{selected}; |
17767
|
0
|
|
|
|
|
|
$item->setPart($newPart); |
17768
|
0
|
|
|
|
|
|
$item->addToRecord($record); |
17769
|
|
|
|
|
|
|
} |
17770
|
|
|
|
|
|
|
|
17771
|
|
|
|
|
|
|
# Serialize the new part |
17772
|
0
|
|
|
|
|
|
my $key = CDS->randomKey; |
17773
|
0
|
|
|
|
|
|
my $newObject = $record->toObject->crypt($key); |
17774
|
0
|
|
|
|
|
|
my $newHash = $newObject->calculateHash; |
17775
|
0
|
|
|
|
|
|
$newPart->{hashAndKey} = CDS::HashAndKey->new($newHash, $key); |
17776
|
0
|
|
|
|
|
|
$newPart->{isMerged} = 1; |
17777
|
0
|
|
|
|
|
|
$o->{parts}->{$newHash->bytes} = $newPart; |
17778
|
0
|
|
|
|
|
|
$o->{privateRoot}->unsaved->state->addObject($newHash, $newObject); |
17779
|
0
|
|
|
|
|
|
$o->{privateRoot}->dataChanged; |
17780
|
|
|
|
|
|
|
} |
17781
|
|
|
|
|
|
|
|
17782
|
|
|
|
|
|
|
# Remove obsolete parts |
17783
|
0
|
|
|
|
|
|
for my $part (values %{$o->{parts}}) { |
|
0
|
|
|
|
|
|
|
17784
|
0
|
0
|
|
|
|
|
next if ! $part->{isMerged}; |
17785
|
0
|
0
|
|
|
|
|
next if $part->{count}; |
17786
|
0
|
|
|
|
|
|
delete $o->{parts}->{$part->{hashAndKey}->hash->bytes}; |
17787
|
0
|
|
|
|
|
|
$o->{privateRoot}->dataChanged; |
17788
|
|
|
|
|
|
|
} |
17789
|
|
|
|
|
|
|
|
17790
|
|
|
|
|
|
|
# Propagate the unsaved state |
17791
|
0
|
|
|
|
|
|
$o->{privateRoot}->unsaved->state->merge($o->{unsaved}->savingState); |
17792
|
0
|
|
|
|
|
|
$o->{unsaved}->savingDone; |
17793
|
0
|
|
|
|
|
|
return 1; |
17794
|
|
|
|
|
|
|
} |
17795
|
|
|
|
|
|
|
|
17796
|
|
|
|
|
|
|
package CDS::UnionList::Item; |
17797
|
|
|
|
|
|
|
|
17798
|
|
|
|
|
|
|
sub new { |
17799
|
0
|
|
|
0
|
|
|
my $class = shift; |
17800
|
0
|
|
|
|
|
|
my $unionList = shift; |
17801
|
0
|
|
|
|
|
|
my $id = shift; |
17802
|
|
|
|
|
|
|
|
17803
|
0
|
|
|
|
|
|
$unionList->{unused}->{count} += 1; |
17804
|
|
|
|
|
|
|
return bless { |
17805
|
|
|
|
|
|
|
unionList => $unionList, |
17806
|
|
|
|
|
|
|
id => $id, |
17807
|
|
|
|
|
|
|
part => $unionList->{unused}, |
17808
|
0
|
|
|
|
|
|
}, $class; |
17809
|
|
|
|
|
|
|
} |
17810
|
|
|
|
|
|
|
|
17811
|
0
|
|
|
0
|
|
|
sub unionList { shift->{unionList} } |
17812
|
0
|
|
|
0
|
|
|
sub id { shift->{id} } |
17813
|
|
|
|
|
|
|
|
17814
|
|
|
|
|
|
|
sub setPart { |
17815
|
0
|
|
|
0
|
|
|
my $o = shift; |
17816
|
0
|
|
|
|
|
|
my $part = shift; |
17817
|
|
|
|
|
|
|
|
17818
|
0
|
|
|
|
|
|
$o->{part}->{count} -= 1; |
17819
|
0
|
|
|
|
|
|
$o->{part} = $part; |
17820
|
0
|
|
|
|
|
|
$o->{part}->{count} += 1; |
17821
|
|
|
|
|
|
|
} |
17822
|
|
|
|
|
|
|
|
17823
|
|
|
|
|
|
|
# abstract sub addToRecord($o, $record) |
17824
|
|
|
|
|
|
|
|
17825
|
|
|
|
|
|
|
package CDS::UnionList::Part; |
17826
|
|
|
|
|
|
|
|
17827
|
|
|
|
|
|
|
sub new { |
17828
|
0
|
|
|
0
|
|
|
my $class = shift; |
17829
|
|
|
|
|
|
|
|
17830
|
0
|
|
|
|
|
|
return bless { |
17831
|
|
|
|
|
|
|
isMerged => 0, |
17832
|
|
|
|
|
|
|
hashAndKey => undef, |
17833
|
|
|
|
|
|
|
size => 0, |
17834
|
|
|
|
|
|
|
count => 0, |
17835
|
|
|
|
|
|
|
selected => 0, |
17836
|
|
|
|
|
|
|
}; |
17837
|
|
|
|
|
|
|
} |
17838
|
|
|
|
|
|
|
|
17839
|
|
|
|
|
|
|
package CDS::Unsaved; |
17840
|
|
|
|
|
|
|
|
17841
|
1
|
|
|
1
|
|
4069
|
use parent -norequire, 'CDS::Store'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
17842
|
|
|
|
|
|
|
|
17843
|
|
|
|
|
|
|
sub new { |
17844
|
0
|
|
|
0
|
|
|
my $class = shift; |
17845
|
0
|
|
|
|
|
|
my $store = shift; |
17846
|
|
|
|
|
|
|
|
17847
|
0
|
|
|
|
|
|
return bless { |
17848
|
|
|
|
|
|
|
state => CDS::Unsaved::State->new, |
17849
|
|
|
|
|
|
|
savingState => undef, |
17850
|
|
|
|
|
|
|
store => $store, |
17851
|
|
|
|
|
|
|
}; |
17852
|
|
|
|
|
|
|
} |
17853
|
|
|
|
|
|
|
|
17854
|
0
|
|
|
0
|
|
|
sub state { shift->{state} } |
17855
|
0
|
|
|
0
|
|
|
sub savingState { shift->{savingState} } |
17856
|
|
|
|
|
|
|
|
17857
|
|
|
|
|
|
|
# *** Saving, state propagation |
17858
|
|
|
|
|
|
|
|
17859
|
|
|
|
|
|
|
sub isSaving { |
17860
|
0
|
|
|
0
|
|
|
my $o = shift; |
17861
|
0
|
|
|
|
|
|
defined $o->{savingState} } |
17862
|
|
|
|
|
|
|
|
17863
|
|
|
|
|
|
|
sub startSaving { |
17864
|
0
|
|
|
0
|
|
|
my $o = shift; |
17865
|
|
|
|
|
|
|
|
17866
|
0
|
0
|
|
|
|
|
die 'Start saving, but already saving' if $o->{savingState}; |
17867
|
0
|
|
|
|
|
|
$o->{savingState} = $o->{state}; |
17868
|
0
|
|
|
|
|
|
$o->{state} = CDS::Unsaved::State->new; |
17869
|
|
|
|
|
|
|
} |
17870
|
|
|
|
|
|
|
|
17871
|
|
|
|
|
|
|
sub savingDone { |
17872
|
0
|
|
|
0
|
|
|
my $o = shift; |
17873
|
|
|
|
|
|
|
|
17874
|
0
|
0
|
|
|
|
|
die 'Not in saving state' if ! $o->{savingState}; |
17875
|
0
|
|
|
|
|
|
$o->{savingState} = undef; |
17876
|
|
|
|
|
|
|
} |
17877
|
|
|
|
|
|
|
|
17878
|
|
|
|
|
|
|
sub savingFailed { |
17879
|
0
|
|
|
0
|
|
|
my $o = shift; |
17880
|
|
|
|
|
|
|
|
17881
|
0
|
0
|
|
|
|
|
die 'Not in saving state' if ! $o->{savingState}; |
17882
|
0
|
|
|
|
|
|
$o->{state}->merge($o->{savingState}); |
17883
|
0
|
|
|
|
|
|
$o->{savingState} = undef; |
17884
|
|
|
|
|
|
|
} |
17885
|
|
|
|
|
|
|
|
17886
|
|
|
|
|
|
|
# *** Store interface |
17887
|
|
|
|
|
|
|
|
17888
|
|
|
|
|
|
|
sub id { |
17889
|
0
|
|
|
0
|
|
|
my $o = shift; |
17890
|
0
|
|
|
|
|
|
'Unsaved'."\n".unpack('H*', CDS->randomBytes(16))."\n".$o->{store}->id } |
17891
|
|
|
|
|
|
|
|
17892
|
|
|
|
|
|
|
sub get { |
17893
|
0
|
|
|
0
|
|
|
my $o = shift; |
17894
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17895
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17896
|
|
|
|
|
|
|
|
17897
|
0
|
|
|
|
|
|
my $stateObject = $o->{state}->{objects}->{$hash->bytes}; |
17898
|
0
|
0
|
|
|
|
|
return $stateObject->{object} if $stateObject; |
17899
|
|
|
|
|
|
|
|
17900
|
0
|
0
|
|
|
|
|
if ($o->{savingState}) { |
17901
|
0
|
|
|
|
|
|
my $savingStateObject = $o->{savingState}->{objects}->{$hash->bytes}; |
17902
|
0
|
0
|
|
|
|
|
return $savingStateObject->{object} if $savingStateObject; |
17903
|
|
|
|
|
|
|
} |
17904
|
|
|
|
|
|
|
|
17905
|
0
|
|
|
|
|
|
return $o->{store}->get($hash, $keyPair); |
17906
|
|
|
|
|
|
|
} |
17907
|
|
|
|
|
|
|
|
17908
|
|
|
|
|
|
|
sub book { |
17909
|
0
|
|
|
0
|
|
|
my $o = shift; |
17910
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17911
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17912
|
|
|
|
|
|
|
|
17913
|
0
|
|
|
|
|
|
return $o->{store}->book($hash, $keyPair); |
17914
|
|
|
|
|
|
|
} |
17915
|
|
|
|
|
|
|
|
17916
|
|
|
|
|
|
|
sub put { |
17917
|
0
|
|
|
0
|
|
|
my $o = shift; |
17918
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17919
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
17920
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17921
|
|
|
|
|
|
|
|
17922
|
0
|
|
|
|
|
|
return $o->{store}->put($hash, $object, $keyPair); |
17923
|
|
|
|
|
|
|
} |
17924
|
|
|
|
|
|
|
|
17925
|
|
|
|
|
|
|
sub list { |
17926
|
0
|
|
|
0
|
|
|
my $o = shift; |
17927
|
0
|
0
|
0
|
|
|
|
my $accountHash = shift; die 'wrong type '.ref($accountHash).' for $accountHash' if defined $accountHash && ref $accountHash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17928
|
0
|
|
|
|
|
|
my $boxLabel = shift; |
17929
|
0
|
|
|
|
|
|
my $timeout = shift; |
17930
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17931
|
|
|
|
|
|
|
|
17932
|
0
|
|
|
|
|
|
return $o->{store}->list($accountHash, $boxLabel, $timeout, $keyPair); |
17933
|
|
|
|
|
|
|
} |
17934
|
|
|
|
|
|
|
|
17935
|
|
|
|
|
|
|
sub modify { |
17936
|
0
|
|
|
0
|
|
|
my $o = shift; |
17937
|
0
|
|
|
|
|
|
my $additions = shift; |
17938
|
0
|
|
|
|
|
|
my $removals = shift; |
17939
|
0
|
0
|
0
|
|
|
|
my $keyPair = shift; die 'wrong type '.ref($keyPair).' for $keyPair' if defined $keyPair && ref $keyPair ne 'CDS::KeyPair'; |
|
0
|
|
|
|
|
|
|
17940
|
|
|
|
|
|
|
|
17941
|
0
|
|
|
|
|
|
return $o->{store}->modify($additions, $removals, $keyPair); |
17942
|
|
|
|
|
|
|
} |
17943
|
|
|
|
|
|
|
|
17944
|
|
|
|
|
|
|
package CDS::Unsaved::State; |
17945
|
|
|
|
|
|
|
|
17946
|
|
|
|
|
|
|
sub new { |
17947
|
0
|
|
|
0
|
|
|
my $class = shift; |
17948
|
|
|
|
|
|
|
|
17949
|
0
|
|
|
|
|
|
return bless { |
17950
|
|
|
|
|
|
|
objects => {}, |
17951
|
|
|
|
|
|
|
mergedSources => [], |
17952
|
|
|
|
|
|
|
dataSavedHandlers => [], |
17953
|
|
|
|
|
|
|
}; |
17954
|
|
|
|
|
|
|
} |
17955
|
|
|
|
|
|
|
|
17956
|
0
|
|
|
0
|
|
|
sub objects { shift->{objects} } |
17957
|
|
|
|
|
|
|
sub mergedSources { |
17958
|
0
|
|
|
0
|
|
|
my $o = shift; |
17959
|
0
|
|
|
|
|
|
@{$o->{mergedSources}} } |
|
0
|
|
|
|
|
|
|
17960
|
|
|
|
|
|
|
sub dataSavedHandlers { |
17961
|
0
|
|
|
0
|
|
|
my $o = shift; |
17962
|
0
|
|
|
|
|
|
@{$o->{dataSavedHandlers}} } |
|
0
|
|
|
|
|
|
|
17963
|
|
|
|
|
|
|
|
17964
|
|
|
|
|
|
|
sub addObject { |
17965
|
0
|
|
|
0
|
|
|
my $o = shift; |
17966
|
0
|
0
|
0
|
|
|
|
my $hash = shift; die 'wrong type '.ref($hash).' for $hash' if defined $hash && ref $hash ne 'CDS::Hash'; |
|
0
|
|
|
|
|
|
|
17967
|
0
|
0
|
0
|
|
|
|
my $object = shift; die 'wrong type '.ref($object).' for $object' if defined $object && ref $object ne 'CDS::Object'; |
|
0
|
|
|
|
|
|
|
17968
|
|
|
|
|
|
|
|
17969
|
0
|
|
|
|
|
|
$o->{objects}->{$hash->bytes} = {hash => $hash, object => $object}; |
17970
|
|
|
|
|
|
|
} |
17971
|
|
|
|
|
|
|
|
17972
|
|
|
|
|
|
|
sub addMergedSource { |
17973
|
0
|
|
|
0
|
|
|
my $o = shift; |
17974
|
|
|
|
|
|
|
|
17975
|
0
|
|
|
|
|
|
push @{$o->{mergedSources}}, @_; |
|
0
|
|
|
|
|
|
|
17976
|
|
|
|
|
|
|
} |
17977
|
|
|
|
|
|
|
|
17978
|
|
|
|
|
|
|
sub addDataSavedHandler { |
17979
|
0
|
|
|
0
|
|
|
my $o = shift; |
17980
|
|
|
|
|
|
|
|
17981
|
0
|
|
|
|
|
|
push @{$o->{dataSavedHandlers}}, @_; |
|
0
|
|
|
|
|
|
|
17982
|
|
|
|
|
|
|
} |
17983
|
|
|
|
|
|
|
|
17984
|
|
|
|
|
|
|
sub merge { |
17985
|
0
|
|
|
0
|
|
|
my $o = shift; |
17986
|
0
|
|
|
|
|
|
my $state = shift; |
17987
|
|
|
|
|
|
|
|
17988
|
0
|
|
|
|
|
|
for my $key (keys %{$state->{objects}}) { |
|
0
|
|
|
|
|
|
|
17989
|
0
|
|
|
|
|
|
$o->{objects}->{$key} = $state->{objects}->{$key}; |
17990
|
|
|
|
|
|
|
} |
17991
|
|
|
|
|
|
|
|
17992
|
0
|
|
|
|
|
|
push @{$o->{mergedSources}}, @{$state->{mergedSources}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
17993
|
0
|
|
|
|
|
|
push @{$o->{dataSavedHandlers}}, @{$state->{dataSavedHandlers}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
17994
|
|
|
|
|
|
|
} |