+ my $uref = Route::User::get($ucall);
+ unless ($uref) {
+ dbg("PCPROT: Route::User $ucall not in config") if isdbg('chanerr');
+ }
+ my $parent = Route::Node::get($ncall);
+ unless ($parent) {
+ dbg("PCPROT: Route::Node $ncall not in config") if isdbg('chanerr');
+ }
+
+ $dxchan = $parent->dxchan if $parent;
+ if ($dxchan && $dxchan ne $self) {
+ dbg("PCPROT: PC17 from $origin trying to alter locally connected $ncall, ignored!") if isdbg('chanerr');
+ return;
+ }
+
+ if (eph_dup($line)) {
+ dbg("PCPROT: dup PC17 detected") if isdbg('chanerr');
+ return;
+ }
+
+ $uref = Route->new($ucall) unless $uref; # throw away
+ my $thing = Thingy::Rt->new(origin=>$main::mycall, user=>$self->{call});
+ $thing->from_DXProt(t=>'du', $ncall eq $self->{call} ? () : ('n', "1$ncall"), u=>"1$ucall", DXProt=>$line);
+ $thing->process($self);
+}
+
+# link request
+sub handle_18
+{
+ my $self = shift;
+ my $pcno = shift;
+ my $line = shift;
+ my $origin = shift;
+ $self->state('init');
+
+ # record the type and version offered
+ if ($_[1] =~ /DXSpider Version: (\d+\.\d+) Build: (\d+\.\d+)/) {
+ $self->version(52.51 + $1);
+ $self->user->version(52.51 + $1);
+ $self->build(0 + $2);
+ $self->user->build(0 + $2);
+ unless ($self->is_spider) {
+ $self->user->sort('S');
+ $self->user->put;
+ $self->sort('S');
+ }
+ } else {
+ $self->version(50.0);
+ $self->version($_[2] / 100) if $_[2] && $_[2] =~ /^\d+$/;
+ $self->user->version($self->version);
+ }
+
+ # first clear out any nodes on this dxchannel
+ my $parent = Route::Node::get($origin);
+ my @rout = $parent->del_nodes;
+ $self->route_pc21($origin, $line, @rout, $parent) if @rout;
+ $self->send_local_config();
+ $self->send(pc20());
+}
+
+# incoming cluster list
+sub handle_19
+{
+ my $self = shift;
+ my $pcno = shift;
+ my $line = shift;
+ my $origin = shift;
+
+ my $i;
+ my $newline = "PC19^";
+
+ # new routing list
+ my @rout;
+
+ # first get the INTERFACE node
+ my $parent = Route::Node::get($origin);
+ unless ($parent) {
+ dbg("DXPROT: my parent $origin has disappeared");
+ $self->disconnect;
+ return;
+ }
+
+ # if the origin isn't the same as the INTERFACE, then reparent, creating nodes as necessary
+ if ($origin ne $self->call) {
+ my $op = Route::Node::get($origin);
+ unless ($op) {
+ $op = $parent->add($origin, 5000, Route::here(1));
+ my $user = DXUser->get_current($origin);
+ if (!$user) {
+ $user = DXUser->new($origin);
+ $user->priv(1); # I have relented and defaulted nodes
+ $user->lockout(1);
+ $user->homenode($origin);
+ $user->node($origin);
+ $user->wantroutepc19(1);
+ }
+ $user->sort('A') unless $user->is_node;
+ $user->put;
+ }
+ $parent = $op;
+ }
+
+ # parse the PC19
+ for ($i = 1; $i < $#_-1; $i += 4) {
+ my $here = $_[$i];
+ my $call = uc $_[$i+1];
+ my $conf = $_[$i+2];
+ my $ver = $_[$i+3];
+ next unless defined $here && defined $conf && is_callsign($call);
+
+ eph_del_regex("^PC(?:21\\^$call|17\\^[^\\^]+\\^$call)");
+
+ # check for sane parameters
+ # $ver = 5000 if $ver eq '0000';
+ next if $ver < 5000; # only works with version 5 software
+ next if length $call < 3; # min 3 letter callsigns
+ next if $call eq $main::mycall;
+
+ # check that this PC19 isn't trying to alter the wrong dxchan
+ my $dxchan = DXChannel->get($call);
+ if ($dxchan && $dxchan != $self) {
+ dbg("PCPROT: PC19 from $origin trying to alter wrong locally connected $call, ignored!") if isdbg('chanerr');
+ next;
+ }
+
+ # add this station to the user database, if required (don't remove SSID from nodes)
+ my $user = DXUser->get_current($call);
+ if (!$user) {
+ $user = DXUser->new($call);
+ $user->priv(1); # I have relented and defaulted nodes
+ $user->lockout(1);
+ $user->homenode($call);
+ $user->node($call);
+ }
+ $user->sort('A') unless $user->is_node;
+
+ RouteDB::update($call, $origin);
+
+ # do we believe this call?
+ my $genline = "PC19^$here^$call^$conf^$ver^$_[-1]^";
+ unless ($call eq $origin || $self->is_believed($call)) {
+ my $pt = $user->lastping($origin) || 0;
+ if ($pt+$investigation_int < $main::systime && !Investigate::get($call, $origin)) {
+ my $ivp = Investigate->new($call, $origin);
+ $ivp->version($ver);
+ $ivp->here($here);
+ $ivp->store_pcxx($pcno,$genline,$origin,'PC19',$here,$call,$conf,$ver,$_[-1]);
+ } else {
+ dbg("PCPROT: We don't believe $call on $origin") if isdbg('chanerr');
+ }
+ $user->put;
+ next;
+ }
+
+ if (eph_dup($genline)) {
+ dbg("PCPROT: dup PC19 for $call detected") if isdbg('chanerr');
+ next;
+ }
+
+ my $r = Route::Node::get($call);
+ my $flags = Route::here($here)|Route::conf($conf);
+
+ # modify the routing table if it is in it, otherwise store it in the pc19list for now
+ if ($r) {
+ my $ar;
+ if ($call ne $parent->call) {
+ if ($self->in_filter_route($r)) {
+ $ar = $parent->add($call, $ver, $flags);
+ push @rout, $ar if $ar;
+ } else {