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.21 by root, Tue Sep 16 07:00:59 2003 UTC vs.
Revision 1.35 by root, Thu Mar 3 17:31:26 2005 UTC

72 72
73package Net::FCP; 73package Net::FCP;
74 74
75use Carp; 75use Carp;
76 76
77$VERSION = 0.08; 77$VERSION = 0.8;
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 s/(?<=[a-z])(?=[A-Z])/_/g;
105 lc $_;
106}
107
108=item $meta = Net::FCP::parse_metadata $string
109
110Parse a metadata string and return it.
111
112The metadata will be a hashref with key C<version> (containing the
113mandatory version header entries) and key C<raw> containing the original
114metadata string.
115
116All other headers are represented by arrayrefs (they can be repeated).
117
118Since this description is confusing, here is a rather verbose example of a
119parsed manifest:
120
121 (
122 raw => "Version...",
123 version => { revision => 1 },
124 document => [
125 {
126 info => { format" => "image/jpeg" },
127 name => "background.jpg",
128 redirect => { target => "freenet:CHK\@ZcagI,ra726bSw" },
129 },
130 {
131 info => { format" => "text/html" },
132 name => ".next",
133 redirect => { target => "freenet:SSK\@ilUPAgM/TFEE/3" },
134 },
135 {
136 info => { format" => "text/html" },
137 redirect => { target => "freenet:CHK\@8M8Po8ucwI,8xA" },
138 }
139 ]
140 )
141
142=cut
143
144sub parse_metadata {
145 my $data = shift;
146 my $meta = { raw => $data };
147
148 if ($data =~ /^Version\015?\012/gc) {
149 my $hdr = $meta->{version} = {};
150
151 for (;;) {
152 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) {
153 my ($k, $v) = ($1, $2);
154 my @p = split /\./, tolc $k, 3;
155
156 $hdr->{$p[0]} = $v if @p == 1; # lamest code I ever wrote
157 $hdr->{$p[0]}{$p[1]} = $v if @p == 2;
158 $hdr->{$p[0]}{$p[1]}{$p[2]} = $v if @p == 3;
159 die "FATAL: 4+ dot metadata" if @p >= 4;
160 }
161
162 if ($data =~ /\GEndPart\015?\012/gc) {
163 # nop
164 } elsif ($data =~ /\GEnd(\015?\012|$)/gc) {
165 last;
166 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) {
167 push @{$meta->{tolc $1}}, $hdr = {};
168 } elsif ($data =~ /\G(.*)/gcs) {
169 print STDERR "metadata format error ($1), please report this string: <<$data>>";
170 die "metadata format error";
171 }
172 }
173 }
174
175 #$meta->{tail} = substr $data, pos $data;
176
177 $meta;
178}
179
180=item $fcp = new Net::FCP [host => $host][, port => $port] 98=item $fcp = new Net::FCP [host => $host][, port => $port][, progress => \&cb]
181 99
182Create 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
183127.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>).
184 102
185Connections are virtual because no persistent physical connection is 103Connections are virtual because no persistent physical connection is
186established. 104established.
187 105
188=begin comment 106You can install a progress callback that is being called with the Net::FCP
107object, a txn object, the type of the transaction and the attributes. Use
108it like this:
189 109
190However, the existance of the node is checked by executing a 110 sub progress_cb {
191C<ClientHello> transaction. 111 my ($self, $txn, $type, $attr) = @_;
192 112
193=end 113 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
114 }
194 115
195=cut 116=cut
196 117
197sub new { 118sub new {
198 my $class = shift; 119 my $class = shift;
199 my $self = bless { @_ }, $class; 120 my $self = bless { @_ }, $class;
200 121
201 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1"; 122 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1";
202 $self->{port} ||= $ENV{FREDPORT} || 8481; 123 $self->{port} ||= $ENV{FREDPORT} || 8481;
203 124
204 #$self->{nodehello} = $self->client_hello
205 # or croak "unable to get nodehello from node\n";
206
207 $self; 125 $self;
208} 126}
209 127
210sub progress { 128sub progress {
211 my ($self, $txn, $type, $attr) = @_; 129 my ($self, $txn, $type, $attr) = @_;
212 #warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
213}
214 130
131 $self->{progress}->($self, $txn, $type, $attr)
132 if $self->{progress};
133}
134
215=item $txn = $fcp->txn(type => attr => val,...) 135=item $txn = $fcp->txn (type => attr => val,...)
216 136
217The low-level interface to transactions. Don't use it. 137The low-level interface to transactions. Don't use it unless you have
218 138"special needs". Instead, use predefiend transactions like this:
219Here are some examples of using transactions:
220 139
221The blocking case, no (visible) transactions involved: 140The blocking case, no (visible) transactions involved:
222 141
223 my $nodehello = $fcp->client_hello; 142 my $nodehello = $fcp->client_hello;
224 143
243sub txn { 162sub txn {
244 my ($self, $type, %attr) = @_; 163 my ($self, $type, %attr) = @_;
245 164
246 $type = touc $type; 165 $type = touc $type;
247 166
248 my $txn = "Net::FCP::Txn::$type"->new(fcp => $self, type => tolc $type, attr => \%attr); 167 my $txn = "Net::FCP::Txn::$type"->new (fcp => $self, type => tolc $type, attr => \%attr);
249 168
250 $txn; 169 $txn;
251} 170}
252 171
253{ # transactions 172{ # transactions
318 237
319=item $txn = $fcp->txn_generate_chk ($metadata, $data[, $cipher]) 238=item $txn = $fcp->txn_generate_chk ($metadata, $data[, $cipher])
320 239
321=item $uri = $fcp->generate_chk ($metadata, $data[, $cipher]) 240=item $uri = $fcp->generate_chk ($metadata, $data[, $cipher])
322 241
323Calculcates a CHK, given the metadata and data. C<$cipher> is either 242Calculates a CHK, given the metadata and data. C<$cipher> is either
324C<Rijndael> or C<Twofish>, with the latter being the default. 243C<Rijndael> or C<Twofish>, with the latter being the default.
325 244
326=cut 245=cut
327 246
328$txn->(generate_chk => sub { 247$txn->(generate_chk => sub {
329 my ($self, $metadata, $data, $cipher) = @_; 248 my ($self, $metadata, $data, $cipher) = @_;
330 249
250 $metadata = Net::FCP::Metadata::build_metadata $metadata;
251
331 $self->txn (generate_chk => 252 $self->txn (generate_chk =>
332 data => "$metadata$data", 253 data => "$metadata$data",
333 metadata_length => length $metadata, 254 metadata_length => xeh length $metadata,
334 cipher => $cipher || "Twofish"); 255 cipher => $cipher || "Twofish");
335}); 256});
336 257
337=item $txn = $fcp->txn_generate_svk_pair 258=item $txn = $fcp->txn_generate_svk_pair
338 259
339=item ($public, $private) = @{ $fcp->generate_svk_pair } 260=item ($public, $private, $crypto) = @{ $fcp->generate_svk_pair }
340 261
341Creates a new SVK pair. Returns an arrayref. 262Creates a new SVK pair. Returns an arrayref with the public key, the
263private key and a crypto key, which is just additional entropy.
342 264
343 [ 265 [
344 "hKs0-WDQA4pVZyMPKNFsK1zapWY", 266 "acLx4dux9fvvABH15Gk6~d3I-yw",
345 "ZnmvMITaTXBMFGl4~jrjuyWxOWg" 267 "cPoDkDMXDGSMM32plaPZDhJDxSs",
268 "BH7LXCov0w51-y9i~BoB3g",
346 ] 269 ]
270
271A private key (for inserting) can be constructed like this:
272
273 SSK@<private_key>,<crypto_key>/<name>
274
275It can be used to insert data. The corresponding public key looks like this:
276
277 SSK@<public_key>PAgM,<crypto_key>/<name>
278
279Watch out for the C<PAgM>-part!
347 280
348=cut 281=cut
349 282
350$txn->(generate_svk_pair => sub { 283$txn->(generate_svk_pair => sub {
351 my ($self) = @_; 284 my ($self) = @_;
352 285
353 $self->txn ("generate_svk_pair"); 286 $self->txn ("generate_svk_pair");
354}); 287});
355 288
356=item $txn = $fcp->txn_insert_private_key ($private) 289=item $txn = $fcp->txn_invert_private_key ($private)
357 290
358=item $public = $fcp->insert_private_key ($private) 291=item $public = $fcp->invert_private_key ($private)
359 292
360Inserts a private key. $private can be either an insert URI (must start 293Inverts a private key (returns the public key). C<$private> can be either
361with C<freenet:SSK@>) or a raw private key (i.e. the private value you get 294an insert URI (must start with C<freenet:SSK@>) or a raw private key (i.e.
362back from C<generate_svk_pair>). 295the private value you get back from C<generate_svk_pair>).
363 296
364Returns the public key. 297Returns the public key.
365 298
366UNTESTED.
367
368=cut 299=cut
369 300
370$txn->(insert_private_key => sub { 301$txn->(invert_private_key => sub {
371 my ($self, $privkey) = @_; 302 my ($self, $privkey) = @_;
372 303
373 $self->txn (invert_private_key => private => $privkey); 304 $self->txn (invert_private_key => private => $privkey);
374}); 305});
375 306
378=item $length = $fcp->get_size ($uri) 309=item $length = $fcp->get_size ($uri)
379 310
380Finds and returns the size (rounded up to the nearest power of two) of the 311Finds and returns the size (rounded up to the nearest power of two) of the
381given document. 312given document.
382 313
383UNTESTED.
384
385=cut 314=cut
386 315
387$txn->(get_size => sub { 316$txn->(get_size => sub {
388 my ($self, $uri) = @_; 317 my ($self, $uri) = @_;
389 318
392 321
393=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]]) 322=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]])
394 323
395=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal) 324=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal)
396 325
397Fetches a (small, as it should fit into memory) file from 326Fetches a (small, as it should fit into memory) key content block from
398freenet. 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>).
399C<undef>).
400 328
401Due to the overhead, a better method to download big files should be used. 329The C<$uri> should begin with C<freenet:>, but the scheme is currently
330added, if missing.
402 331
403 my ($meta, $data) = @{ 332 my ($meta, $data) = @{
404 $fcp->client_get ( 333 $fcp->client_get (
405 "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w" 334 "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w"
406 ) 335 )
409=cut 338=cut
410 339
411$txn->(client_get => sub { 340$txn->(client_get => sub {
412 my ($self, $uri, $htl, $removelocal) = @_; 341 my ($self, $uri, $htl, $removelocal) = @_;
413 342
343 $uri =~ s/^freenet://; $uri = "freenet:$uri";
344
414 $self->txn (client_get => URI => $uri, hops_to_live => (defined $htl ? $htl :15), 345 $self->txn (client_get => URI => $uri, hops_to_live => xeh (defined $htl ? $htl : 15),
415 remove_local_key => $removelocal ? "true" : "false"); 346 remove_local_key => $removelocal ? "true" : "false");
416}); 347});
417 348
418=item $txn = $fcp->txn_client_put ($uri, $metadata, $data, $htl, $removelocal) 349=item $txn = $fcp->txn_client_put ($uri, $metadata, $data, $htl, $removelocal)
419 350
420=item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal); 351=item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
421 352
422Insert a new key. If the client is inserting a CHK, the URI may be 353Insert a new key. If the client is inserting a CHK, the URI may be
423abbreviated as just CHK@. In this case, the node will calculate the 354abbreviated as just CHK@. In this case, the node will calculate the
424CHK. 355CHK. If the key is a private SSK key, the node will calculcate the public
356key and the resulting public URI.
425 357
426C<$meta> can be a reference or a string (ONLY THE STRING CASE IS IMPLEMENTED!). 358C<$meta> can be a hash reference (same format as returned by
359C<Net::FCP::parse_metadata>) or a string.
427 360
428THIS INTERFACE IS UNTESTED AND SUBJECT TO CHANGE. 361The result is an arrayref with the keys C<uri>, C<public_key> and C<private_key>.
429 362
430=cut 363=cut
431 364
432$txn->(client_put => sub { 365$txn->(client_put => sub {
433 my ($self, $uri, $meta, $data, $htl, $removelocal) = @_; 366 my ($self, $uri, $metadata, $data, $htl, $removelocal) = @_;
434 367
435 $self->txn (client_put => URI => $uri, hops_to_live => (defined $htl ? $htl :15), 368 $metadata = Net::FCP::Metadata::build_metadata $metadata;
369 $uri =~ s/^freenet://; $uri = "freenet:$uri";
370
371 $self->txn (client_put => URI => $uri,
372 hops_to_live => xeh (defined $htl ? $htl : 15),
436 remove_local_key => $removelocal ? "true" : "false", 373 remove_local_key => $removelocal ? "true" : "false",
437 data => "$meta$data", metadata_length => length $meta); 374 data => "$metadata$data", metadata_length => xeh length $metadata);
438}); 375});
439 376
440} # transactions 377} # transactions
441 378
442=item MISSING: (ClientPut), InsretKey
443
444=back 379=back
445 380
446=head2 THE Net::FCP::Txn CLASS 381=head2 THE Net::FCP::Txn CLASS
447 382
448All requests (or transactions) are executed in a asynchroneous way (LIE: 383All requests (or transactions) are executed in a asynchronous way. For
449uploads are blocking). For each request, a C<Net::FCP::Txn> object is 384each request, a C<Net::FCP::Txn> object is created (worse: a tcp
450created (worse: a tcp connection is created, too). 385connection is created, too).
451 386
452For each request there is actually a different subclass (and it's possible 387For each request there is actually a different subclass (and it's possible
453to subclass these, although of course not documented). 388to subclass these, although of course not documented).
454 389
455The most interesting method is C<result>. 390The most interesting method is C<result>.
507 442
508 #shutdown $fh, 1; # freenet buggy?, well, it's java... 443 #shutdown $fh, 1; # freenet buggy?, well, it's java...
509 444
510 $self->{fh} = $fh; 445 $self->{fh} = $fh;
511 446
512 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1); 447 $self->{w} = $EVENT->new_from_fh ($fh)
448 ->cb (sub { $self->fh_ready_w })
449 ->poll (0, 1, 1);
513 450
514 $self; 451 $self;
515} 452}
516 453
517=item $txn = $txn->cb ($coderef) 454=item $txn = $txn->cb ($coderef)
553 $self; 490 $self;
554} 491}
555 492
556=item $txn->cancel (%attr) 493=item $txn->cancel (%attr)
557 494
558Cancels the operation with a C<cancel> exception anf the given attributes 495Cancels the operation with a C<cancel> exception and the given attributes
559(consider at least giving the attribute C<reason>). 496(consider at least giving the attribute C<reason>).
560 497
561UNTESTED. 498UNTESTED.
562 499
563=cut 500=cut
672 } 609 }
673} 610}
674 611
675sub progress { 612sub progress {
676 my ($self, $type, $attr) = @_; 613 my ($self, $type, $attr) = @_;
614
677 $self->{fcp}->progress ($self, $type, $attr); 615 $self->{fcp}->progress ($self, $type, $attr);
678} 616}
679 617
680=item $result = $txn->result 618=item $result = $txn->result
681 619
682Waits until a result is available and then returns it. 620Waits until a result is available and then returns it.
683 621
684This waiting is (depending on your event model) not very efficient, as it 622This waiting is (depending on your event model) not very efficient, as it
685is done outside the "mainloop". 623is done outside the "mainloop". The biggest problem, however, is that it's
624blocking one thread of execution. Try to use the callback mechanism, if
625possible, and call result from within the callback (or after is has been
626run), as then no waiting is necessary.
686 627
687=cut 628=cut
688 629
689sub result { 630sub result {
690 my ($self) = @_; 631 my ($self) = @_;
730 671
731use base Net::FCP::Txn; 672use base Net::FCP::Txn;
732 673
733sub rcv_success { 674sub rcv_success {
734 my ($self, $attr) = @_; 675 my ($self, $attr) = @_;
735 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]); 676 $self->set_result ([$attr->{public_key}, $attr->{private_key}, $attr->{crypto_key}]);
736} 677}
737 678
738package Net::FCP::Txn::InsertPrivateKey; 679package Net::FCP::Txn::InvertPrivateKey;
739 680
740use base Net::FCP::Txn; 681use base Net::FCP::Txn;
741 682
742sub rcv_success { 683sub rcv_success {
743 my ($self, $attr) = @_; 684 my ($self, $attr) = @_;
744 $self->set_result ($attr->{PublicKey}); 685 $self->set_result ($attr->{public_key});
745} 686}
746 687
747package Net::FCP::Txn::GetSize; 688package Net::FCP::Txn::GetSize;
748 689
749use base Net::FCP::Txn; 690use base Net::FCP::Txn;
750 691
751sub rcv_success { 692sub rcv_success {
752 my ($self, $attr) = @_; 693 my ($self, $attr) = @_;
753 $self->set_result ($attr->{Length}); 694 $self->set_result (hex $attr->{length});
754} 695}
755 696
756package Net::FCP::Txn::GetPut; 697package Net::FCP::Txn::GetPut;
757 698
758# base class for get and put 699# base class for get and put
759 700
760use base Net::FCP::Txn; 701use base Net::FCP::Txn;
761 702
762*rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception; 703*rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception;
763*rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception; 704*rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception;
764 705
765sub rcv_restarted { 706sub rcv_restarted {
766 my ($self, $attr, $type) = @_; 707 my ($self, $attr, $type) = @_;
767 708
768 delete $self->{datalength}; 709 delete $self->{datalength};
785 726
786 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} }); 727 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} });
787 728
788 if ($self->{datalength} == length $self->{data}) { 729 if ($self->{datalength} == length $self->{data}) {
789 my $data = delete $self->{data}; 730 my $data = delete $self->{data};
790 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, ""; 731 my $meta = new Net::FCP::Metadata (substr $data, 0, $self->{metalength}, "");
791 732
792 $self->set_result ([$meta, $data]); 733 $self->set_result ([$meta, $data]);
734 $self->eof;
793 } 735 }
794} 736}
795 737
796sub rcv_data_found { 738sub rcv_data_found {
797 my ($self, $attr, $type) = @_; 739 my ($self, $attr, $type) = @_;
805package Net::FCP::Txn::ClientPut; 747package Net::FCP::Txn::ClientPut;
806 748
807use base Net::FCP::Txn::GetPut; 749use base Net::FCP::Txn::GetPut;
808 750
809*rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception; 751*rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception;
810*rcv_key_collision = \&Net::FCP::Txn::rcv_throw_exception;
811 752
812sub rcv_pending { 753sub rcv_pending {
813 my ($self, $attr, $type) = @_; 754 my ($self, $attr, $type) = @_;
814 $self->progress ($type, $attr); 755 $self->progress ($type, $attr);
815} 756}
817sub rcv_success { 758sub rcv_success {
818 my ($self, $attr, $type) = @_; 759 my ($self, $attr, $type) = @_;
819 $self->set_result ($attr); 760 $self->set_result ($attr);
820} 761}
821 762
763sub rcv_key_collision {
764 my ($self, $attr, $type) = @_;
765 $self->set_result ({ key_collision => 1, %$attr });
766}
767
822=back 768=back
823 769
824=head2 The Net::FCP::Exception CLASS 770=head2 The Net::FCP::Exception CLASS
825 771
826Any unexpected (non-standard) responses that make it impossible to return 772Any unexpected (non-standard) responses that make it impossible to return
835 781
836package Net::FCP::Exception; 782package Net::FCP::Exception;
837 783
838use overload 784use overload
839 '""' => sub { 785 '""' => sub {
840 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>\n"; 786 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>";
841 }; 787 };
842 788
843=item $exc = new Net::FCP::Exception $type, \%attr 789=item $exc = new Net::FCP::Exception $type, \%attr
844 790
845Create a new exception object of the given type (a string like 791Create a new exception object of the given type (a string like
892 838
893=head1 BUGS 839=head1 BUGS
894 840
895=head1 AUTHOR 841=head1 AUTHOR
896 842
897 Marc Lehmann <pcg@goof.com> 843 Marc Lehmann <schmorp@schmorp.de>
898 http://www.goof.com/pcg/marc/ 844 http://home.schmorp.de/
899 845
900=cut 846=cut
901 847
902package Net::FCP::Event::Auto; 848package Net::FCP::Event::Auto;
903 849
904my @models = ( 850my @models = (
905 [Coro => Coro::Event:: ], 851 [Coro => Coro::Event::],
906 [Event => Event::], 852 [Event => Event::],
907 [Glib => Glib:: ], 853 [Glib => Glib::],
908 [Tk => Tk::], 854 [Tk => Tk::],
909); 855);
910 856
911sub AUTOLOAD { 857sub AUTOLOAD {
912 $AUTOLOAD =~ s/.*://; 858 $AUTOLOAD =~ s/.*://;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines