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

Comparing Net-FCP/FCP.pm (file contents):
Revision 1.13 by root, Wed Sep 10 05:06:16 2003 UTC vs.
Revision 1.32 by root, Fri May 14 17:25:17 2004 UTC

35The import tag to use is named C<event=xyz>, e.g. C<event=Event>, 35The import tag to use is named C<event=xyz>, e.g. C<event=Event>,
36C<event=Glib> etc. 36C<event=Glib> etc.
37 37
38You should specify the event module to use only in the main program. 38You should specify the event module to use only in the main program.
39 39
40If no event model has been specified, FCP tries to autodetect it on first
41use (e.g. first transaction), in this order: Coro, Event, Glib, Tk.
42
43=head2 FREENET BASICS
44
45Ok, this section will not explain any freenet basics to you, just some
46problems I found that you might want to avoid:
47
48=over 4
49
50=item freenet URIs are _NOT_ URIs
51
52Whenever a "uri" is required by the protocol, freenet expects a kind of
53URI prefixed with the "freenet:" scheme, e.g. "freenet:CHK...". However,
54these are not URIs, as freeent fails to parse them correctly, that is, you
55must unescape an escaped characters ("%2c" => ",") yourself. Maybe in the
56future this library will do it for you, so watch out for this incompatible
57change.
58
59=item Numbers are in HEX
60
61Virtually every number in the FCP protocol is in hex. Be sure to use
62C<hex()> on all such numbers, as the module (currently) does nothing to
63convert these for you.
64
65=back
66
40=head2 THE Net::FCP CLASS 67=head2 THE Net::FCP CLASS
41 68
42=over 4 69=over 4
43 70
44=cut 71=cut
45 72
46package Net::FCP; 73package Net::FCP;
47 74
48use Carp; 75use Carp;
49 76
50$VERSION = 0.05; 77$VERSION = 0.7;
51 78
52no warnings; 79no warnings;
53 80
81use Net::FCP::Metadata;
82use Net::FCP::Util qw(tolc touc xeh);
83
54our $EVENT = Net::FCP::Event::Auto::; 84our $EVENT = Net::FCP::Event::Auto::;
55$EVENT = Net::FCP::Event::Event;#d#
56 85
57sub import { 86sub import {
58 shift; 87 shift;
59 88
60 for (@_) { 89 for (@_) {
61 if (/^event=(\w+)$/) { 90 if (/^event=(\w+)$/) {
62 $EVENT = "Net::FCP::Event::$1"; 91 $EVENT = "Net::FCP::Event::$1";
92 eval "require $EVENT";
63 } 93 }
64 } 94 }
65 eval "require $EVENT";
66 die $@ if $@; 95 die $@ if $@;
67} 96}
68 97
69sub touc($) {
70 local $_ = shift;
71 1 while s/((?:^|_)(?:svk|chk|uri)(?:_|$))/\U$1/;
72 s/(?:^|_)(.)/\U$1/g;
73 $_;
74}
75
76sub tolc($) {
77 local $_ = shift;
78 s/(?<=[a-z])(?=[A-Z])/_/g;
79 lc $_;
80}
81
82=item $meta = Net::FCP::parse_metadata $string
83
84Parse a metadata string and return it.
85
86The metadata will be a hashref with key C<version> (containing
87the mandatory version header entries).
88
89All other headers are represented by arrayrefs (they can be repeated).
90
91Since this is confusing, here is a rather verbose example of a parsed
92manifest:
93
94 (
95 version => { revision => 1 },
96 document => [
97 {
98 "info.format" => "image/jpeg",
99 name => "background.jpg",
100 "redirect.target" => "freenet:CHK\@ZcagI,ra726bSw"
101 },
102 {
103 "info.format" => "text/html",
104 name => ".next",
105 "redirect.target" => "freenet:SSK\@ilUPAgM/TFEE/3"
106 },
107 {
108 "info.format" => "text/html",
109 "redirect.target" => "freenet:CHK\@8M8Po8ucwI,8xA"
110 }
111 ]
112 )
113
114=cut
115
116sub parse_metadata {
117 my $meta;
118
119 my $data = shift;
120 if ($data =~ /^Version\015?\012/gc) {
121 my $hdr = $meta->{version} = {};
122
123 for (;;) {
124 while ($data =~ /\G([^=\015\012]+)=([^\015\012]*)\015?\012/gc) {
125 my ($k, $v) = ($1, $2);
126 my @p = split /\./, tolc $k, 3;
127
128 $hdr->{$p[0]} = $v if @p == 1; # lamest code I ever wrote
129 $hdr->{$p[0]}{$p[1]} = $v if @p == 2;
130 $hdr->{$p[0]}{$p[1]}{$p[3]} = $v if @p == 3;
131 die "FATAL: 4+ dot metadata" if @p >= 4;
132 }
133
134 if ($data =~ /\GEndPart\015?\012/gc) {
135 # nop
136 } elsif ($data =~ /\GEnd\015?\012/gc) {
137 last;
138 } elsif ($data =~ /\G([A-Za-z0-9.\-]+)\015?\012/gcs) {
139 push @{$meta->{tolc $1}}, $hdr = {};
140 } elsif ($data =~ /\G(.*)/gcs) {
141 die "metadata format error ($1)";
142 }
143 }
144 }
145
146 #$meta->{tail} = substr $data, pos $data;
147
148 $meta;
149}
150
151=item $fcp = new Net::FCP [host => $host][, port => $port] 98=item $fcp = new Net::FCP [host => $host][, port => $port][, progress => \&cb]
152 99
153Create 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
154127.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>).
155 102
156Connections are virtual because no persistent physical connection is 103Connections are virtual because no persistent physical connection is
157established. However, the existance of the node is checked by executing a 104established.
158C<ClientHello> transaction. 105
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:
109
110 sub progress_cb {
111 my ($self, $txn, $type, $attr) = @_;
112
113 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
114 }
159 115
160=cut 116=cut
161 117
162sub new { 118sub new {
163 my $class = shift; 119 my $class = shift;
164 my $self = bless { @_ }, $class; 120 my $self = bless { @_ }, $class;
165 121
166 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1"; 122 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1";
167 $self->{port} ||= $ENV{FREDPORT} || 8481; 123 $self->{port} ||= $ENV{FREDPORT} || 8481;
168 124
169 #$self->{nodehello} = $self->client_hello
170 # or croak "unable to get nodehello from node\n";
171
172 $self; 125 $self;
173} 126}
174 127
175sub progress { 128sub progress {
176 my ($self, $txn, $type, $attr) = @_; 129 my ($self, $txn, $type, $attr) = @_;
177 warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
178}
179 130
131 $self->{progress}->($self, $txn, $type, $attr)
132 if $self->{progress};
133}
134
180=item $txn = $fcp->txn(type => attr => val,...) 135=item $txn = $fcp->txn (type => attr => val,...)
181 136
182The low-level interface to transactions. Don't use it. 137The low-level interface to transactions. Don't use it unless you have
183 138"special needs". Instead, use predefiend transactions like this:
184Here are some examples of using transactions:
185 139
186The blocking case, no (visible) transactions involved: 140The blocking case, no (visible) transactions involved:
187 141
188 my $nodehello = $fcp->client_hello; 142 my $nodehello = $fcp->client_hello;
189 143
208sub txn { 162sub txn {
209 my ($self, $type, %attr) = @_; 163 my ($self, $type, %attr) = @_;
210 164
211 $type = touc $type; 165 $type = touc $type;
212 166
213 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);
214 168
215 $txn; 169 $txn;
216} 170}
217 171
218sub _txn($&) { 172{ # transactions
173
174my $txn = sub {
219 my ($name, $sub) = @_; 175 my ($name, $sub) = @_;
220 *{"$name\_txn"} = $sub; 176 *{"txn_$name"} = $sub;
221 *{$name} = sub { $sub->(@_)->result }; 177 *{$name} = sub { $sub->(@_)->result };
222} 178};
223 179
224=item $txn = $fcp->txn_client_hello 180=item $txn = $fcp->txn_client_hello
225 181
226=item $nodehello = $fcp->client_hello 182=item $nodehello = $fcp->client_hello
227 183
233 protocol => "1.2", 189 protocol => "1.2",
234 } 190 }
235 191
236=cut 192=cut
237 193
238_txn client_hello => sub { 194$txn->(client_hello => sub {
239 my ($self) = @_; 195 my ($self) = @_;
240 196
241 $self->txn ("client_hello"); 197 $self->txn ("client_hello");
242}; 198});
243 199
244=item $txn = $fcp->txn_client_info 200=item $txn = $fcp->txn_client_info
245 201
246=item $nodeinfo = $fcp->client_info 202=item $nodeinfo = $fcp->client_info
247 203
271 routing_time => "a5", 227 routing_time => "a5",
272 } 228 }
273 229
274=cut 230=cut
275 231
276_txn client_info => sub { 232$txn->(client_info => sub {
277 my ($self) = @_; 233 my ($self) = @_;
278 234
279 $self->txn ("client_info"); 235 $self->txn ("client_info");
280}; 236});
281 237
282=item $txn = $fcp->txn_generate_chk ($metadata, $data) 238=item $txn = $fcp->txn_generate_chk ($metadata, $data[, $cipher])
283 239
284=item $uri = $fcp->generate_chk ($metadata, $data) 240=item $uri = $fcp->generate_chk ($metadata, $data[, $cipher])
285 241
286Creates a new CHK, given the metadata and data. UNTESTED. 242Calculates a CHK, given the metadata and data. C<$cipher> is either
243C<Rijndael> or C<Twofish>, with the latter being the default.
287 244
288=cut 245=cut
289 246
290_txn generate_chk => sub { 247$txn->(generate_chk => sub {
291 my ($self, $metadata, $data) = @_; 248 my ($self, $metadata, $data, $cipher) = @_;
292 249
293 $self->txn (generate_chk => data => "$data$metadata", metadata_length => length $metadata); 250 $metadata = Net::FCP::Metadata::build_metadata $metadata;
251
252 $self->txn (generate_chk =>
253 data => "$metadata$data",
254 metadata_length => xeh length $metadata,
255 cipher => $cipher || "Twofish");
294}; 256});
295 257
296=item $txn = $fcp->txn_generate_svk_pair 258=item $txn = $fcp->txn_generate_svk_pair
297 259
298=item ($public, $private) = @{ $fcp->generate_svk_pair } 260=item ($public, $private, $crypto) = @{ $fcp->generate_svk_pair }
299 261
300Creates 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.
301 264
302 [ 265 [
303 "hKs0-WDQA4pVZyMPKNFsK1zapWY", 266 "acLx4dux9fvvABH15Gk6~d3I-yw",
304 "ZnmvMITaTXBMFGl4~jrjuyWxOWg" 267 "cPoDkDMXDGSMM32plaPZDhJDxSs",
268 "BH7LXCov0w51-y9i~BoB3g",
305 ] 269 ]
306 270
307=cut 271A private key (for inserting) can be constructed like this:
308 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!
280
281=cut
282
309_txn generate_svk_pair => sub { 283$txn->(generate_svk_pair => sub {
310 my ($self) = @_; 284 my ($self) = @_;
311 285
312 $self->txn ("generate_svk_pair"); 286 $self->txn ("generate_svk_pair");
313}; 287});
314 288
315=item $txn = $fcp->txn_insert_private_key ($private) 289=item $txn = $fcp->txn_invert_private_key ($private)
316 290
317=item $uri = $fcp->insert_private_key ($private) 291=item $public = $fcp->invert_private_key ($private)
318 292
319Inserts 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
320with freenet:SSK@) or a raw private key (i.e. the private value you get back 294an insert URI (must start with C<freenet:SSK@>) or a raw private key (i.e.
321from C<generate_svk_pair>). 295the private value you get back from C<generate_svk_pair>).
322 296
323Returns the public key. 297Returns the public key.
324 298
325UNTESTED.
326
327=cut 299=cut
328 300
329_txn insert_private_key => sub { 301$txn->(invert_private_key => sub {
330 my ($self, $privkey) = @_; 302 my ($self, $privkey) = @_;
331 303
332 $self->txn (invert_private_key => private => $privkey); 304 $self->txn (invert_private_key => private => $privkey);
333}; 305});
334 306
335=item $txn = $fcp->txn_get_size ($uri) 307=item $txn = $fcp->txn_get_size ($uri)
336 308
337=item $length = $fcp->get_size ($uri) 309=item $length = $fcp->get_size ($uri)
338 310
339Finds 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
340given document. 312given document.
341 313
342UNTESTED.
343
344=cut 314=cut
345 315
346_txn get_size => sub { 316$txn->(get_size => sub {
347 my ($self, $uri) = @_; 317 my ($self, $uri) = @_;
348 318
349 $self->txn (get_size => URI => $uri); 319 $self->txn (get_size => URI => $uri);
350}; 320});
351 321
352=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]]) 322=item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]])
353 323
354=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal) 324=item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal)
355 325
356Fetches a (small, as it should fit into memory) file from 326Fetches a (small, as it should fit into memory) key content block from
357freenet. 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>).
358C<undef>).
359 328
360Due 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.
361 331
362 my ($meta, $data) = @{ 332 my ($meta, $data) = @{
363 $fcp->client_get ( 333 $fcp->client_get (
364 "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w" 334 "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w"
365 ) 335 )
366 }; 336 };
367 337
368=cut 338=cut
369 339
370_txn client_get => sub { 340$txn->(client_get => sub {
371 my ($self, $uri, $htl, $removelocal) = @_; 341 my ($self, $uri, $htl, $removelocal) = @_;
372 342
373 $self->txn (client_get => URI => $uri, hops_to_live => ($htl || 15), remove_local_key => $removelocal ? "true" : "false"); 343 $uri =~ s/^freenet://; $uri = "freenet:$uri";
344
345 $self->txn (client_get => URI => $uri, hops_to_live => xeh (defined $htl ? $htl : 15),
346 remove_local_key => $removelocal ? "true" : "false");
374}; 347});
375 348
376=item MISSING: ClientPut 349=item $txn = $fcp->txn_client_put ($uri, $metadata, $data, $htl, $removelocal)
350
351=item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
352
353Insert a new key. If the client is inserting a CHK, the URI may be
354abbreviated as just CHK@. In this case, the node will calculate the
355CHK. If the key is a private SSK key, the node will calculcate the public
356key and the resulting public URI.
357
358C<$meta> can be a hash reference (same format as returned by
359C<Net::FCP::parse_metadata>) or a string.
360
361The result is an arrayref with the keys C<uri>, C<public_key> and C<private_key>.
362
363=cut
364
365$txn->(client_put => sub {
366 my ($self, $uri, $metadata, $data, $htl, $removelocal) = @_;
367
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),
373 remove_local_key => $removelocal ? "true" : "false",
374 data => "$metadata$data", metadata_length => xeh length $metadata);
375});
376
377} # transactions
377 378
378=back 379=back
379 380
380=head2 THE Net::FCP::Txn CLASS 381=head2 THE Net::FCP::Txn CLASS
381 382
382All requests (or transactions) are executed in a asynchroneous way (LIE: 383All requests (or transactions) are executed in a asynchronous way. For
383uploads 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
384created (worse: a tcp connection is created, too). 385connection is created, too).
385 386
386For 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
387to subclass these, although of course not documented). 388to subclass these, although of course not documented).
388 389
389The most interesting method is C<result>. 390The most interesting method is C<result>.
417 while (my ($k, $v) = each %{$self->{attr}}) { 418 while (my ($k, $v) = each %{$self->{attr}}) {
418 $attr .= (Net::FCP::touc $k) . "=$v\012" 419 $attr .= (Net::FCP::touc $k) . "=$v\012"
419 } 420 }
420 421
421 if (defined $data) { 422 if (defined $data) {
422 $attr .= "DataLength=" . (length $data) . "\012"; 423 $attr .= sprintf "DataLength=%x\012", length $data;
423 $data = "Data\012$data"; 424 $data = "Data\012$data";
424 } else { 425 } else {
425 $data = "EndMessage\012"; 426 $data = "EndMessage\012";
426 } 427 }
427 428
434 and !$!{EINPROGRESS} 435 and !$!{EINPROGRESS}
435 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n"; 436 and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n";
436 437
437 $self->{sbuf} = 438 $self->{sbuf} =
438 "\x00\x00\x00\x02" 439 "\x00\x00\x00\x02"
439 . Net::FCP::touc $self->{type} 440 . (Net::FCP::touc $self->{type})
440 . "\012$attr$data"; 441 . "\012$attr$data";
441 442
442 #$fh->shutdown (1); # freenet buggy?, well, it's java... 443 #shutdown $fh, 1; # freenet buggy?, well, it's java...
443 444
444 $self->{fh} = $fh; 445 $self->{fh} = $fh;
445 446
446 $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)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1);
447 448
483 484
484sub userdata($$) { 485sub userdata($$) {
485 my ($self, $data) = @_; 486 my ($self, $data) = @_;
486 $self->{userdata} = $data; 487 $self->{userdata} = $data;
487 $self; 488 $self;
489}
490
491=item $txn->cancel (%attr)
492
493Cancels the operation with a C<cancel> exception anf the given attributes
494(consider at least giving the attribute C<reason>).
495
496UNTESTED.
497
498=cut
499
500sub cancel {
501 my ($self, %attr) = @_;
502 $self->throw (Net::FCP::Exception->new (cancel => { %attr }));
503 $self->set_result;
504 $self->eof;
488} 505}
489 506
490sub fh_ready_w { 507sub fh_ready_w {
491 my ($self) = @_; 508 my ($self) = @_;
492 509
532 } else { 549 } else {
533 $self->eof; 550 $self->eof;
534 } 551 }
535} 552}
536 553
537sub rcv_data {
538 my ($self, $chunk) = @_;
539
540 $self->{data} .= $chunk;
541
542 $self->progress ("data", { chunk => length $chunk, total => length $self->{data}, end => $self->{datalength} });
543}
544
545sub rcv { 554sub rcv {
546 my ($self, $type, $attr) = @_; 555 my ($self, $type, $attr) = @_;
547 556
548 $type = Net::FCP::tolc $type; 557 $type = Net::FCP::tolc $type;
549 558
557} 566}
558 567
559# used as a default exception thrower 568# used as a default exception thrower
560sub rcv_throw_exception { 569sub rcv_throw_exception {
561 my ($self, $attr, $type) = @_; 570 my ($self, $attr, $type) = @_;
562 $self->throw (new Net::FCP::Exception $type, $attr); 571 $self->throw (Net::FCP::Exception->new ($type, $attr));
563} 572}
564 573
565*rcv_failed = \&Net::FCP::Txn::rcv_throw_exception; 574*rcv_failed = \&Net::FCP::Txn::rcv_throw_exception;
566*rcv_format_error = \&Net::FCP::Txn::rcv_throw_exception; 575*rcv_format_error = \&Net::FCP::Txn::rcv_throw_exception;
567 576
568sub throw { 577sub throw {
569 my ($self, $exc) = @_; 578 my ($self, $exc) = @_;
570 579
571 $self->{exception} = $exc; 580 $self->{exception} = $exc;
572 $self->set_result (1); 581 $self->set_result;
573 $self->eof; # must be last to avoid loops 582 $self->eof; # must be last to avoid loops
574} 583}
575 584
576sub set_result { 585sub set_result {
577 my ($self, $result) = @_; 586 my ($self, $result) = @_;
589 delete $self->{w}; 598 delete $self->{w};
590 delete $self->{fh}; 599 delete $self->{fh};
591 600
592 delete $self->{fcp}{txn}{$self}; 601 delete $self->{fcp}{txn}{$self};
593 602
594 $self->set_result; # just in case 603 unless (exists $self->{result}) {
604 $self->throw (Net::FCP::Exception->new (short_data => {
605 reason => "unexpected eof or internal node error",
606 }));
607 }
595} 608}
596 609
597sub progress { 610sub progress {
598 my ($self, $type, $attr) = @_; 611 my ($self, $type, $attr) = @_;
612
599 $self->{fcp}->progress ($self, $type, $attr); 613 $self->{fcp}->progress ($self, $type, $attr);
600} 614}
601 615
602=item $result = $txn->result 616=item $result = $txn->result
603 617
604Waits until a result is available and then returns it. 618Waits until a result is available and then returns it.
605 619
606This waiting is (depending on your event model) not very efficient, as it 620This waiting is (depending on your event model) not very efficient, as it
607is done outside the "mainloop". 621is done outside the "mainloop". The biggest problem, however, is that it's
622blocking one thread of execution. Try to use the callback mechanism, if
623possible, and call result from within the callback (or after is has been
624run), as then no waiting is necessary.
608 625
609=cut 626=cut
610 627
611sub result { 628sub result {
612 my ($self) = @_; 629 my ($self) = @_;
643use base Net::FCP::Txn; 660use base Net::FCP::Txn;
644 661
645sub rcv_success { 662sub rcv_success {
646 my ($self, $attr) = @_; 663 my ($self, $attr) = @_;
647 664
648 $self->set_result ($attr); 665 $self->set_result ($attr->{uri});
649} 666}
650 667
651package Net::FCP::Txn::GenerateSVKPair; 668package Net::FCP::Txn::GenerateSVKPair;
652 669
653use base Net::FCP::Txn; 670use base Net::FCP::Txn;
654 671
655sub rcv_success { 672sub rcv_success {
656 my ($self, $attr) = @_; 673 my ($self, $attr) = @_;
657
658 $self->set_result ([$attr->{PublicKey}, $attr->{PrivateKey}]); 674 $self->set_result ([$attr->{public_key}, $attr->{private_key}, $attr->{crypto_key}]);
659} 675}
660 676
661package Net::FCP::Txn::InvertPrivateKey; 677package Net::FCP::Txn::InvertPrivateKey;
662 678
663use base Net::FCP::Txn; 679use base Net::FCP::Txn;
664 680
665sub rcv_success { 681sub rcv_success {
666 my ($self, $attr) = @_; 682 my ($self, $attr) = @_;
667
668 $self->set_result ($attr->{PublicKey}); 683 $self->set_result ($attr->{public_key});
669} 684}
670 685
671package Net::FCP::Txn::GetSize; 686package Net::FCP::Txn::GetSize;
672 687
673use base Net::FCP::Txn; 688use base Net::FCP::Txn;
674 689
675sub rcv_success { 690sub rcv_success {
676 my ($self, $attr) = @_; 691 my ($self, $attr) = @_;
677
678 $self->set_result ($attr->{Length}); 692 $self->set_result (hex $attr->{length});
679} 693}
680 694
681package Net::FCP::Txn::GetPut; 695package Net::FCP::Txn::GetPut;
682 696
683# base class for get and put 697# base class for get and put
684 698
685use base Net::FCP::Txn; 699use base Net::FCP::Txn;
686 700
687*rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception; 701*rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception;
688*rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception; 702*rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception;
689 703
690sub rcv_restarted { 704sub rcv_restarted {
691 my ($self, $attr, $type) = @_; 705 my ($self, $attr, $type) = @_;
692 706
693 delete $self->{datalength}; 707 delete $self->{datalength};
701 715
702use base Net::FCP::Txn::GetPut; 716use base Net::FCP::Txn::GetPut;
703 717
704*rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception; 718*rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception;
705 719
720sub rcv_data {
721 my ($self, $chunk) = @_;
722
723 $self->{data} .= $chunk;
724
725 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} });
726
727 if ($self->{datalength} == length $self->{data}) {
728 my $data = delete $self->{data};
729 my $meta = new Net::FCP::Metadata (substr $data, 0, $self->{metalength}, "");
730
731 $self->set_result ([$meta, $data]);
732 $self->eof;
733 }
734}
735
706sub rcv_data_found { 736sub rcv_data_found {
707 my ($self, $attr, $type) = @_; 737 my ($self, $attr, $type) = @_;
708 738
709 $self->progress ($type, $attr); 739 $self->progress ($type, $attr);
710 740
711 $self->{datalength} = hex $attr->{data_length}; 741 $self->{datalength} = hex $attr->{data_length};
712 $self->{metalength} = hex $attr->{metadata_length}; 742 $self->{metalength} = hex $attr->{metadata_length};
713} 743}
714 744
715sub eof {
716 my ($self) = @_;
717
718 if ($self->{datalength} == length $self->{data}) {
719 my $data = delete $self->{data};
720 my $meta = Net::FCP::parse_metadata substr $data, 0, $self->{metalength}, "";
721
722 $self->set_result ([$meta, $data]);
723 } elsif (!exists $self->{result}) {
724 $self->throw (Net::FCP::Exception->new (short_data => {
725 reason => "unexpected eof or internal node error",
726 received => length $self->{data},
727 expected => $self->{datalength},
728 }));
729 }
730}
731
732package Net::FCP::Txn::ClientPut; 745package Net::FCP::Txn::ClientPut;
733 746
734use base Net::FCP::Txn::GetPut; 747use base Net::FCP::Txn::GetPut;
735 748
736*rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception; 749*rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception;
737*rcv_key_collision = \&Net::FCP::Txn::rcv_throw_exception;
738 750
739sub rcv_pending { 751sub rcv_pending {
740 my ($self, $attr, $type) = @_; 752 my ($self, $attr, $type) = @_;
741 $self->progress ($type, $attr); 753 $self->progress ($type, $attr);
742} 754}
744sub rcv_success { 756sub rcv_success {
745 my ($self, $attr, $type) = @_; 757 my ($self, $attr, $type) = @_;
746 $self->set_result ($attr); 758 $self->set_result ($attr);
747} 759}
748 760
761sub rcv_key_collision {
762 my ($self, $attr, $type) = @_;
763 $self->set_result ({ key_collision => 1, %$attr });
764}
765
766=back
767
768=head2 The Net::FCP::Exception CLASS
769
770Any unexpected (non-standard) responses that make it impossible to return
771the advertised result will result in an exception being thrown when the
772C<result> method is called.
773
774These exceptions are represented by objects of this class.
775
776=over 4
777
778=cut
779
749package Net::FCP::Exception; 780package Net::FCP::Exception;
750 781
751use overload 782use overload
752 '""' => sub { 783 '""' => sub {
753 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>\n"; 784 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>";
754 }; 785 };
786
787=item $exc = new Net::FCP::Exception $type, \%attr
788
789Create a new exception object of the given type (a string like
790C<route_not_found>), and a hashref containing additional attributes
791(usually the attributes of the message causing the exception).
792
793=cut
755 794
756sub new { 795sub new {
757 my ($class, $type, $attr) = @_; 796 my ($class, $type, $attr) = @_;
758 797
759 bless [Net::FCP::tolc $type, { %$attr }], $class; 798 bless [Net::FCP::tolc $type, { %$attr }], $class;
760} 799}
761 800
801=item $exc->type([$type])
802
803With no arguments, returns the exception type. Otherwise a boolean
804indicating wether the exception is of the given type is returned.
805
806=cut
807
808sub type {
809 my ($self, $type) = @_;
810
811 @_ >= 2
812 ? $self->[0] eq $type
813 : $self->[0];
814}
815
816=item $exc->attr([$attr])
817
818With no arguments, returns the attributes. Otherwise the named attribute
819value is returned.
820
821=cut
822
823sub attr {
824 my ($self, $attr) = @_;
825
826 @_ >= 2
827 ? $self->[1]{$attr}
828 : $self->[1];
829}
830
762=back 831=back
763 832
764=head1 SEE ALSO 833=head1 SEE ALSO
765 834
766L<http://freenet.sf.net>. 835L<http://freenet.sf.net>.
772 Marc Lehmann <pcg@goof.com> 841 Marc Lehmann <pcg@goof.com>
773 http://www.goof.com/pcg/marc/ 842 http://www.goof.com/pcg/marc/
774 843
775=cut 844=cut
776 845
846package Net::FCP::Event::Auto;
847
848my @models = (
849 [Coro => Coro::Event::],
850 [Event => Event::],
851 [Glib => Glib::],
852 [Tk => Tk::],
853);
854
855sub AUTOLOAD {
856 $AUTOLOAD =~ s/.*://;
857
858 for (@models) {
859 my ($model, $package) = @$_;
860 if (defined ${"$package\::VERSION"}) {
861 $EVENT = "Net::FCP::Event::$model";
862 eval "require $EVENT"; die if $@;
863 goto &{"$EVENT\::$AUTOLOAD"};
864 }
865 }
866
867 for (@models) {
868 my ($model, $package) = @$_;
869 $EVENT = "Net::FCP::Event::$model";
870 if (eval "require $EVENT") {
871 goto &{"$EVENT\::$AUTOLOAD"};
872 }
873 }
874
875 die "No event module selected for Net::FCP and autodetect failed. Install any of these: Coro, Event, Glib or Tk.";
876}
877
7771; 8781;
778 879

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines