package Thingy;
use vars qw($VERSION $BRANCH @queue @permin @persec);
-$VERSION = sprintf( "%d.%03d", q$Revision$ =~ /(\d+)\.(\d+)/ );
-$BRANCH = sprintf( "%d.%03d", q$Revision$ =~ /\d+\.\d+\.(\d+)\.(\d+)/ || (0,0));
-$main::build += $VERSION;
-$main::branch += $BRANCH;
+
+main::mkver($VERSION = q$Revision$);
@queue = (); # the input / processing queue
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;
}
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;
+ return unless $thing->out_filter($dxchan);
+ }
+
+ # 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;
- # generate the line which may (or not) be cached
- my @out;
- if (my $ref = $thing->{class}) {
- push @out, ref $ref ? @$ref : $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);
}
}
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);
}
+
+ # remember any useful routes
+ RouteDB::update($thing->{origin}, $dxchan->{call}, $thing->{hopsaway});
+ RouteDB::update($thing->{user}, $dxchan->{call}, $thing->{hopsaway}) if exists $thing->{user};
+
$thing->handle($dxchan);
}
}
$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;