+ unless ($self->{do_pc9x}) {
+ dbg("PCPROT: PC9x come in from non-PC9x node, ignored") if isdbg('chanerr');
+ return;
+ }
+
+ my $parent = check_pc9x_t($pcall, $t, 92, 1) || return;
+ my $oparent = $parent;
+
+ $parent->do_pc9x(1);
+ $parent->via_pc92(1);
+
+ if ($sort eq 'F' || $sort eq 'R') {
+
+ # this is the route finding section
+ # here is where the consequences of the 'find' command
+ # are dealt with
+
+ my $from = $_[4];
+ my $target = $_[5];
+
+ if ($sort eq 'F') {
+ my $flag;
+ my $ref;
+ my $dxchan;
+ if ($ref = DXChannel::get($target)) {
+ $flag = 1; # we are directly connected
+ } else {
+ $ref = Route::get($target);
+ $dxchan = $ref->dxchan;
+ $flag = 2;
+ }
+ if ($ref && $flag && $dxchan) {
+ $self->send(pc92r($from, $target, $flag, int($dxchan->{pingave}*1000)));
+ return;
+ }
+ } elsif ($sort eq 'R') {
+ if (my $dxchan = DXChannel::get($from)) {
+ handle_pc92_find_reply($dxchan, $pcall, $from, $target, @_[6,7]);
+ } else {
+ my $ref = Route::get($from);
+ if ($ref) {
+ my @dxchan = grep {$_->do_pc9x} $ref->alldxchan;
+ if (@dxchan) {
+ $_->send($line) for @dxchan;
+ } else {
+ dbg("PCPROT: no return route, ignored") if isdbg('chanerr')
+ }
+ } else {
+ dbg("PCPROT: no return route, ignored") if isdbg('chanerr')
+ }
+ }
+ return;
+ }
+ } elsif ($sort eq 'A' || $sort eq 'D' || $sort eq 'C') {
+
+ # remember the last channel we arrived on
+ $parent->PC92C_dxchan($self->{call}) unless $self->{call} eq $parent->call;
+
+ # this is the main route section
+ # here is where all the routes are created and destroyed
+
+ my @ent = map {[ _decode_pc92_call($_) ]} grep {$_ && /^[0-7]/} @_[4 .. $#_];
+
+ if (@ent) {
+
+ # look at the first one which will always be a node of some sort
+ # except in the case of 'A' or 'D' in which the $pcall is used
+ # otherwise use the node call and update any information
+ # that needs to be done.
+ my ($call, $is_node, $is_extnode, $here, $version, $build) = @{$ent[0]};
+ if (($sort eq 'A' || $sort eq 'D') && !$is_node) {
+ # parent is already set correctly
+ # this is to allow shortcuts for A and D records
+ # not repeating the origin call to no real purpose
+ ;
+ } else {
+ if ($call && $is_node) {
+ if ($call eq $main::mycall) {
+ dbg("PCPROT: $call looped back onto $main::mycall, ignored") if isdbg('chanerr');
+ return;
+ }
+ # this is only accepted from my "self".
+ # this also kills configs from PC92 nodes with external PC19 nodes that are also
+ # locally connected. Local nodes always take precedence.
+ if (DXChannel::get($call) && $call ne $self->{call}) {
+ dbg("PCPROT: locally connected node $call from other another node $self->{call}, ignored") if isdbg('chanerr');
+ return;
+ }
+ if ($is_extnode) {
+ # reparent to external node (note that we must have received a 'C' or 'A' record
+ # from the true parent node for this external before we get one for the this node
+ unless ($parent = Route::Node::get($call)) {
+ if ($is_extnode && $oparent) {
+ @radd = _add_thingy($oparent, $ent[0]);
+ $parent = $radd[0];
+ } else {
+ dbg("PCPROT: no previous C or A for this external node received, ignored") if isdbg('chanerr');
+ return;
+ }
+ }
+ $parent = check_pc9x_t($call, $t, 92) || return;
+ $parent->via_pc92(1);
+ $parent->PC92C_dxchan($self->{call});
+ }
+ } else {
+ dbg("PCPROT: must be mycall or external node as first entry, ignored") if isdbg('chanerr');
+ return;
+ }
+ $parent->here(Route::here($here));
+ $parent->version($version) if $version && $version > $parent->version;
+ $parent->build($build) if $build && $build > $parent->build;
+ $parent->PC92C_dxchan($self->{call}) unless $self->{call} eq $parent->call;
+ shift @ent;
+ }
+ }
+
+ # do a pass through removing any references to either locally connected nodes or mycall
+ my @nent;
+ for (@ent) {
+ next unless $_;
+ if ($_->[0] eq $main::mycall || DXChannel::get($_->[0])) {
+ dbg("PCPROT: $_->[0] refers to locally connected node, ignored") if isdbg('chanerr');
+ next;
+ }
+ push @nent, $_;
+ }
+
+ if ($sort eq 'A') {
+ for (@nent) {
+ push @radd, _add_thingy($parent, $_);
+ }
+ } elsif ($sort eq 'D') {
+ for (@nent) {
+ push @rdel, _del_thingy($parent, $_);
+ }
+ } elsif ($sort eq 'C') {
+ my (@nodes, @users);
+
+ # we only reset obscounts on config records
+ $oparent->reset_obs;
+ dbg("ROUTE: reset obscount on $pcall now " . $oparent->obscount) if isdbg('obscount');
+ if ($oparent != $parent) {
+ $parent->reset_obs;
+ dbg("ROUTE: reset obscount on $parent->{call} now " . $parent->obscount) if isdbg('obscount');
+ }
+
+ #
+ foreach my $r (@nent) {
+ # my ($call, $is_node, $is_extnode, $here, $version, $build) = _decode_pc92_call($_);
+ if ($r->[0]) {
+ if ($r->[1]) {
+ push @nodes, $r->[0];
+ } else {
+ push @users, $r->[0];
+ }
+ } else {
+ dbg("PCPROT: pc92 call entry '$_' not decoded, ignored") if isdbg('chanerr');
+ }
+ }
+
+ my ($dnodes, $dusers, $nnodes, $nusers) = $parent->calc_config_changes(\@nodes, \@users);
+
+ # add users here
+ foreach my $r (@nent) {
+ my $call = $r->[0];
+ if ($call) {
+ push @radd,_add_thingy($parent, $r) if grep $call eq $_, (@$nnodes, @$nusers);
+ }
+ }
+ # del users here
+ foreach my $r (@$dnodes) {
+ push @rdel,_del_thingy($parent, [$r, 1]);
+ }
+ foreach my $r (@$dusers) {
+ push @rdel,_del_thingy($parent, [$r, 0]);
+ }
+
+ # remember this last PC92C for rebroadcast on demand
+ $parent->last_PC92C($line);
+ } else {
+ dbg("PCPROT: unknown action '$sort', ignored") if isdbg('chanerr');
+ return;
+ }
+
+ foreach my $r (@rdel) {
+ next unless $r;
+
+ $self->route_pc21($pcall, undef, $r) if $r->isa('Route::Node');
+ $self->route_pc17($pcall, undef, $parent, $r) if $r->isa('Route::User');
+ }
+ my @pc19 = grep { $_ && $_->isa('Route::Node') } @radd;
+ my @pc16 = grep { $_ && $_->isa('Route::User') } @radd;
+ unshift @pc19, $parent if $self->{state} eq 'init92' && $oparent == $parent;
+ $self->route_pc19($pcall, undef, @pc19) if @pc19;
+ $self->route_pc16($pcall, undef, $parent, @pc16) if @pc16;
+ }