+ 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);
+ }
+}
+
+# queue this thing for processing
+sub queue
+{
+ my $thing = shift;
+ my $dxchan = shift;
+ $thing->{dxchan} = $dxchan->call;
+ push @queue, $thing;
+}
+
+#
+# this is the main commutator loop. In due course it will
+# 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});
+ 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);
+ }
+ }
+
+ # per second and per minute processing
+ if ($main::systime != $lastsec) {
+ if ($main::systime >= $lastmin+60) {
+ foreach my $r (@permin) {
+ &{$r->[0]}();
+ }
+ $lastmin = $main::systime;
+ }
+ foreach my $r (@persec) {
+ &{$r->[0]}();
+ }
+ $lastsec = $main::systime;
+ }
+}
+
+sub add_minute_process
+{
+ my $pkg = shift;
+ my $addr = shift;
+ my $name = shift;
+ dbg('Adding $name to Thingy per minute queue');
+ push @permin, [$addr, $name];
+}
+
+sub add_second_process
+{
+ my $pkg = shift;
+ my $addr = shift;
+ my $name = shift;
+ dbg('Adding $name to Thingy per second queue');
+ push @persec, [$addr, $name];
+}
+
+
+sub ascii
+{
+ my $thing = shift;
+ my $dd = new Data::Dumper([$thing]);
+ $dd->Indent(0);
+ $dd->Terse(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;
+}