add more code gradually
[spider.git] / perl / Thingy / Route.pm
1 #
2 # Generate route Thingies
3 #
4 # $Id$
5 #
6 # Copyright (c) 2004 Dirk Koopman G1TLH
7 #
8
9 package Thingy::Route;
10
11 use strict;
12
13 use vars qw($VERSION $BRANCH);
14 $VERSION = sprintf( "%d.%03d", q$Revision$ =~ /(\d+)\.(\d+)/ );
15 $BRANCH = sprintf( "%d.%03d", q$Revision$ =~ /\d+\.\d+\.(\d+)\.(\d+)/,(0,0));
16 $main::build += $VERSION;
17 $main::branch += $BRANCH;
18
19 use vars qw(@ISA);
20
21 @ISA = qw(Thingy);
22
23 # this is node connect 
24 sub new_node_connect
25 {
26         my $pkg = shift;
27         my $fromnode = shift;
28         my $inon = shift;
29         my @n = map {uc} @_;
30         my $t = $pkg->SUPER::new(_fromnode=>$fromnode,
31                                                          _inon=>$inon,
32                                                          id=>'DXSpider', v=>$main::version, b=>$main::build,                                                     t=>'nc', n=>\@n);
33         return $t;
34 }
35
36 # this is node disconnect 
37 sub new_node_disconnect
38 {
39         my $pkg = shift;
40         my $fromnode = shift;
41         my $inon = shift;
42         my @n = map {uc} @_;
43         my $t = $pkg->SUPER::new(_fromnode=>$fromnode,
44                                                          _inon=>$inon,
45                                                          t=>'nd', n=>\@n);
46         return $t;
47 }
48
49 # a full node update
50 sub new_node_update
51 {
52         my $pkg = shift;
53         
54         my @nodes = grep {$_ ne $main::mycall} DXChannel::get_all_node_calls();
55         my @users = DXChannel::get_all_user_calls();
56         
57         my $t = $pkg->SUPER::new(t=>'nu', 
58                                                          id=>'DXSpider', v=>$main::version, b=>$main::build, 
59                                                          n=>\@nodes, u=>\@users);
60         return $t;
61 }
62
63 sub new_user_connect
64 {
65         my $pkg = shift;
66         my $fromnode = shift;
67         my $inon = shift;
68         my @u = map {uc} @_;
69         my $t = $pkg->SUPER::new(_fromnode=>$fromnode,
70                                                          _inon=>$inon,
71                                                          t=>'uc', u=>\@u);
72         return $t;
73 }
74
75 sub new_user_discconnect
76 {
77         my $pkg = shift;
78         my $fromnode = shift;
79         my $inon = shift;
80         my @u = map {uc} @_;
81         my $t = $pkg->SUPER::new(_fromnode=>$fromnode,
82                                                          _inon=>$inon,
83                                                          t=>'ud', u=>\@u);
84         return $t;
85 }
86
87 sub normal
88 {
89
90 }
91
92 # node update (this will completely rewrite the node's info)
93 sub handle_nu
94 {
95         my $t = shift;
96         
97 }
98
99 # node connection
100 sub handle_nc
101 {
102         my $t = shift;
103
104         my @rout;
105         
106         # first get the fromnode
107         my $dxchan = DXChannel->get($t->{_inon}) || return;
108         my $parent = Route::Node::get($t->{_fromnode});
109
110         unless ($parent) {
111                 push @rout, $parent = Route::Node->new($t->{_fromnode});
112         }
113
114         for (@{$t->{n}) {
115                 my ($here, $call) = unpack "AA*", $_;
116
117                 # if it is a new node add it to the user database
118                 my $user = DXUser->get_current($call);
119                 unless ($user) {
120                         $user = DXUser->new($call);
121                         $user->sort('A');
122                         $user->priv(1);         # I have relented and defaulted nodes
123                         $user->lockout(1);
124                         $user->homenode($call);
125                         $user->node($call);
126                 }
127
128                 # add each of the nodes to this parent
129                 
130         }
131                 # add this station to the user database, if required (don't remove SSID from nodes)
132
133                 my $r = Route::Node::get($call);
134                 my $flags = Route::here($here)|Route::conf($conf);
135
136                 # modify the routing table if it is in it, otherwise store it in the pc19list for now
137                 if ($r) {
138                         my $ar;
139                         if ($call ne $parent->call) {
140                                 if ($self->in_filter_route($r)) {
141                                         $ar = $parent->add($call, $ver, $flags);
142                                         push @rout, $ar if $ar;
143                                 } else {
144                                         next;
145                                 }
146                         }
147                         if ($r->version ne $ver || $r->flags != $flags) {
148                                 $r->version($ver);
149                                 $r->flags($flags);
150                                 push @rout, $r unless $ar;
151                         }
152                 } else {
153
154                         # if he is directly connected or allowed then add him, otherwise store him up for later
155                         if ($call eq $self->{call} || $user->wantroutepc19) {
156                                 my $new = Route->new($call); # throw away
157                                 if ($self->in_filter_route($new)) {
158                                         my $ar = $parent->add($call, $ver, $flags);
159                                         $user->wantroutepc19(1) unless defined $user->wantroutepc19;
160                                         push @rout, $ar if $ar;
161                                 } else {
162                                         next;
163                                 }
164                         } else {
165                                 $pc19list{$call} = [] unless exists $pc19list{$call};
166                                 my $nl = $pc19list{$call};
167                                 push @{$pc19list{$call}}, [$self->{call}, $ver, $flags] unless grep $_->[0] eq $self->{call}, @$nl;
168                         }
169                 }
170
171                 # unbusy and stop and outgoing mail (ie if somehow we receive another PC19 without a disconnect)
172                 my $mref = DXMsg::get_busy($call);
173                 $mref->stop_msg($call) if $mref;
174                                 
175                 $user->lastin($main::systime) unless DXChannel->get($call);
176                 $user->put;
177         }
178
179
180         $self->route_pc19($origin, $line, @rout) if @rout;
181         
182 }
183
184 # node disconnection
185 sub handle_nd
186 {
187         my $t = shift;
188         
189 }
190
191 # user connection
192 sub handle_uc
193 {
194         my $t = shift;
195
196         my $newline = "PC16^";
197         my $parent = Route::Node::get($t->{_fromnode}); 
198
199         # if there is a parent, proceed, otherwise if there is a latent PC19 in the PC19list, 
200         # fix it up in the routing tables and issue it forth before the PC16
201         unless ($parent) {
202                 my $nl = $pc19list{$ncall};
203
204                 if ($nl && @_ > 3) { # 3 because of the hop count!
205
206                         # this is a new (remembered) node, now attach it to me if it isn't in filtered
207                         # and we haven't disallowed it
208                         my $user = DXUser->get_current($ncall);
209                         if (!$user) {
210                                 $user = DXUser->new($ncall);
211                                 $user->sort('A');
212                                 $user->priv(1); # I have relented and defaulted nodes
213                                 $user->lockout(1);
214                                 $user->homenode($ncall);
215                                 $user->node($ncall);
216                         }
217
218                         my $wantpc19 = $user->wantroutepc19;
219                         if ($wantpc19 || !defined $wantpc19) {
220                                 my $new = Route->new($ncall); # throw away
221                                 if ($self->in_filter_route($new)) {
222                                         my @nrout;
223                                         for (@$nl) {
224                                                 $parent = Route::Node::get($_->[0]);
225                                                 $dxchan = $parent->dxchan if $parent;
226                                                 if ($dxchan && $dxchan ne $self) {
227                                                         dbg("PCPROT: PC19 from $self->{call} trying to alter locally connected $ncall, ignored!") if isdbg('chanerr');
228                                                         $parent = undef;
229                                                 }
230                                                 if ($parent) {
231                                                         my $r = $parent->add($ncall, $_->[1], $_->[2]);
232                                                         push @nrout, $r unless @nrout;
233                                                 }
234                                         }
235                                         $user->wantroutepc19(1) unless defined $wantpc19; # for now we work on the basis that pc16 = real route 
236                                         $user->lastin($main::systime) unless DXChannel->get($ncall);
237                                         $user->put;
238                                                 
239                                         # route the pc19 - this will cause 'stuttering PC19s' for a while
240                                         $self->route_pc19($origin, $line, @nrout) if @nrout ;
241                                         $parent = Route::Node::get($ncall);
242                                         unless ($parent) {
243                                                 dbg("PCPROT: lost $ncall after sending PC19 for it?");
244                                                 return;
245                                         }
246                                 } else {
247                                         return;
248                                 }
249                                 delete $pc19list{$ncall};
250                         }
251                 } else {
252                         dbg("PCPROT: Node $ncall not in config") if isdbg('chanerr');
253                         return;
254                 }
255         } else {
256                                 
257                 $dxchan = $parent->dxchan;
258                 if ($dxchan && $dxchan ne $self) {
259                         dbg("PCPROT: PC16 from $self->{call} trying to alter locally connected $ncall, ignored!") if isdbg('chanerr');
260                         return;
261                 }
262
263                 # input filter if required
264                 return unless $self->in_filter_route($parent);
265         }
266
267         my $i;
268         my @rout;
269         for ($i = 2; $i < $#_; $i++) {
270                 my ($call, $conf, $here) = $_[$i] =~ /^(\S+) (\S) (\d)/o;
271                 next unless $call && $conf && defined $here && is_callsign($call);
272                 next if $call eq $main::mycall;
273
274                 eph_del_regex("^PC17\\^$call\\^$ncall");
275                                 
276                 $conf = $conf eq '*';
277
278                 # reject this if we think it is a node already
279                 my $r = Route::Node::get($call);
280                 my $u = DXUser->get_current($call) unless $r;
281                 if ($r || ($u && $u->is_node)) {
282                         dbg("PCPROT: $call is a node") if isdbg('chanerr');
283                         next;
284                 }
285                                 
286                 $r = Route::User::get($call);
287                 my $flags = Route::here($here)|Route::conf($conf);
288                                 
289                 if ($r) {
290                         my $au = $r->addparent($parent);                                        
291                         if ($r->flags != $flags) {
292                                 $r->flags($flags);
293                                 $au = $r;
294                         }
295                         push @rout, $r if $au;
296                 } else {
297                         push @rout, $parent->add_user($call, $flags);
298                 }
299                 
300                                 
301                 # add this station to the user database, if required
302                 $call =~ s/-\d+$//o;    # remove ssid for users
303                 my $user = DXUser->get_current($call);
304                 $user = DXUser->new($call) if !$user;
305                 $user->homenode($parent->call) if !$user->homenode;
306                 $user->node($parent->call);
307                 $user->lastin($main::systime) unless DXChannel->get($call);
308                 $user->put;
309         }
310         $self->route_pc16($origin, $line, $parent, @rout) if @rout;     
311 }
312
313 # user disconnection
314 sub handle_ud
315 {
316         my $t = shift;
317
318         my $uref = Route::User::get($ucall);
319         unless ($uref) {
320                 dbg("PCPROT: Route::User $ucall not in config") if isdbg('chanerr');
321                 return;
322         }
323         my $parent = Route::Node::get($ncall);
324         unless ($parent) {
325                 dbg("PCPROT: Route::Node $ncall not in config") if isdbg('chanerr');
326                 return;
327         }                       
328
329         $dxchan = $parent->dxchan;
330         if ($dxchan && $dxchan ne $self) {
331                 dbg("PCPROT: PC17 from $self->{call} trying to alter locally connected $ncall, ignored!") if isdbg('chanerr');
332                 return;
333         }
334
335         # input filter if required
336         return unless $self->in_filter_route($parent);
337                         
338         $parent->del_user($uref);
339
340         if (eph_dup($line)) {
341                 dbg("PCPROT: dup PC17 detected") if isdbg('chanerr');
342                 return;
343         }
344
345         $self->route_pc17($origin, $line, $parent, $uref);
346         
347 }