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