fix the nasty feature in perl 5.6 with my $ref = $foo if $bar for
[spider.git] / perl / Thingy.pm
index 7bbf3edb1568673e265769b36926ea0068dea786..afc2129b913641eaaaf641330781de873bc535cd 100644 (file)
@@ -36,14 +36,19 @@ my $lastmin = time;
 
 use DXChannel;
 use DXDebug;
+use DXUtil;
+
 
 # we expect all thingies to be subclassed
 sub new
 {
        my $class = shift;
+       my $pkg = ref $class || $class;
        my $thing = {@_};
+
+       $thing->{origin} ||= $main::mycall;
        
-       bless $thing, $class;
+       bless $thing, $pkg;
        return $thing;
 }
 
@@ -53,38 +58,80 @@ sub send
        my $thing = shift;
        my $dxchan = shift;
        my $class;
+       my $sub;
+       
        if (@_) {
                $class = shift;
        } elsif ($dxchan->isa('DXChannel')) {
                $class = ref $dxchan;
        }
 
+       # BEWARE!!!!!
+       no strict 'refs';
+
        # do output filtering
        if ($thing->can('out_filter')) {
                return unless $thing->out_filter($dxchan);
        }
 
-       # generate the line which may (or not) be cached
-       my @out;
-       if (my $ref = $thing->{class}) {
-               push @out, ref $ref ? @$ref : $ref;
+       # before send (and line generation) things
+       # function must return true to make the send happen
+       $sub = "before_send_$class";
+       if ($thing->can($sub)) {
+               return unless $thing->$sub($dxchan);
+       }
+       
+       # generate the protocol line which may (or not) be cached
+       my $ref;
+       unless ($ref = $thing->{class}) {
+               $sub = "gen_$class";
+               $ref = $thing->$sub($dxchan) if $thing->can($sub);
+       }
+       $dxchan->send(ref $ref ? @$ref : $ref) if $ref;
+
+       # after send
+       if ($thing->can('after_send_all')) {
+               $thing->after_send_all($dxchan);
        } else {
-               no strict 'refs';
-               my $sub = "gen_$class";
-               push @out, $thing->$sub($dxchan) if $thing->can($sub);
+               $sub = "after_send_$class";
+               $thing->$sub($dxchan) if $thing->can($sub);
        }
-       $dxchan->send(@out) if @out;
 }
 
-# broadcast to all except @_
+# 
+# This is the main routing engine for the new protocol. Broadcast is a slight
+# misnomer, because if it thinks it can route it down one or interfaces, it will.
+# 
+# It handles anything it recognises as a callsign, sees if it can find it in a 
+# routing table, and if it does, then routes the message.
+#
+# If it can't then it will broadcast it.
+#
 sub broadcast
 {
        my $thing = shift;
        dbg("Thingy::broadcast: " . $thing->ascii) if isdbg('thing'); 
 
-       foreach my $dxchan (DXChannel::get_all()) {
+       my @dxchan;
+       my $to ||= $thing->{route}; 
+       $to     ||= $thing->{touser};
+       $to ||= $thing->{group};
+       if ($to && is_callsign($to) && (my $ref = Route::get($to))) {
+               dbg("Thingy::broadcast: routing for $to") if isdbg('thing');
+               @dxchan = $ref->alldxchan;
+       } else {
+               @dxchan = DXChannel::get_all();
+       }
+
+       dbg("Thingy::broadcast: offered " . join(',', map {$_->call} @dxchan)) if isdbg('thing');
+       
+       foreach my $dxchan (@dxchan) {
                next if $dxchan == $main::me;
                next if grep $dxchan == $_, @_;
+               next if $dxchan->{call} eq $thing->{origin};
+               next if $thing->{user} && !$dxchan->is_user && $dxchan->{call} eq $thing->{user};
+               
+               dbg("Thingy::broadcast: sending to $dxchan->{call}") if isdbg('thing');
                $thing->send($dxchan); 
        }
 }
@@ -98,14 +145,26 @@ sub queue
        push @queue, $thing;
 }
 
+#
 # this is the main commutator loop. In due course it will
-# become the *only* commutator loop
+# become the *only* commutator loop, This can be called in one
+# of two ways: either with 2 args or with none.
+#
+# The two arg form is an immediate "queue and handle" and does
+# a full cycle, immediately
+#
 sub process
 {
        my $thing;
+
+       if (@_ == 2) {
+               $thing = shift;
+               $thing->queue(shift);
+       }
+
        while (@queue) {
                $thing = shift @queue;
-               my $dxchan = DXChannel->get($thing->{dxchan});
+               my $dxchan = DXChannel::get($thing->{dxchan});
                if ($dxchan) {
                        if ($thing->can('in_filter')) {
                                next unless $thing->in_filter($dxchan);
@@ -159,9 +218,41 @@ sub ascii
        my $dd = new Data::Dumper([$thing]);
        $dd->Indent(0);
        $dd->Terse(1);
-       $dd->Sortkeys(1);
+       #$dd->Sortkeys(1);
     $dd->Quotekeys($] < 5.005 ? 1 : 0);
        return $dd->Dumpxs;
 }
+
+sub add_auth
+{
+       my $thing = shift;
+       my $s = $thing->{'s'} = sprintf "%X", int(rand() * 100000000);
+       my $auth = Verify->new("DXSp,$main::mycall,$s,$thing->{v},$thing->{b}");
+       $thing->{auth} = $auth->challenge($main::me->user->passphrase);
+}
+
+#
+# create a generalised reply to a passed thing, if it isn't replyable 
+# to then undef is returned
+#  
+sub new_reply
+{
+       my $thing = shift;
+       my $out;
+       
+       if ($thing->{group} eq $main::mycall) {
+               $out = $thing->new;
+               $out->{touser} = $thing->{user} if $thing->{user};
+               $out->{group} = $thing->{origin};
+       } elsif (DXChannel::get($thing->{group})) {
+               $out = $thing->new(user => $thing->{group});
+               $out->{touser} = $thing->{user} if $thing->{user};
+               $out->{group} = $thing->{origin};
+       } elsif ($thing->{touser} && DXChannel::get($thing->{touser})) {
+               $out = $thing->new(user => $thing->{touser});
+               $out->{group} = $thing->{group};
+       }
+       return $out;
+}
 1;