ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-FCP/FCP.pm
(Generate patch)

Comparing cvsroot/Net-FCP/FCP.pm (file contents):
Revision 1.29 by root, Thu May 13 21:43:16 2004 UTC vs.
Revision 1.32 by root, Fri May 14 17:25:17 2004 UTC

72 72
73package Net::FCP; 73package Net::FCP;
74 74
75use Carp; 75use Carp;
76 76
77$VERSION = 0.6; 77$VERSION = 0.7;
78 78
79no warnings; 79no warnings;
80
81use Net::FCP::Metadata;
82use Net::FCP::Util qw(tolc touc xeh);
80 83
81our $EVENT = Net::FCP::Event::Auto::; 84our $EVENT = Net::FCP::Event::Auto::;
82 85
83sub import { 86sub import {
84 shift; 87 shift;
90 } 93 }
91 } 94 }
92 die $@ if $@; 95 die $@ if $@;
93} 96}
94 97
95sub touc($) {
96 local $_ = shift;
97 1 while s/((?:^|_)(?:svk|chk|uri)(?:_|$))/\U$1/;
98 s/(?:^|_)(.)/\U$1/g;
99 $_;
100}
101
102sub tolc($) {
103 local $_ = shift;
104 1 while s/(SVK|CHK|URI)([^_])/$1\_$2/i;
105 1 while s/([^_])(SVK|CHK|URI)/$1\_$2/i;
106 s/(?<=[a-z])(?=[A-Z])/_/g;
107 lc $_;
108}
109
110# the opposite of hex
111sub xeh($) {
112 sprintf "%x", $_[0];
113}
114
115=item $meta = Net::FCP::parse_metadata $string
116
117Parse a metadata string and return it.
118
119The metadata will be a hashref with key C<version> (containing the
120mandatory version header entries) and key C<raw> containing the original
121metadata string.
122
123All other headers are represented by arrayrefs (they can be repeated).
124
125Since this description is confusing, here is a rather verbose example of a
126parsed manifest:
127
128 (
129 raw => "Version...",
130 version => { revision => 1 },
131 document => [
132 {
133 info => { format" => "image/jpeg" },
134 name => "background.jpg",
135 redirect => { target => "freenet:CHK\@ZcagI,ra726bSw" },
136 },
137 {
138 info => { format" => "text/html" },
139 name => ".next",
140 redirect => { target => "freenet:SSK\@ilUPAgM/TFEE/3" },
141 },
142 {
143 info => { format" => "text/html" },
144 redirect => { target => "freenet:CHK\@8M8Po8ucwI,8xA" },
145 }
146 ]
147 )
148
149=cut
150
151sub parse_metadata {
152 my $data = shift;
153 my $meta = { raw => $data };
154
155 if ($data =~ /^Version\015?\012/gc) {
156 my $hdr = $meta->{version} = {};
157
158 for (;;) {
159 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) {
160 my ($k, $v) = ($1, $2);
161 my @p = split /\./, tolc $k, 3;
162
163 $hdr->{$p[0]} = $v if @p == 1; # lamest code I ever wrote
164 $hdr->{$p[0]}{$p[1]} = $v if @p == 2;
165 $hdr->{$p[0]}{$p[1]}{$p[2]} = $v if @p == 3;
166 die "FATAL: 4+ dot metadata" if @p >= 4;
167 }
168
169 if ($data =~ /\GEndPart\015?\012/gc) {
170 # nop
171 } elsif ($data =~ /\GEnd(\015?\012|$)/gc) {
172 last;
173 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) {
174 push @{$meta->{tolc $1}}, $hdr = {};
175 } elsif ($data =~ /\G(.*)/gcs) {
176 print STDERR "metadata format error ($1), please report this string: <<$data>>";
177 die "metadata format error";
178 }
179 }
180 }
181
182 #$meta->{tail} = substr $data, pos $data;
183
184 $meta;
185}
186
187=item $string = Net::FCP::build_metadata $meta
188
189Takes a hash reference as returned by C<Net::FCP::parse_metadata> and
190returns the corresponding string form. If a string is given, it's returned
191as is.
192
193=cut
194
195sub build_metadata_subhash($$$) {
196 my ($prefix, $level, $hash) = @_;
197
198 join "",
199 map
200 ref $hash->{$_} ? build_metadata_subhash ($prefix . (Net::FCP::touc $_) . ".", $level + 1, $hash->{$_})
201 : $prefix . ($level > 1 ? $_ : Net::FCP::touc $_) . "=" . $hash->{$_} . "\n",
202 keys %$hash;
203}
204
205sub build_metadata_hash($$) {
206 my ($header, $hash) = @_;
207
208 if (ref $hash eq ARRAY::) {
209 join "", map build_metadata_hash ($header, $_), @$hash
210 } else {
211 (Net::FCP::touc $header) . "\n"
212 . (build_metadata_subhash "", 0, $hash)
213 . "EndPart\n";
214 }
215}
216
217sub build_metadata($) {
218 my ($meta) = @_;
219
220 return $meta unless ref $meta;
221
222 $meta = { %$meta };
223
224 delete $meta->{raw};
225
226 my $res =
227 (build_metadata_hash version => delete $meta->{version})
228 . (join "", map +(build_metadata_hash $_, $meta->{$_}), keys %$meta);
229
230 substr $res, 0, -5; # get rid of "Part". Broken Syntax....
231}
232
233
234=item $fcp = new Net::FCP [host => $host][, port => $port][, progress => \&cb] 98=item $fcp = new Net::FCP [host => $host][, port => $port][, progress => \&cb]
235 99
236Create a new virtual FCP connection to the given host and port (default 100Create a new virtual FCP connection to the given host and port (default
237127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>). 101127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>).
238 102
247 my ($self, $txn, $type, $attr) = @_; 111 my ($self, $txn, $type, $attr) = @_;
248 112
249 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n"; 113 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
250 } 114 }
251 115
252=begin comment
253
254However, the existance of the node is checked by executing a
255C<ClientHello> transaction.
256
257=end
258
259=cut 116=cut
260 117
261sub new { 118sub new {
262 my $class = shift; 119 my $class = shift;
263 my $self = bless { @_ }, $class; 120 my $self = bless { @_ }, $class;
264 121
265 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1"; 122 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1";
266 $self->{port} ||= $ENV{FREDPORT} || 8481; 123 $self->{port} ||= $ENV{FREDPORT} || 8481;
267 124
268 #$self->{nodehello} = $self->client_hello
269 # or croak "unable to get nodehello from node\n";
270
271 $self; 125 $self;
272} 126}
273 127
274sub progress { 128sub progress {
275 my ($self, $txn, $type, $attr) = @_; 129 my ($self, $txn, $type, $attr) = @_;
276 130
277 $self->{progress}->($self, $txn, $type, $attr) 131 $self->{progress}->($self, $txn, $type, $attr)
278 if $self->{progress}; 132 if $self->{progress};
279} 133}
280 134
281=item $txn = $fcp->txn(type => attr => val,...) 135=item $txn = $fcp->txn (type => attr => val,...)
282 136
283The low-level interface to transactions. Don't use it. 137The low-level interface to transactions. Don't use it unless you have
284 138"special needs". Instead, use predefiend transactions like this:
285Here are some examples of using transactions:
286 139
287The blocking case, no (visible) transactions involved: 140The blocking case, no (visible) transactions involved:
288 141
289 my $nodehello = $fcp->client_hello; 142 my $nodehello = $fcp->client_hello;
290 143
392=cut 245=cut
393 246
394$txn->(generate_chk => sub { 247$txn->(generate_chk => sub {
395 my ($self, $metadata, $data, $cipher) = @_; 248 my ($self, $metadata, $data, $cipher) = @_;
396 249
250 $metadata = Net::FCP::Metadata::build_metadata $metadata;
251
397 $self->txn (generate_chk => 252 $self->txn (generate_chk =>
398 data => "$metadata$data", 253 data => "$metadata$data",
399 metadata_length => xeh length $metadata, 254 metadata_length => xeh length $metadata,
400 cipher => $cipher || "Twofish"); 255 cipher => $cipher || "Twofish");
401}); 256});
402 257
403=item $txn = $fcp->txn_generate_svk_pair 258=item $txn = $fcp->txn_generate_svk_pair
404 259
405=item ($public, $private) = @{ $fcp->generate_svk_pair } 260=item ($public, $private, $crypto) = @{ $fcp->generate_svk_pair }
406 261
407Creates a new SVK pair. Returns an arrayref with the public key, the 262Creates a new SVK pair. Returns an arrayref with the public key, the
408private key and a crypto key, which is just additional entropy. 263private key and a crypto key, which is just additional entropy.
409 264
410 [ 265 [
466 321
467=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]]) 322=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]])
468 323
469=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal) 324=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal)
470 325
471Fetches a (small, as it should fit into memory) file from 326Fetches a (small, as it should fit into memory) key content block from
472freenet. C<$meta> is the metadata (as returned by C<parse_metadata> or 327freenet. C<$meta> is a C<Net::FCP::Metadata> object or C<undef>).
473C<undef>).
474 328
475The C<$uri> should begin with C<freenet:>, but the scheme is currently 329The C<$uri> should begin with C<freenet:>, but the scheme is currently
476added, if missing. 330added, if missing.
477
478Due to the overhead, a better method to download big files should be used.
479 331
480 my ($meta, $data) = @{ 332 my ($meta, $data) = @{
481 $fcp->client_get ( 333 $fcp->client_get (
482 "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w" 334 "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w"
483 ) 335 )
486=cut 338=cut
487 339
488$txn->(client_get => sub { 340$txn->(client_get => sub {
489 my ($self, $uri, $htl, $removelocal) = @_; 341 my ($self, $uri, $htl, $removelocal) = @_;
490 342
491 $uri =~ s/^freenet://; 343 $uri =~ s/^freenet://; $uri = "freenet:$uri";
492 $uri = "freenet:$uri";
493 344
494 $self->txn (client_get => URI => $uri, hops_to_live => xeh (defined $htl ? $htl : 15), 345 $self->txn (client_get => URI => $uri, hops_to_live => xeh (defined $htl ? $htl : 15),
495 remove_local_key => $removelocal ? "true" : "false"); 346 remove_local_key => $removelocal ? "true" : "false");
496}); 347});
497 348
510The result is an arrayref with the keys C<uri>, C<public_key> and C<private_key>. 361The result is an arrayref with the keys C<uri>, C<public_key> and C<private_key>.
511 362
512=cut 363=cut
513 364
514$txn->(client_put => sub { 365$txn->(client_put => sub {
515 my ($self, $uri, $meta, $data, $htl, $removelocal) = @_; 366 my ($self, $uri, $metadata, $data, $htl, $removelocal) = @_;
516 367
517 $meta = build_metadata $meta; 368 $metadata = Net::FCP::Metadata::build_metadata $metadata;
369 $uri =~ s/^freenet://; $uri = "freenet:$uri";
518 370
519 $self->txn (client_put => URI => $uri, 371 $self->txn (client_put => URI => $uri,
520 hops_to_live => xeh (defined $htl ? $htl : 15), 372 hops_to_live => xeh (defined $htl ? $htl : 15),
521 remove_local_key => $removelocal ? "true" : "false", 373 remove_local_key => $removelocal ? "true" : "false",
522 data => "$meta$data", metadata_length => xeh length $meta); 374 data => "$metadata$data", metadata_length => xeh length $metadata);
523}); 375});
524 376
525} # transactions 377} # transactions
526 378
527=back 379=back
872 724
873 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} }); 725 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} });
874 726
875 if ($self->{datalength} == length $self->{data}) { 727 if ($self->{datalength} == length $self->{data}) {
876 my $data = delete $self->{data}; 728 my $data = delete $self->{data};
877 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, ""; 729 my $meta = new Net::FCP::Metadata (substr $data, 0, $self->{metalength}, "");
878 730
879 $self->set_result ([$meta, $data]); 731 $self->set_result ([$meta, $data]);
880 $self->eof; 732 $self->eof;
881 } 733 }
882} 734}
893package Net::FCP::Txn::ClientPut; 745package Net::FCP::Txn::ClientPut;
894 746
895use base Net::FCP::Txn::GetPut; 747use base Net::FCP::Txn::GetPut;
896 748
897*rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception; 749*rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception;
898*rcv_key_collision = \&Net::FCP::Txn::rcv_throw_exception;
899 750
900sub rcv_pending { 751sub rcv_pending {
901 my ($self, $attr, $type) = @_; 752 my ($self, $attr, $type) = @_;
902 $self->progress ($type, $attr); 753 $self->progress ($type, $attr);
903} 754}
904 755
905sub rcv_success { 756sub rcv_success {
906 my ($self, $attr, $type) = @_; 757 my ($self, $attr, $type) = @_;
907 $self->set_result ($attr); 758 $self->set_result ($attr);
759}
760
761sub rcv_key_collision {
762 my ($self, $attr, $type) = @_;
763 $self->set_result ({ key_collision => 1, %$attr });
908} 764}
909 765
910=back 766=back
911 767
912=head2 The Net::FCP::Exception CLASS 768=head2 The Net::FCP::Exception CLASS

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines