ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-FCP/FCP.pm
Revision: 1.30
Committed: Fri May 14 16:12:26 2004 UTC (22 years, 4 months ago) by root
Branch: MAIN
Changes since 1.29: +24 -149 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     Net::FCP - http://freenet.sf.net client protocol
4    
5     =head1 SYNOPSIS
6    
7     use Net::FCP;
8    
9     my $fcp = new Net::FCP;
10    
11     my $ni = $fcp->txn_node_info->result;
12     my $ni = $fcp->node_info;
13    
14     =head1 DESCRIPTION
15    
16     See L<http://freenet.sourceforge.net/index.php?page=fcp> for a description
17     of what the messages do. I am too lazy to document all this here.
18    
19     =head1 WARNING
20    
21     This module is alpha. While it probably won't destroy (much :) of your
22 root 1.9 data, it currently falls short of what it should provide (intelligent uri
23     following, splitfile downloads, healing...)
24    
25     =head2 IMPORT TAGS
26    
27     Nothing much can be "imported" from this module right now. There are,
28     however, certain "import tags" that can be used to select the event model
29     to be used.
30    
31     Event models are implemented as modules under the C<Net::FCP::Event::xyz>
32     class, where C<xyz> is the event model to use. The default is C<Event> (or
33     later C<Auto>).
34    
35     The import tag to use is named C<event=xyz>, e.g. C<event=Event>,
36     C<event=Glib> etc.
37    
38     You should specify the event module to use only in the main program.
39 root 1.1
40 root 1.20 If no event model has been specified, FCP tries to autodetect it on first
41     use (e.g. first transaction), in this order: Coro, Event, Glib, Tk.
42    
43 root 1.17 =head2 FREENET BASICS
44    
45     Ok, this section will not explain any freenet basics to you, just some
46     problems I found that you might want to avoid:
47    
48     =over 4
49    
50     =item freenet URIs are _NOT_ URIs
51    
52     Whenever a "uri" is required by the protocol, freenet expects a kind of
53     URI prefixed with the "freenet:" scheme, e.g. "freenet:CHK...". However,
54     these are not URIs, as freeent fails to parse them correctly, that is, you
55     must unescape an escaped characters ("%2c" => ",") yourself. Maybe in the
56     future this library will do it for you, so watch out for this incompatible
57     change.
58    
59     =item Numbers are in HEX
60    
61     Virtually every number in the FCP protocol is in hex. Be sure to use
62     C<hex()> on all such numbers, as the module (currently) does nothing to
63     convert these for you.
64    
65     =back
66    
67 root 1.1 =head2 THE Net::FCP CLASS
68    
69     =over 4
70    
71     =cut
72    
73     package Net::FCP;
74    
75     use Carp;
76    
77 root 1.30 $VERSION = 0.7;
78 root 1.10
79     no warnings;
80 root 1.1
81 root 1.30 use Net::FCP::Metadata;
82    
83 root 1.9 our $EVENT = Net::FCP::Event::Auto::;
84 root 1.1
85 root 1.9 sub import {
86     shift;
87 root 1.1
88 root 1.9 for (@_) {
89     if (/^event=(\w+)$/) {
90     $EVENT = "Net::FCP::Event::$1";
91 root 1.20 eval "require $EVENT";
92 root 1.9 }
93     }
94 root 1.12 die $@ if $@;
95 root 1.1 }
96    
97 root 1.2 sub touc($) {
98     local $_ = shift;
99     1 while s/((?:^|_)(?:svk|chk|uri)(?:_|$))/\U$1/;
100     s/(?:^|_)(.)/\U$1/g;
101     $_;
102     }
103    
104     sub tolc($) {
105     local $_ = shift;
106 root 1.27 1 while s/(SVK|CHK|URI)([^_])/$1\_$2/i;
107     1 while s/([^_])(SVK|CHK|URI)/$1\_$2/i;
108 root 1.2 s/(?<=[a-z])(?=[A-Z])/_/g;
109     lc $_;
110     }
111    
112 root 1.23 # the opposite of hex
113     sub xeh($) {
114     sprintf "%x", $_[0];
115     }
116    
117 root 1.27 =item $fcp = new Net::FCP [host => $host][, port => $port][, progress => \&cb]
118 root 1.1
119     Create a new virtual FCP connection to the given host and port (default
120 root 1.5 127.0.0.1:8481, or the environment variables C<FREDHOST> and C<FREDPORT>).
121 root 1.1
122     Connections are virtual because no persistent physical connection is
123 root 1.17 established.
124    
125 root 1.27 You can install a progress callback that is being called with the Net::FCP
126     object, a txn object, the type of the transaction and the attributes. Use
127     it like this:
128    
129     sub progress_cb {
130     my ($self, $txn, $type, $attr) = @_;
131    
132     warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
133     }
134    
135 root 1.1 =cut
136    
137     sub new {
138     my $class = shift;
139     my $self = bless { @_ }, $class;
140    
141 root 1.5 $self->{host} ||= $ENV{FREDHOST} || "127.0.0.1";
142 root 1.12 $self->{port} ||= $ENV{FREDPORT} || 8481;
143 root 1.1
144     $self;
145     }
146    
147 root 1.9 sub progress {
148     my ($self, $txn, $type, $attr) = @_;
149 root 1.27
150     $self->{progress}->($self, $txn, $type, $attr)
151     if $self->{progress};
152 root 1.9 }
153    
154 root 1.30 =item $txn = $fcp->txn (type => attr => val,...)
155 root 1.1
156 root 1.30 The low-level interface to transactions. Don't use it unless you have
157     "special needs". Instead, use predefiend transactions like this:
158 root 1.12
159     The blocking case, no (visible) transactions involved:
160    
161     my $nodehello = $fcp->client_hello;
162    
163     A transaction used in a blocking fashion:
164    
165     my $txn = $fcp->txn_client_hello;
166     ...
167     my $nodehello = $txn->result;
168    
169     Or shorter:
170    
171     my $nodehello = $fcp->txn_client_hello->result;
172    
173     Setting callbacks:
174    
175     $fcp->txn_client_hello->cb(
176     sub { my $nodehello => $_[0]->result }
177     );
178    
179 root 1.1 =cut
180    
181     sub txn {
182     my ($self, $type, %attr) = @_;
183    
184 root 1.2 $type = touc $type;
185    
186 root 1.29 my $txn = "Net::FCP::Txn::$type"->new (fcp => $self, type => tolc $type, attr => \%attr);
187 root 1.1
188     $txn;
189     }
190    
191 root 1.17 { # transactions
192    
193     my $txn = sub {
194 root 1.1 my ($name, $sub) = @_;
195 root 1.17 *{"txn_$name"} = $sub;
196 root 1.1 *{$name} = sub { $sub->(@_)->result };
197 root 1.17 };
198 root 1.1
199     =item $txn = $fcp->txn_client_hello
200    
201     =item $nodehello = $fcp->client_hello
202    
203     Executes a ClientHello request and returns it's results.
204    
205     {
206 root 1.2 max_file_size => "5f5e100",
207 root 1.4 node => "Fred,0.6,1.46,7050"
208 root 1.2 protocol => "1.2",
209 root 1.1 }
210    
211     =cut
212    
213 root 1.17 $txn->(client_hello => sub {
214 root 1.1 my ($self) = @_;
215    
216 root 1.2 $self->txn ("client_hello");
217 root 1.17 });
218 root 1.1
219     =item $txn = $fcp->txn_client_info
220    
221     =item $nodeinfo = $fcp->client_info
222    
223     Executes a ClientInfo request and returns it's results.
224    
225     {
226 root 1.2 active_jobs => "1f",
227     allocated_memory => "bde0000",
228     architecture => "i386",
229     available_threads => 17,
230 root 1.4 datastore_free => "5ce03400",
231     datastore_max => "2540be400",
232 root 1.2 datastore_used => "1f72bb000",
233 root 1.4 estimated_load => 52,
234     free_memory => "5cc0148",
235 root 1.2 is_transient => "false",
236 root 1.4 java_name => "Java HotSpot(_T_M) Server VM",
237 root 1.2 java_vendor => "http://www.blackdown.org/",
238 root 1.4 java_version => "Blackdown-1.4.1-01",
239     least_recent_timestamp => "f41538b878",
240     max_file_size => "5f5e100",
241 root 1.2 most_recent_timestamp => "f77e2cc520"
242 root 1.4 node_address => "1.2.3.4",
243     node_port => 369,
244     operating_system => "Linux",
245     operating_system_version => "2.4.20",
246     routing_time => "a5",
247 root 1.1 }
248    
249     =cut
250    
251 root 1.17 $txn->(client_info => sub {
252 root 1.1 my ($self) = @_;
253    
254 root 1.2 $self->txn ("client_info");
255 root 1.17 });
256 root 1.1
257 root 1.21 =item $txn = $fcp->txn_generate_chk ($metadata, $data[, $cipher])
258 root 1.1
259 root 1.21 =item $uri = $fcp->generate_chk ($metadata, $data[, $cipher])
260 root 1.1
261 root 1.27 Calculates a CHK, given the metadata and data. C<$cipher> is either
262 root 1.21 C<Rijndael> or C<Twofish>, with the latter being the default.
263 root 1.1
264     =cut
265    
266 root 1.17 $txn->(generate_chk => sub {
267 root 1.21 my ($self, $metadata, $data, $cipher) = @_;
268 root 1.1
269 root 1.30 $metadata = Net::FCP::Metadata::build_metadata $metadata;
270    
271 root 1.21 $self->txn (generate_chk =>
272 root 1.30 data => "$metadata$data",
273     metadata_length => xeh length $metadata,
274     cipher => $cipher || "Twofish");
275 root 1.17 });
276 root 1.1
277     =item $txn = $fcp->txn_generate_svk_pair
278    
279     =item ($public, $private) = @{ $fcp->generate_svk_pair }
280    
281 root 1.29 Creates a new SVK pair. Returns an arrayref with the public key, the
282     private key and a crypto key, which is just additional entropy.
283 root 1.1
284     [
285 root 1.29 "acLx4dux9fvvABH15Gk6~d3I-yw",
286     "cPoDkDMXDGSMM32plaPZDhJDxSs",
287     "BH7LXCov0w51-y9i~BoB3g",
288 root 1.1 ]
289    
290 root 1.29 A private key (for inserting) can be constructed like this:
291    
292     SSK@<private_key>,<crypto_key>/<name>
293    
294     It can be used to insert data. The corresponding public key looks like this:
295    
296     SSK@<public_key>PAgM,<crypto_key>/<name>
297    
298     Watch out for the C<PAgM>-part!
299    
300 root 1.1 =cut
301    
302 root 1.17 $txn->(generate_svk_pair => sub {
303 root 1.1 my ($self) = @_;
304    
305 root 1.2 $self->txn ("generate_svk_pair");
306 root 1.17 });
307 root 1.1
308 root 1.29 =item $txn = $fcp->txn_invert_private_key ($private)
309 root 1.1
310 root 1.29 =item $public = $fcp->invert_private_key ($private)
311 root 1.1
312 root 1.29 Inverts a private key (returns the public key). C<$private> can be either
313     an insert URI (must start with C<freenet:SSK@>) or a raw private key (i.e.
314     the private value you get back from C<generate_svk_pair>).
315 root 1.1
316     Returns the public key.
317    
318     =cut
319    
320 root 1.29 $txn->(invert_private_key => sub {
321 root 1.1 my ($self, $privkey) = @_;
322    
323 root 1.2 $self->txn (invert_private_key => private => $privkey);
324 root 1.17 });
325 root 1.1
326     =item $txn = $fcp->txn_get_size ($uri)
327    
328     =item $length = $fcp->get_size ($uri)
329    
330     Finds and returns the size (rounded up to the nearest power of two) of the
331     given document.
332    
333     =cut
334    
335 root 1.17 $txn->(get_size => sub {
336 root 1.1 my ($self, $uri) = @_;
337    
338 root 1.2 $self->txn (get_size => URI => $uri);
339 root 1.17 });
340 root 1.1
341 root 1.5 =item $txn = $fcp->txn_client_get ($uri [, $htl = 15 [, $removelocal = 0]])
342    
343 root 1.7 =item ($metadata, $data) = @{ $fcp->client_get ($uri, $htl, $removelocal)
344 root 1.5
345 root 1.30 Fetches a (small, as it should fit into memory) key content block from
346     freenet. C<$meta> is a C<Net::FCP::Metadata> object or C<undef>).
347 root 1.5
348 root 1.27 The C<$uri> should begin with C<freenet:>, but the scheme is currently
349     added, if missing.
350    
351 root 1.7 my ($meta, $data) = @{
352 root 1.5 $fcp->client_get (
353     "freenet:CHK@hdXaxkwZ9rA8-SidT0AN-bniQlgPAwI,XdCDmBuGsd-ulqbLnZ8v~w"
354     )
355     };
356    
357     =cut
358    
359 root 1.17 $txn->(client_get => sub {
360 root 1.5 my ($self, $uri, $htl, $removelocal) = @_;
361    
362 root 1.30 $uri =~ s/^freenet://; $uri = "freenet:$uri";
363 root 1.26
364 root 1.23 $self->txn (client_get => URI => $uri, hops_to_live => xeh (defined $htl ? $htl : 15),
365 root 1.17 remove_local_key => $removelocal ? "true" : "false");
366     });
367    
368     =item $txn = $fcp->txn_client_put ($uri, $metadata, $data, $htl, $removelocal)
369    
370     =item my $uri = $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
371    
372     Insert a new key. If the client is inserting a CHK, the URI may be
373     abbreviated as just CHK@. In this case, the node will calculate the
374 root 1.29 CHK. If the key is a private SSK key, the node will calculcate the public
375     key and the resulting public URI.
376 root 1.17
377 root 1.29 C<$meta> can be a hash reference (same format as returned by
378     C<Net::FCP::parse_metadata>) or a string.
379 root 1.17
380 root 1.29 The result is an arrayref with the keys C<uri>, C<public_key> and C<private_key>.
381 root 1.17
382     =cut
383    
384     $txn->(client_put => sub {
385 root 1.30 my ($self, $uri, $metadata, $data, $htl, $removelocal) = @_;
386 root 1.17
387 root 1.30 $metadata = Net::FCP::Metadata::build_metadata $metadata;
388     $uri =~ s/^freenet://; $uri = "freenet:$uri";
389 root 1.29
390     $self->txn (client_put => URI => $uri,
391     hops_to_live => xeh (defined $htl ? $htl : 15),
392 root 1.17 remove_local_key => $removelocal ? "true" : "false",
393 root 1.30 data => "$metadata$data", metadata_length => xeh length $metadata);
394 root 1.17 });
395    
396     } # transactions
397 root 1.5
398 root 1.1 =back
399    
400     =head2 THE Net::FCP::Txn CLASS
401    
402 root 1.23 All requests (or transactions) are executed in a asynchronous way. For
403     each request, a C<Net::FCP::Txn> object is created (worse: a tcp
404     connection is created, too).
405 root 1.1
406     For each request there is actually a different subclass (and it's possible
407     to subclass these, although of course not documented).
408    
409     The most interesting method is C<result>.
410    
411     =over 4
412    
413     =cut
414    
415     package Net::FCP::Txn;
416    
417 root 1.12 use Fcntl;
418     use Socket;
419    
420 root 1.1 =item new arg => val,...
421    
422     Creates a new C<Net::FCP::Txn> object. Not normally used.
423    
424     =cut
425    
426     sub new {
427     my $class = shift;
428     my $self = bless { @_ }, $class;
429    
430 root 1.12 $self->{signal} = $EVENT->new_signal;
431    
432     $self->{fcp}{txn}{$self} = $self;
433    
434 root 1.1 my $attr = "";
435     my $data = delete $self->{attr}{data};
436    
437     while (my ($k, $v) = each %{$self->{attr}}) {
438 root 1.2 $attr .= (Net::FCP::touc $k) . "=$v\012"
439 root 1.1 }
440    
441     if (defined $data) {
442 root 1.21 $attr .= sprintf "DataLength=%x\012", length $data;
443 root 1.1 $data = "Data\012$data";
444     } else {
445     $data = "EndMessage\012";
446     }
447    
448 root 1.12 socket my $fh, PF_INET, SOCK_STREAM, 0
449     or Carp::croak "unable to create new tcp socket: $!";
450 root 1.1 binmode $fh, ":raw";
451 root 1.12 fcntl $fh, F_SETFL, O_NONBLOCK;
452     connect $fh, (sockaddr_in $self->{fcp}{port}, inet_aton $self->{fcp}{host})
453     and !$!{EWOULDBLOCK}
454     and !$!{EINPROGRESS}
455     and Carp::croak "FCP::txn: unable to connect to $self->{fcp}{host}:$self->{fcp}{port}: $!\n";
456    
457     $self->{sbuf} =
458     "\x00\x00\x00\x02"
459 root 1.21 . (Net::FCP::touc $self->{type})
460 root 1.12 . "\012$attr$data";
461 root 1.1
462 root 1.21 #shutdown $fh, 1; # freenet buggy?, well, it's java...
463 root 1.1
464     $self->{fh} = $fh;
465    
466 root 1.12 $self->{w} = $EVENT->new_from_fh ($fh)->cb(sub { $self->fh_ready_w })->poll(0, 1, 1);
467 root 1.1
468     $self;
469     }
470    
471 root 1.12 =item $txn = $txn->cb ($coderef)
472    
473     Sets a callback to be called when the request is finished. The coderef
474     will be called with the txn as it's sole argument, so it has to call
475     C<result> itself.
476    
477     Returns the txn object, useful for chaining.
478 root 1.9
479 root 1.12 Example:
480    
481     $fcp->txn_client_get ("freenet:CHK....")
482     ->userdata ("ehrm")
483     ->cb(sub {
484     my $data = shift->result;
485     });
486 root 1.9
487     =cut
488    
489 root 1.12 sub cb($$) {
490     my ($self, $cb) = @_;
491     $self->{cb} = $cb;
492     $self;
493     }
494    
495     =item $txn = $txn->userdata ([$userdata])
496    
497     Set user-specific data. This is useful in progress callbacks. The data can be accessed
498     using C<< $txn->{userdata} >>.
499    
500     Returns the txn object, useful for chaining.
501    
502     =cut
503    
504     sub userdata($$) {
505 root 1.9 my ($self, $data) = @_;
506 root 1.12 $self->{userdata} = $data;
507     $self;
508     }
509    
510 root 1.17 =item $txn->cancel (%attr)
511    
512     Cancels the operation with a C<cancel> exception anf the given attributes
513     (consider at least giving the attribute C<reason>).
514    
515     UNTESTED.
516    
517     =cut
518    
519     sub cancel {
520     my ($self, %attr) = @_;
521     $self->throw (Net::FCP::Exception->new (cancel => { %attr }));
522     $self->set_result;
523     $self->eof;
524     }
525    
526 root 1.12 sub fh_ready_w {
527     my ($self) = @_;
528    
529     my $len = syswrite $self->{fh}, $self->{sbuf};
530    
531     if ($len > 0) {
532     substr $self->{sbuf}, 0, $len, "";
533     unless (length $self->{sbuf}) {
534     fcntl $self->{fh}, F_SETFL, 0;
535     $self->{w}->cb(sub { $self->fh_ready_r })->poll (1, 0, 1);
536     }
537     } elsif (defined $len) {
538     $self->throw (Net::FCP::Exception->new (network_error => { reason => "unexpected end of file while writing" }));
539     } else {
540     $self->throw (Net::FCP::Exception->new (network_error => { reason => "$!" }));
541     }
542 root 1.9 }
543    
544 root 1.12 sub fh_ready_r {
545 root 1.1 my ($self) = @_;
546    
547     if (sysread $self->{fh}, $self->{buf}, 65536, length $self->{buf}) {
548     for (;;) {
549     if ($self->{datalen}) {
550 root 1.13 #warn "expecting new datachunk $self->{datalen}, got ".(length $self->{buf})."\n";#d#
551 root 1.1 if (length $self->{buf} >= $self->{datalen}) {
552 root 1.11 $self->rcv_data (substr $self->{buf}, 0, delete $self->{datalen}, "");
553 root 1.1 } else {
554     last;
555     }
556 root 1.5 } elsif ($self->{buf} =~ s/^DataChunk\015?\012Length=([0-9a-fA-F]+)\015?\012Data\015?\012//) {
557     $self->{datalen} = hex $1;
558 root 1.13 #warn "expecting new datachunk $self->{datalen}\n";#d#
559 root 1.7 } elsif ($self->{buf} =~ s/^([a-zA-Z]+)\015?\012(?:(.+?)\015?\012)?EndMessage\015?\012//s) {
560 root 1.2 $self->rcv ($1, {
561     map { my ($a, $b) = split /=/, $_, 2; ((Net::FCP::tolc $a), $b) }
562     split /\015?\012/, $2
563     });
564 root 1.1 } else {
565     last;
566     }
567     }
568     } else {
569     $self->eof;
570     }
571     }
572    
573     sub rcv {
574     my ($self, $type, $attr) = @_;
575    
576 root 1.2 $type = Net::FCP::tolc $type;
577    
578 root 1.5 #use PApp::Util; warn PApp::Util::dumpval [$type, $attr];
579    
580 root 1.2 if (my $method = $self->can("rcv_$type")) {
581 root 1.1 $method->($self, $attr, $type);
582     } else {
583     warn "received unexpected reply type '$type' for '$self->{type}', ignoring\n";
584     }
585     }
586    
587 root 1.12 # used as a default exception thrower
588     sub rcv_throw_exception {
589     my ($self, $attr, $type) = @_;
590 root 1.15 $self->throw (Net::FCP::Exception->new ($type, $attr));
591 root 1.12 }
592    
593     *rcv_failed = \&Net::FCP::Txn::rcv_throw_exception;
594     *rcv_format_error = \&Net::FCP::Txn::rcv_throw_exception;
595    
596 root 1.9 sub throw {
597     my ($self, $exc) = @_;
598    
599     $self->{exception} = $exc;
600 root 1.17 $self->set_result;
601 root 1.12 $self->eof; # must be last to avoid loops
602 root 1.9 }
603    
604 root 1.5 sub set_result {
605 root 1.1 my ($self, $result) = @_;
606    
607 root 1.12 unless (exists $self->{result}) {
608     $self->{result} = $result;
609     $self->{cb}->($self) if exists $self->{cb};
610     $self->{signal}->send;
611     }
612 root 1.1 }
613    
614 root 1.5 sub eof {
615     my ($self) = @_;
616 root 1.12
617     delete $self->{w};
618     delete $self->{fh};
619    
620     delete $self->{fcp}{txn}{$self};
621    
622 root 1.17 unless (exists $self->{result}) {
623     $self->throw (Net::FCP::Exception->new (short_data => {
624     reason => "unexpected eof or internal node error",
625     }));
626     }
627 root 1.5 }
628    
629 root 1.9 sub progress {
630     my ($self, $type, $attr) = @_;
631 root 1.27
632 root 1.9 $self->{fcp}->progress ($self, $type, $attr);
633     }
634    
635 root 1.1 =item $result = $txn->result
636    
637     Waits until a result is available and then returns it.
638    
639 root 1.5 This waiting is (depending on your event model) not very efficient, as it
640 root 1.23 is done outside the "mainloop". The biggest problem, however, is that it's
641     blocking one thread of execution. Try to use the callback mechanism, if
642     possible, and call result from within the callback (or after is has been
643     run), as then no waiting is necessary.
644 root 1.1
645     =cut
646    
647     sub result {
648     my ($self) = @_;
649    
650 root 1.12 $self->{signal}->wait while !exists $self->{result};
651 root 1.9
652     die $self->{exception} if $self->{exception};
653 root 1.1
654     return $self->{result};
655     }
656    
657     package Net::FCP::Txn::ClientHello;
658    
659     use base Net::FCP::Txn;
660    
661 root 1.2 sub rcv_node_hello {
662 root 1.1 my ($self, $attr) = @_;
663    
664 root 1.5 $self->set_result ($attr);
665 root 1.1 }
666    
667     package Net::FCP::Txn::ClientInfo;
668    
669     use base Net::FCP::Txn;
670    
671 root 1.2 sub rcv_node_info {
672 root 1.1 my ($self, $attr) = @_;
673    
674 root 1.5 $self->set_result ($attr);
675 root 1.1 }
676    
677     package Net::FCP::Txn::GenerateCHK;
678    
679     use base Net::FCP::Txn;
680    
681     sub rcv_success {
682     my ($self, $attr) = @_;
683    
684 root 1.21 $self->set_result ($attr->{uri});
685 root 1.1 }
686    
687     package Net::FCP::Txn::GenerateSVKPair;
688    
689     use base Net::FCP::Txn;
690    
691     sub rcv_success {
692     my ($self, $attr) = @_;
693 root 1.29 $self->set_result ([$attr->{public_key}, $attr->{private_key}, $attr->{crypto_key}]);
694 root 1.1 }
695    
696 root 1.29 package Net::FCP::Txn::InvertPrivateKey;
697 root 1.1
698     use base Net::FCP::Txn;
699    
700     sub rcv_success {
701     my ($self, $attr) = @_;
702 root 1.29 $self->set_result ($attr->{public_key});
703 root 1.1 }
704    
705     package Net::FCP::Txn::GetSize;
706    
707     use base Net::FCP::Txn;
708    
709     sub rcv_success {
710     my ($self, $attr) = @_;
711 root 1.29 $self->set_result (hex $attr->{length});
712 root 1.5 }
713    
714 root 1.12 package Net::FCP::Txn::GetPut;
715    
716     # base class for get and put
717    
718     use base Net::FCP::Txn;
719    
720 root 1.27 *rcv_uri_error = \&Net::FCP::Txn::rcv_throw_exception;
721     *rcv_route_not_found = \&Net::FCP::Txn::rcv_throw_exception;
722 root 1.12
723     sub rcv_restarted {
724     my ($self, $attr, $type) = @_;
725    
726     delete $self->{datalength};
727     delete $self->{metalength};
728     delete $self->{data};
729    
730     $self->progress ($type, $attr);
731     }
732    
733 root 1.5 package Net::FCP::Txn::ClientGet;
734    
735 root 1.12 use base Net::FCP::Txn::GetPut;
736    
737     *rcv_data_not_found = \&Net::FCP::Txn::rcv_throw_exception;
738 root 1.5
739 root 1.17 sub rcv_data {
740     my ($self, $chunk) = @_;
741 root 1.9
742 root 1.17 $self->{data} .= $chunk;
743 root 1.5
744 root 1.19 $self->progress ("data", { chunk => length $chunk, received => length $self->{data}, total => $self->{datalength} });
745 root 1.9
746 root 1.12 if ($self->{datalength} == length $self->{data}) {
747     my $data = delete $self->{data};
748 root 1.30 my $meta = new Net::FCP::Metadata (substr $data, 0, $self->{metalength}, "");
749 root 1.12
750     $self->set_result ([$meta, $data]);
751 root 1.22 $self->eof;
752 root 1.12 }
753 root 1.9 }
754    
755 root 1.17 sub rcv_data_found {
756     my ($self, $attr, $type) = @_;
757    
758     $self->progress ($type, $attr);
759    
760     $self->{datalength} = hex $attr->{data_length};
761     $self->{metalength} = hex $attr->{metadata_length};
762     }
763    
764 root 1.12 package Net::FCP::Txn::ClientPut;
765 root 1.9
766 root 1.12 use base Net::FCP::Txn::GetPut;
767 root 1.9
768 root 1.12 *rcv_size_error = \&Net::FCP::Txn::rcv_throw_exception;
769 root 1.9
770 root 1.12 sub rcv_pending {
771 root 1.9 my ($self, $attr, $type) = @_;
772     $self->progress ($type, $attr);
773 root 1.5 }
774    
775 root 1.12 sub rcv_success {
776     my ($self, $attr, $type) = @_;
777     $self->set_result ($attr);
778 root 1.30 }
779    
780     sub rcv_key_collision {
781     my ($self, $attr, $type) = @_;
782     $self->set_result ({ key_collision => 1, %$attr });
783 root 1.9 }
784    
785 root 1.17 =back
786    
787     =head2 The Net::FCP::Exception CLASS
788    
789     Any unexpected (non-standard) responses that make it impossible to return
790     the advertised result will result in an exception being thrown when the
791     C<result> method is called.
792    
793     These exceptions are represented by objects of this class.
794    
795     =over 4
796    
797     =cut
798    
799 root 1.9 package Net::FCP::Exception;
800    
801     use overload
802     '""' => sub {
803 root 1.22 "Net::FCP::Exception<<$_[0][0]," . (join ":", %{$_[0][1]}) . ">>";
804 root 1.9 };
805    
806 root 1.17 =item $exc = new Net::FCP::Exception $type, \%attr
807    
808     Create a new exception object of the given type (a string like
809     C<route_not_found>), and a hashref containing additional attributes
810     (usually the attributes of the message causing the exception).
811    
812     =cut
813    
814 root 1.9 sub new {
815     my ($class, $type, $attr) = @_;
816    
817 root 1.12 bless [Net::FCP::tolc $type, { %$attr }], $class;
818 root 1.17 }
819    
820     =item $exc->type([$type])
821    
822     With no arguments, returns the exception type. Otherwise a boolean
823     indicating wether the exception is of the given type is returned.
824    
825     =cut
826    
827     sub type {
828     my ($self, $type) = @_;
829    
830     @_ >= 2
831     ? $self->[0] eq $type
832     : $self->[0];
833     }
834    
835     =item $exc->attr([$attr])
836    
837     With no arguments, returns the attributes. Otherwise the named attribute
838     value is returned.
839    
840     =cut
841    
842     sub attr {
843     my ($self, $attr) = @_;
844    
845     @_ >= 2
846     ? $self->[1]{$attr}
847     : $self->[1];
848 root 1.1 }
849    
850     =back
851    
852     =head1 SEE ALSO
853    
854     L<http://freenet.sf.net>.
855    
856     =head1 BUGS
857    
858     =head1 AUTHOR
859    
860     Marc Lehmann <pcg@goof.com>
861     http://www.goof.com/pcg/marc/
862    
863     =cut
864 root 1.20
865     package Net::FCP::Event::Auto;
866    
867     my @models = (
868 root 1.27 [Coro => Coro::Event::],
869 root 1.20 [Event => Event::],
870 root 1.27 [Glib => Glib::],
871 root 1.20 [Tk => Tk::],
872     );
873    
874     sub AUTOLOAD {
875     $AUTOLOAD =~ s/.*://;
876    
877     for (@models) {
878     my ($model, $package) = @$_;
879     if (defined ${"$package\::VERSION"}) {
880     $EVENT = "Net::FCP::Event::$model";
881     eval "require $EVENT"; die if $@;
882     goto &{"$EVENT\::$AUTOLOAD"};
883     }
884     }
885    
886     for (@models) {
887     my ($model, $package) = @$_;
888     $EVENT = "Net::FCP::Event::$model";
889     if (eval "require $EVENT") {
890     goto &{"$EVENT\::$AUTOLOAD"};
891     }
892     }
893    
894     die "No event module selected for Net::FCP and autodetect failed. Install any of these: Coro, Event, Glib or Tk.";
895     }
896 root 1.1
897     1;
898