Author: alexlehm
Date: 2007-04-01 00:50:14 +0000 (Sun, 01 Apr 2007)
New Revision: 12505
Added:
trunk/apps/perlFreenet/Freenet/
trunk/apps/perlFreenet/Freenet/Connection.pm
trunk/apps/perlFreenet/Freenet/Message.pm
trunk/apps/perlFreenet/README
trunk/apps/perlFreenet/perlfn.pl
trunk/apps/perlFreenet/putcomplexdir.pl
Log:
initial commit, added README
Added: trunk/apps/perlFreenet/Freenet/Connection.pm
===================================================================
--- trunk/apps/perlFreenet/Freenet/Connection.pm
(rev 0)
+++ trunk/apps/perlFreenet/Freenet/Connection.pm 2007-04-01 00:50:14 UTC
(rev 12505)
@@ -0,0 +1,196 @@
+package Freenet::Connection;
+
+use Freenet::Message;
+use IO::socket::INET;
+
+sub new {
+ my $class = shift;
+ my $self = {};
+ bless $self, $class;
+ $self->initialize(@_);
+ return $self;
+}
+
+# we expect a hash with options, right now we only use Node
+# and Client
+#
+# you can use a string with the node hostname
+# or nothing at all
+# Node defaults to hostname:9481
+# or to localhost:9481 if nothing is given.
+# Client name defaults to perlfn, but please choose a sensible name
+# since you can only connect with each client name once
+
+sub initialize
+{
+ my $self = shift;
+ my $options = shift;
+
+ my $node;
+ my $socket;
+ my $clientname;
+ my $debug=0;
+
+ if(defined($options)) {
+ if(ref($options)) {
+ $node=$options->{Node} || "localhost";
+ $clientname=$options->{Client};
+ $debug=$options->{Debug} || 0;
+ } else {
+ # assume it's a string otherwise
+ $node=$options;
+ }
+ }
+
+ $node||="localhost";
+ $clientname||="perlfn";
+
+ # TODO: does this work with IPv6?
+ if($node !~ /:\d+$/) {
+ $node.=":9481";
+ }
+
+ $self->{node}=$node;
+ $self->{clientname}=$clientname;
+ $self->{debug}=$debug;
+ $self->{socket}=undef;
+}
+
+sub debug
+{
+ return shift->{debug};
+}
+
+sub connect
+{
+ my $self=shift;
+
+ # connect to the node
+ $socket=IO::Socket::INET->new(PeerAddr => $self->{node});
+
+ if(!$socket) {
+ # failed for some reason TODO: should wrap this is a proper
error?
+ return undef;
+ }
+
+ $self->debug && print "connected to ".$self->{node}."\n";
+
+ $self->{socket}=$socket;
+
+ # now send a ClientHello
+ $msg=Freenet::Message->new("ClientHello",
+ {
+ ExpectedVersion => "2.0",
+ Name => $self->{clientname},
+ }
+ );
+
+ if(!($self->sendmessage($msg))) {
+ return undef;
+ }
+ $response=$self->getmessage();
+
+ # TODO: keep node's features somewhere so we can use it later
+
+ return $response;
+}
+
+sub getmessage
+{
+ my $self=shift;
+
+ my $socket=$self->{socket};
+
+ my $message;
+ my $headers;
+ my $data;
+
+ $_=<$socket>;
+ chomp;
+ $self->debug && print "<$_\n";
+
+ $message=$_;
+
+ while(1) {
+ $_=<$socket>;
+ if($_ eq "") {
+ warn "read empty message from socket, probably the socket was closed by
the node\n";
+ return undef;
+ }
+ chomp;
+ $self->debug && print "<$_\n";
+ last if /^EndMessage$/;
+ last if /^Data$/;
+ my($k,$v)=split(/=/,$_,2);
+ $headers->{$k}=$v;
+ }
+
+ if(/^Data$/) {
+ # handle data after message headers
+ my($dl)=$headers->{DataLength};
+ if(!defined($dl)) {
+ warn "warning: Message ends with Data line, but no DataLength. We will
probably break afterwards\n";
+ } else {
+ $self->debug && print "<data follows\n";
+ read($socket,$data,$dl);
+ $self->debug && print "<read data\n";
+ }
+ }
+
+ my($res)=Freenet::Message->new($message, $headers);
+ if(defined($data)) {
+ $res->{data}=$data;
+ }
+
+ return $res;
+}
+
+sub sendmessage
+{
+ my($self)=shift;
+ my($msg)=shift;
+ if(!ref($msg)) {
+ # assume the parameters are the same as for Message->new
+ $msg=Freenet::Message->new($msg,shift);
+ }
+ my $sock=$self->{socket};
+
+ if(!ref($msg) eq "Freenet::Message") {
+ warn "wrong argument type\n";
+ }
+
+ print $sock $msg->message,"\n";
+ $self->debug && print ">".$msg->message."\n";
+
+ foreach my $k (keys(%{$msg->header})) {
+ my($h)=$msg->header($k);
+ print $sock "$k=$h\n";
+ $self->debug && print ">$k=$h\n";
+ }
+
+ print $sock "EndMessage\n";
+ $self->debug && print ">EndMessage\n";
+
+ if(defined($msg->data)) {
+ $self->debug && print ">data follows\n";
+ print $sock $msg->data;
+ $self->debug && print ">wrote data\n";
+ }
+
+ return 1;
+}
+
+sub disconnect
+{
+ my $self=shift;
+ my $ret;
+
+ $ret=close($self->{socket});
+ $self->{socket}=undef;
+
+ $self->debug && print "client disconnected\n";
+
+ return $ret;
+}
+
+1;
Added: trunk/apps/perlFreenet/Freenet/Message.pm
===================================================================
--- trunk/apps/perlFreenet/Freenet/Message.pm (rev 0)
+++ trunk/apps/perlFreenet/Freenet/Message.pm 2007-04-01 00:50:14 UTC (rev
12505)
@@ -0,0 +1,67 @@
+package Freenet::Message;
+
+sub new {
+ my $class = shift;
+ my $self = {};
+ bless $self, $class;
+ $self->initialize(@_);
+ return $self;
+}
+
+sub initialize
+{
+ my $self = shift;
+
+ $self->{data}=undef;
+ $self->{message}=shift;
+ # if we don't have header field, you can just leave them out
+ # e.g. ->new("ShutDown");
+ $self->{header}=shift || {};
+}
+
+# TODO: how can these be assigned to?
+sub data
+{
+ return shift->{data};
+}
+
+# TODO: should this be called "name" or "messagename"?
+sub message
+{
+ return shift->{message};
+}
+
+# get header hash ref or one header
+
+sub header
+{
+ my($self)=shift;
+ if(int(@_)==0) {
+ return $self->{header};
+ } else {
+ my $k=shift;
+ return $self->{header}->{$k};
+ }
+}
+
+# as_string is useful for debugging, returns the complete message ending
+# with either EndMessage or Data
+
+sub as_string
+{
+ my($self)=shift;
+
+ my($s)=$self->message."\n";
+ foreach my $k (keys(%{$self->header})) {
+ $s.=$k."=".$self->header($k)."\n";
+ }
+ if(defined($self->data)) {
+ # ignore the data field for now
+ $s.="Data\n";
+ } else {
+ $s.="EndMessage\n";
+ }
+ return $s;
+}
+
+1;
Added: trunk/apps/perlFreenet/README
===================================================================
--- trunk/apps/perlFreenet/README (rev 0)
+++ trunk/apps/perlFreenet/README 2007-04-01 00:50:14 UTC (rev 12505)
@@ -0,0 +1,24 @@
+Perl support for Freenet
+
+This is still very preliminary, you can connect to the server and
send/received
+messages including data. Some examples are in perlfn.pl and in
putcomplexdir.pl.
+
+There is no support for waiting for or polling messages yet, so longer
+operations will probably not work and you cannot start more than one operation
+unless you take care of sorting the progress messages yourself.
+
+Right now there is no Makefile.PL nor PPM, you can either keep the Freenet
+directory in your local dir or copy them to lib/perl/<version>/
+
+There are quite a few things missing, e.g. Perldoc.
+
+Until now, I have used the library under Windows only (Vista and XP) with
+ActiveState Perl 5.8.8, it should work under Linux as well, but this is yet
+untested.
+
+For feedback about the code, please use devel at P7LnnR2qMOTZdYbBa_teC92vTLQ
in
+[MAILBOX] or use the bug tracker at https://bugs.freenetproject.org/
+(perlFreenet)
+
+Thats all for now, I'll leave you to finding your own bugs ...
+
Added: trunk/apps/perlFreenet/perlfn.pl
===================================================================
--- trunk/apps/perlFreenet/perlfn.pl (rev 0)
+++ trunk/apps/perlFreenet/perlfn.pl 2007-04-01 00:50:14 UTC (rev 12505)
@@ -0,0 +1,125 @@
+#! perl
+
+use Data::Dumper;
+use File::Slurp;
+
+use Freenet::Message;
+use Freenet::Connection;
+
+$node=Freenet::Connection->new({Node => 'localhost', Client=>'perl
testclient', Debug=>1});
+($nodehello=$node->connect) || warn "connect failed\n";
+
+if($nodehello->message ne "NodeHello") {
+ warn "something went wrong, got ".$nodehello->message." instead of
NodeHello\n";
+}
+
+# get uptime
+$node->sendmessage("GetNode", {WithVolatile => 'true'});
+$nodedata=$node->getmessage;
+$uptime=$nodedata->header("volatile.uptimeSeconds")/3600.0;
+print "uptime $uptime hours\n";
+
+# get a list of peer node names
+
+$node->sendmessage("ListPeers",
+ {
+ WithVolatile => 'true',
+ WithMetadata => 'true',
+ }
+);
+
+while(1) {
+ my($msg)=$node->getmessage;
+ if($msg->message eq "Peer") {
+ $myName=$msg->header(myName);
+ $status=$msg->header("volatile.status");
+ print "$myName $status\n";
+ }
+ last if $msg->message eq "EndListPeers";
+};
+
+$node->sendmessage("ClientGet",
+ {
+ IgnoreDS=>"false",
+ DSOnly=>"false",
+ URI=>'USK at
Aegl9hc-9O2-VMpXBYuxwuj9JAoMDdXHlNjGst1hLD8,xJTwS8hLh5Uv-20UbH9Mp64nnfqjbGkTDaUlo4EPr9M,AQACAAE/fn_rrd/2/activelink.png',
+ Identifier=>"Request Number One",
+ Verbosity=>0,
+ ReturnType=>"direct",
+ MaxSize=>1000000,
+ MaxTempSize=>1000000,
+ MaxRetries=>100,
+ PriorityClass=>1,
+ Persistence=>"connection",
+ ClientToken=>"hello",
+ Global=>"false",
+ }
+);
+
+my($msg)=$node->getmessage;
+print $msg->as_string;
+
+if($msg->message ne "DataFound") {
+ die "didn't get the expected message (got ".$msg->message.")\n";
+}
+
+# from the wiki documentation I thought you have to do GetRequestStatus after
+# DataFound, but apparently the file is returned directly afterwards
+
+#$node->sendmessage(Freenet::Message->new("GetRequestStatus",
+#{
+# Identifier=>"Request Number One",
+# Global=>"true",
+# OnlyData=>"false",
+#}
+#));
+
+$msg=$node->getmessage;
+print $msg->as_string;
+
+print "retrieved file size ".length($msg->data),"\n";
+
+write_file("test.png", {binmode => ':raw' }, $msg->data);
+
+$node->sendmessage("GenerateSSK", {Identifier=>"My Identifier Blah Blah"});
+print $node->getmessage->as_string;
+
+# shut down node (you probably dont want to do this is a test script)
+
+#$node->sendmessage("Shutdown");
+#print $node->getmessage->as_string;
+
+# get CHK of a known file
+
+# have to create the message beforehand since we have to add data element
+
+$msg=Freenet::Message->new("ClientPut",
+ {
+
+ URI=>"CHK@",
+ "Metadata.ContentType"=>"text/pdf",
+ Identifier=>"My Test File",
+ Verbosity=>"0",
+ MaxRetries=>"10",
+ PriorityClass=>"1",
+ GetCHKOnly=>"true",
+ Global=>"false",
+ DontCompress=>"true",
+ ClientToken=>"Hello!!!",
+ UploadFrom=>"direct",
+ TargetFilename=>"document.pdf",
+ }
+);
+
+$data=read_file("c:/document.pdf",{binmode => ':raw'});
+
+$msg->{data}=$data;
+$msg->{header}->{DataLength}=length($data);
+
+$node->sendmessage($msg);
+
+my($msg)=$node->getmessage;
+print $msg->as_string;
+
+$node->disconnect || warn "disconnect failed\n";
+
Added: trunk/apps/perlFreenet/putcomplexdir.pl
===================================================================
--- trunk/apps/perlFreenet/putcomplexdir.pl (rev 0)
+++ trunk/apps/perlFreenet/putcomplexdir.pl 2007-04-01 00:50:14 UTC (rev
12505)
@@ -0,0 +1,55 @@
+#! perl
+
+use Data::Dumper;
+use File::Slurp;
+
+use Freenet::Message;
+use Freenet::Connection;
+
+$node=Freenet::Connection->new({Node => '192.168.178.20', Client=>'perl
putcomplexdir', Debug=>1});
+($nodehello=$node->connect) || warn "connect failed\n";
+
+if($nodehello->message ne "NodeHello") {
+ warn "something went wrong, got ".$nodehello->message." instread of
NodeHello\n";
+}
+
+$file0=read_file("index.html");
+$file1=read_file("foo.zip",{binmode=>':raw'});
+$file2=read_file("doc.pdf",{binmode=>':raw'});
+
+$msg=Freenet::Message->new("ClientPutComplexDir",
+ {
+ Identifier=>"My Test Dir Insert",
+ Verbosity=>1023,
+ MaxRetries=>999,
+ PriorityClass=>2,
+ URI=>'CHK@',
+ GetCHKOnly=>"false",
+ DontCompress=>"false",
+ ClientToken=>"My Client Token",
+ Persistence=>"connection",
+ Global=>"false",
+ DefaultName=>"index.html",
+ "Files.0.Name"=>"index.html",
+ "Files.0.UploadFrom"=>"direct",
+ "Files.0.Metadata.ContentType"=>"text/html",
+ "Files.0.DataLength"=>length($file0),
+ "Files.1.Name"=>"foo.zip",
+ "Files.1.UploadFrom"=>"direct",
+ "Files.1.Metadata.ContentType"=>"application/zip",
+ "Files.1.DataLength"=>length($file1),
+ "Files.2.Name"=>"doc.pdf",
+ "Files.2.UploadFrom"=>"direct",
+ "Files.2.Metadata.ContentType"=>"application/pdf",
+ "Files.2.DataLength"=>length($file2),
+ }
+);
+
+$msg->{data}=$file0.$file1.$file2;
+
+$node->sendmessage($msg);
+
+while(1) {
+ my($msg)=$node->getmessage;
+}
+$node->disconnect || warn "disconnect failed\n";