4 # This is the new fundamental protocol engine handler
6 # This is where all the new things (and eventually all the old things
11 # Copyright (c) 2004 Dirk Koopman G1TLH
18 use vars qw($VERSION $BRANCH @queue @permin @persec);
20 main::mkver($VERSION = q$Revision$);
22 @queue = (); # the input / processing queue
25 # these are set up using the Thingy->add_second_process($addr, $name)
26 # and Thingy->add_minute_process($addr, $name)
28 # They replace the old cycle in cluster.pl
31 @persec = (); # this replaces the cycle in cluster.pl
32 @permin = (); # this is an extra per minute cycle
42 # we expect all thingies to be subclassed
46 my $pkg = ref $class || $class;
49 $thing->{origin} ||= $main::mycall;
55 # send it out in the format asked for, if available
65 } elsif ($dxchan->isa('DXChannel')) {
73 if ($thing->can('out_filter')) {
74 return unless $thing->out_filter($dxchan);
77 # before send (and line generation) things
78 # function must return true to make the send happen
79 $sub = "before_send_$class";
80 if ($thing->can($sub)) {
81 return $thing->$sub($dxchan);
84 # generate the protocol line which may (or not) be cached
86 unless ($ref = $thing->{class}) {
88 $ref = $thing->$sub($dxchan) if $thing->can($sub);
90 $dxchan->send(ref $ref ? @$ref : $ref) if $ref;
93 if ($thing->can('after_send_all')) {
94 $thing->after_send_all($dxchan);
96 $sub = "after_send_$class";
97 $thing->$sub($dxchan) if $thing->can($sub);
102 # This is the main routing engine for the new protocol. Broadcast is a slight
103 # misnomer, because if it thinks it can route it down one or interfaces, it will.
105 # It handles anything it recognises as a callsign, sees if it can find it in a
106 # routing table, and if it does, then routes the message.
108 # If it can't then it will broadcast it.
113 dbg("Thingy::broadcast: " . $thing->ascii) if isdbg('thing');
116 my $to ||= $thing->{route};
117 $to ||= $thing->{touser};
118 $to ||= $thing->{group};
119 if ($to && is_callsign($to) && (my $ref = Route::get($to))) {
120 dbg("Thingy::broadcast: routing for $to") if isdbg('thing');
121 @dxchan = $ref->alldxchan;
123 @dxchan = DXChannel::get_all();
126 dbg("Thingy::broadcast: offered " . join(',', map {$_->call} @dxchan)) if isdbg('thing');
128 foreach my $dxchan (@dxchan) {
129 next if $dxchan == $main::me;
130 next if grep $dxchan == $_, @_;
131 next if $dxchan->{call} eq $thing->{origin};
132 next if $thing->{user} && !$dxchan->is_user && $dxchan->{call} eq $thing->{user};
134 dbg("Thingy::broadcast: sending to $dxchan->{call}") if isdbg('thing');
135 $thing->send($dxchan);
139 # queue this thing for processing
144 $thing->{dxchan} = $dxchan->call;
149 # this is the main commutator loop. In due course it will
150 # become the *only* commutator loop, This can be called in one
151 # of two ways: either with 2 args or with none.
153 # The two arg form is an immediate "queue and handle" and does
154 # a full cycle, immediately
162 $thing->queue(shift);
166 $thing = shift @queue;
167 my $dxchan = DXChannel::get($thing->{dxchan});
169 if ($thing->can('in_filter')) {
170 next unless $thing->in_filter($dxchan);
173 # remember any useful routes
174 RouteDB::update($thing->{origin}, $dxchan->{call}, $thing->{hopsaway});
175 RouteDB::update($thing->{user}, $dxchan->{call}, $thing->{hopsaway}) if exists $thing->{user};
177 $thing->handle($dxchan);
181 # per second and per minute processing
182 if ($main::systime != $lastsec) {
183 if ($main::systime >= $lastmin+60) {
184 foreach my $r (@permin) {
187 $lastmin = $main::systime;
189 foreach my $r (@persec) {
192 $lastsec = $main::systime;
196 sub add_minute_process
201 dbg('Adding $name to Thingy per minute queue');
202 push @permin, [$addr, $name];
205 sub add_second_process
210 dbg('Adding $name to Thingy per second queue');
211 push @persec, [$addr, $name];
218 my $dd = new Data::Dumper([$thing]);
222 $dd->Quotekeys($] < 5.005 ? 1 : 0);
229 my $s = $thing->{'s'} = sprintf "%X", int(rand() * 100000000);
230 my $auth = Verify->new("DXSp,$main::mycall,$s,$thing->{v},$thing->{b}");
231 $thing->{auth} = $auth->challenge($main::me->user->passphrase);
235 # create a generalised reply to a passed thing, if it isn't replyable
236 # to then undef is returned
243 if ($thing->{group} eq $main::mycall) {
245 $out->{touser} = $thing->{user} if $thing->{user};
246 $out->{group} = $thing->{origin};
247 } elsif (DXChannel::get($thing->{group})) {
248 $out = $thing->new(user => $thing->{group});
249 $out->{touser} = $thing->{user} if $thing->{user};
250 $out->{group} = $thing->{origin};
251 } elsif ($thing->{touser} && DXChannel::get($thing->{touser})) {
252 $out = $thing->new(user => $thing->{touser});
253 $out->{group} = $thing->{group};