try to fix protocol version
[spider.git] / perl / DXProt.pm
index b9d00a3786fdf910fc68885bd750f6d15b189b57..cc468cd54409a0e6cfb3b103aab3187d235baf2b 100644 (file)
@@ -40,14 +40,14 @@ use Thingy::Dx;
 use Thingy::Rt;
 use Thingy::Ping;
 use Thingy::T;
+use Thingy::Hello;
+use Thingy::Bye;
 
 use strict;
 
 use vars qw($VERSION $BRANCH);
-$VERSION = sprintf( "%d.%03d", q$Revision$ =~ /(\d+)\.(\d+)/ );
-$BRANCH = sprintf( "%d.%03d", q$Revision$ =~ /^\d+\.\d+(?:\.(\d+)\.(\d+))?$/  || (0,0));
-$main::build += $VERSION;
-$main::branch += $BRANCH;
+
+main::mkver($VERSION = q$Revision$);
 
 use vars qw($pc11_max_age $pc23_max_age $last_pc50 $eph_restime $eph_info_restime $eph_pc34_restime
                        $last_hour $last10 %eph  %pings %rcmds $ann_to_talk
@@ -214,7 +214,6 @@ sub init
        my $user = DXUser->get($main::mycall);
        die "User $main::mycall not setup or disappeared RTFM" unless $user;
        
-       $myprot_version += $main::version*100;
        $main::me = DXProt->new($main::mycall, 0, $user); 
        $main::me->{here} = 1;
        $main::me->{state} = "indifferent";
@@ -223,8 +222,9 @@ sub init
        $main::me->{metric} = 0;
        $main::me->{pingave} = 0;
        $main::me->{registered} = 1;
-       $main::me->{version} = 5251 + $main::version;
+       $main::me->{version} = $myprot_version + int ($main::version * 100);
        $main::me->{build} = $main::build;
+       $main::me->{lastcf} = $main::me->{lasthello} = time;
 }
 
 #
@@ -238,8 +238,7 @@ sub new
        # add this node to the table, the values get filled in later
        my $pkg = shift;
        my $call = shift;
-       $main::routeroot->add($call, '5000', Route::here(1)) if $call ne $main::mycall;
-
+       $main::routeroot->add($call, '5000', 1) if $call ne $main::mycall;
        return $self;
 }
 
@@ -309,6 +308,11 @@ sub start
        $self->state('init');
        $self->{pc50_t} = $main::systime;
 
+       # ALWAYS output the hello
+       my $thing = Thingy::Hello->new(user => $call, h => $self->{here});
+       $thing->broadcast($self);
+       $self->lasthello($main::systime);
+       
        # send info to all logged in thingies
        $self->tell_login('loginn');
 
@@ -434,8 +438,8 @@ sub handle_10
 #      RouteDB::update($to, $_[6]);
 
        # it is here and logged on
-       $dxchan = DXChannel->get($main::myalias) if $to eq $main::mycall;
-       $dxchan = DXChannel->get($to) unless $dxchan;
+       $dxchan = DXChannel::get($main::myalias) if $to eq $main::mycall;
+       $dxchan = DXChannel::get($to) unless $dxchan;
        if ($dxchan && $dxchan->is_user) {
                $_[3] =~ s/\%5E/^/g;
                $dxchan->talk($from, $to, $via, $_[3]);
@@ -533,9 +537,9 @@ sub handle_11
        
        my @spot = Spot::prepare($_[1], $_[2], $d, $_[5], $_[6], $_[7]);
 
-       my $thing = Thingy::Dx->new(origin=>$main::mycall, group=>'DX');
+       my $thing = Thingy::Dx->new(origin=>$main::mycall);
        $thing->from_DXProt(DXProt=>$line,spotdata=>\@spot);
-       $thing->queue($self);
+       $thing->process($self);
 
        # this goes after the input filtering, but before the add
        # so that if it is input filtered, it isn't added to the dup
@@ -593,20 +597,6 @@ sub handle_11
                        }
                }
        }
-                               
-       # local processing 
-       my $r;
-       eval {
-               $r = Local::spot($self, @spot);
-       };
-       #                       dbg("Local::spot1 error $@") if isdbg('local') if $@;
-       return if $r;
-
-       # DON'T be silly and send on PC26s!
-       return if $pcno == 26;
-
-       # send out the filtered spots
-#      send_dx_spot($self, $line, @spot) if @spot;
 }
                
 # announces
@@ -647,7 +637,7 @@ sub handle_12
 
        my $dxchan;
        
-       if ((($dxchan = DXChannel->get($_[2])) && $dxchan->is_user) || $_[4] =~ /^[\#\w.]+$/){
+       if ((($dxchan = DXChannel::get($_[2])) && $dxchan->is_user) || $_[4] =~ /^[\#\w.]+$/){
                $self->send_chat($line, @_[1..6]);
        } elsif ($_[2] eq '*' || $_[2] eq $main::mycall) {
 
@@ -707,15 +697,14 @@ sub handle_16
                return;
        }
 
-
-       RouteDB::update($ncall, $origin);
+       RouteDB::update($ncall, $self->{call});
 
        # do we believe this call? 
-       unless ($ncall eq $origin || $self->is_believed($ncall)) {
-               if (my $ivp = Investigate::get($ncall, $origin)) {
+       unless ($ncall eq $self->{call} || $self->is_believed($ncall)) {
+               if (my $ivp = Investigate::get($ncall, $self->{call})) {
                        $ivp->store_pcxx($pcno,$line,$origin,@_);
                } else {
-                       dbg("PCPROT: We don't believe $ncall on $origin") if isdbg('chanerr');
+                       dbg("PCPROT: We don't believe $ncall on $self->{call}") if isdbg('chanerr');
                }
                return;
        }
@@ -729,59 +718,122 @@ sub handle_16
 
        # if there is a parent, proceed, otherwise if there is a latent PC19 in the PC19list, 
        # fix it up in the routing tables and issue it forth before the PC16
+       unless ($parent) {
+               my $nl = $pc19list{$ncall};
+
+               if ($nl && @_ > 3) { # 3 because of the hop count!
+
+                       # this is a new (remembered) node, now attach it to me if it isn't in filtered
+                       # and we haven't disallowed it
+                       my $user = DXUser->get_current($ncall);
+                       if (!$user) {
+                               $user = DXUser->new($ncall);
+                               $user->sort('A');
+                               $user->priv(1); # I have relented and defaulted nodes
+                               $user->lockout(1);
+                               $user->homenode($ncall);
+                               $user->node($ncall);
+                       }
+
+                       my $wantpc19 = $user->wantroutepc19;
+                       if ($wantpc19 || !defined $wantpc19) {
+                               my $new = Route->new($ncall); # throw away
+                               if ($self->in_filter_route($new)) {
+                                       my @nrout;
+                                       for (@$nl) {
+                                               $parent = Route::Node::get($_->[0]);
+                                               $dxchan = $parent->dxchan if $parent;
+                                               if ($dxchan && $dxchan ne $self) {
+                                                       dbg("PCPROT: PC19 from $self->{call} trying to alter locally connected $ncall, ignored!") if isdbg('chanerr');
+                                                       $parent = undef;
+                                               }
        if ($parent) {
+                                                       my $r = $parent->add($ncall, $_->[1], $_->[2]);
+                                                       push @nrout, $r unless @nrout;
+                                               }
+                                       }
+                                       $user->wantroutepc19(1) unless defined $wantpc19; # for now we work on the basis that pc16 = real route 
+                                       $user->lastin($main::systime) unless DXChannel::get($ncall);
+                                       $user->put;
+                                               
+                                       # route the pc19 - this will cause 'stuttering PC19s' for a while
+                                       $self->route_pc19($origin, $line, @nrout) if @nrout ;
+                                       $parent = Route::Node::get($ncall);
+                                       unless ($parent) {
+                                               dbg("PCPROT: lost $ncall after sending PC19 for it?");
+                                               return;
+                                       }
+                               } else {
+                                       return;
+                               }
+                               delete $pc19list{$ncall};
+                       }
+               } else {
+                       dbg("PCPROT: Node $ncall not in config") if isdbg('chanerr');
+                       return;
+               }
+       } else {
 
                $dxchan = $parent->dxchan;
                if ($dxchan && $dxchan ne $self) {
-                       dbg("PCPROT: PC16 from $origin trying to alter locally connected $ncall, ignored!") if isdbg('chanerr');
+                       dbg("PCPROT: PC16 from $self->{call} trying to alter locally connected $ncall, ignored!") if isdbg('chanerr');
                        return;
                }
 
+               # input filter if required
+               return unless $self->in_filter_route($parent);
+       }
+
+       # is he under the control of the new protocol?
+       if ($parent && $parent->np) {
+               dbg("PCPROT: $ncall aranea node, ignored") if isdbg('chanerr');
+               return;
+       }
                
-               my $i;
-               my @rout;
-               for ($i = 2; $i < $#_; $i++) {
-                       my ($call, $conf, $here) = $_[$i] =~ /^(\S+) (\S) (\d)/o;
-                       next unless $call && $conf && defined $here && is_callsign($call);
-                       next if $call eq $main::mycall;
-                       
-                       eph_del_regex("^PC17\\^$call\\^$ncall");
-                               
-                       $conf = 1;
-                               
-                       my $r = Route::User::get($call);
-                       my $flags = Route::here($here)|Route::conf($conf);
-                               
-                       if ($r) {
-                               my $au = $r->addparent($parent);                                        
-                               if ($r->flags != $flags) {
-                                       $r->flags($flags);
-                                       $au = $r;
-                               }
-                               push @rout, $r if $au;
-                       } else {
-                               push @rout, $parent->add_user($call, $flags);
-                       }
+       my $i;
+       my @rout;
+       for ($i = 2; $i < $#_; $i++) {
+               my ($call, $conf, $here) = $_[$i] =~ /^(\S+) (\S) (\d)/o;
+               next unless $call && $conf && defined $here && is_callsign($call);
+               next if $call eq $main::mycall;
                
-                               
-                       # add this station to the user database, if required
-                       $call =~ s/-\d+$//o;    # remove ssid for users
-                       my $user = DXUser->get_current($call);
-                       $user = DXUser->new($call) if !$user;
-                       $user->homenode($parent->call) if !$user->homenode;
-                       $user->node($parent->call);
-                       $user->lastin($main::systime) unless DXChannel->get($call);
-                       $user->put;
+               eph_del_regex("^PC17\\^$call\\^$ncall");
+               
+               $conf = $conf eq '*';
+               
+               # reject this if we think it is a node already
+               my $r = Route::Node::get($call);
+               my $u = DXUser->get_current($call) unless $r;
+               if ($r || ($u && $u->is_node)) {
+                       dbg("PCPROT: $call is a node") if isdbg('chanerr');
+                       next;
                }
-
-               if (@rout) {
-                       my $thing = Thingy::Dx->new(origin=>$main::mycall);
-                       $thing->from_DXProt(DXProt=>$line,originref=>$parent);
-                       $thing->queue($self);
+               
+               $r = Route::User::get($call);
+               my $flags = $here;
+               
+               if ($r) {
+                       my $au = $r->addparent($parent);                                        
+                       if ($r->flags != $flags) {
+                               $r->flags($flags);
+                               $au = $r;
+                       }
+                       push @rout, $r if $au;
                } else {
-                       dbg("PCPROT: No usable users") if isdbg('chanerr');
+                       push @rout, $parent->add_user($call, $flags);
                }
+               
+               
+               # add this station to the user database, if required
+               $call =~ s/-\d+$//o;    # remove ssid for users
+               my $user = DXUser->get_current($call);
+               $user = DXUser->new($call) if !$user;
+               $user->homenode($parent->call) if !$user->homenode;
+               $user->node($parent->call);
+               $user->lastin($main::systime) unless DXChannel::get($call);
+               $user->put;
        }
+       $self->route_pc16($origin, $line, $parent, @rout) if @rout;
 }
                
 # remove a user
@@ -799,7 +851,7 @@ sub handle_17
                        
        # do I want users from this channel?
        unless ($self->user->wantpc16) {
-               dbg("PCPROT: don't send users to $origin") if isdbg('chanerr');
+               dbg("PCPROT: don't send users to $self->{call}") if isdbg('chanerr');
                return;
        }
        if ($ncall eq $main::mycall) {
@@ -807,14 +859,14 @@ sub handle_17
                return;
        }
 
-       RouteDB::delete($ncall, $origin);
+       RouteDB::delete($ncall, $self->{call});
 
        # do we believe this call? 
-       unless ($ncall eq $origin || $self->is_believed($ncall)) {
-               if (my $ivp = Investigate::get($ncall, $origin)) {
+       unless ($ncall eq $self->{call} || $self->is_believed($ncall)) {
+               if (my $ivp = Investigate::get($ncall, $self->{call})) {
                        $ivp->store_pcxx($pcno,$line,$origin,@_);
                } else {
-                       dbg("PCPROT: We don't believe $ncall on $origin") if isdbg('chanerr');
+                       dbg("PCPROT: We don't believe $ncall on $self->{call}") if isdbg('chanerr');
                }
                return;
        }
@@ -830,7 +882,13 @@ sub handle_17
 
        $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');
+               dbg("PCPROT: PC17 from $self->{call} trying to alter locally connected $ncall, ignored!") if isdbg('chanerr');
+               return;
+       }
+
+       # is he under the control of the new protocol?
+       if ($parent && $parent->np) {
+               dbg("PCPROT: $ncall aranea node, ignored") if isdbg('chanerr');
                return;
        }
 
@@ -862,8 +920,8 @@ sub handle_18
 
        # 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->version(0 + $1);
+               $self->user->version(0 + $1);
                $self->build(0 + $2);
                $self->user->build(0 + $2);
                unless ($self->is_spider) {
@@ -911,7 +969,7 @@ sub handle_19
        if ($origin ne $self->call) {
                my $op = Route::Node::get($origin);
                unless ($op) {
-                       $op = $parent->add($origin, 5000, Route::here(1));
+                       $op = $parent->add($origin, 5000, 1);
                        my $user = DXUser->get_current($origin);
                        if (!$user) {
                                $user = DXUser->new($origin);
@@ -944,7 +1002,7 @@ sub handle_19
                next if $call eq $main::mycall;
 
                # check that this PC19 isn't trying to alter the wrong dxchan
-               my $dxchan = DXChannel->get($call);
+               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;
@@ -985,8 +1043,13 @@ sub handle_19
                }
 
                my $r = Route::Node::get($call);
-               my $flags = Route::here($here)|Route::conf($conf);
+               my $flags = $here;
 
+               # is he under the control of the new protocol?
+               if ($r && $r->np) {
+                       dbg("PCPROT: $call aranea node, ignored") if isdbg('chanerr');
+                       next;
+               }
                # modify the routing table if it is in it, otherwise store it in the pc19list for now
                if ($r) {
                        my $ar;
@@ -998,11 +1061,6 @@ sub handle_19
                                        next;
                                }
                        }
-                       if ($r->version ne $ver || $r->flags != $flags) {
-                               $r->version($ver);
-                               $r->flags($flags);
-                               push @rout, $r unless $ar;
-                       }
                } else {
 
                        # if he is directly connected or allowed then add him, otherwise store him up for later
@@ -1026,7 +1084,7 @@ sub handle_19
                my $mref = DXMsg::get_busy($call);
                $mref->stop_msg($call) if $mref;
                                
-               $user->lastin($main::systime) unless DXChannel->get($call);
+               $user->lastin($main::systime) unless DXChannel::get($call);
                $user->put;
        }
 
@@ -1045,6 +1103,11 @@ sub handle_20
        $self->send(pc22());
        $self->state('normal');
        $self->{lastping} = 0;
+       my $thing = Thingy::Rt->new(user=>$self->{call});
+       my $nref = Route::Node::get($self->{call});
+       $thing->copy_pc16_data($nref);
+       $thing->broadcast($self);
+       $self->lastcf($main::systime);
 }
                
 # delete a cluster from the list
@@ -1095,7 +1158,7 @@ sub handle_21
                        my $node = Route::Node::get($call);
                        if ($node) {
                                                
-                               my $dxchan = DXChannel->get($call);
+                               my $dxchan = DXChannel::get($call);
                                if ($dxchan && $dxchan != $self) {
                                        dbg("PCPROT: PC21 from $origin trying to alter locally connected $call, ignored!") if isdbg('chanerr');
                                        return;
@@ -1103,6 +1166,12 @@ sub handle_21
                                                
                                # input filter it
                                return unless $self->in_filter_route($node);
+
+                               # is he under the control of the new protocol?
+                               if ($node->np) {
+                                       dbg("PCPROT: $call aranea node, ignored") if isdbg('chanerr');
+                                       return;
+                               }
                                                
                                # routing objects
                                push @rout, $node->del($parent);
@@ -1125,6 +1194,11 @@ sub handle_22
        my $origin = shift;
        $self->state('normal');
        $self->{lastping} = 0;
+       my $thing = Thingy::Rt->new(user=>$self->{call});
+       my $nref = Route::Node::get($self->{call});
+       $thing->copy_pc16_data($nref);
+       $thing->broadcast($self);
+       $self->lastcf($main::systime);
 }
                                
 # WWV info
@@ -1464,70 +1538,15 @@ sub handle_51
        my $pcno = shift;
        my $line = shift;
        my $origin = shift;
-       my $to = $_[1];
-       my $from = $_[2];
-       my $flag = $_[3];
 
-                       
-       # is it for us?
-       if ($to eq $main::mycall) {
-               if ($flag == 1) {
-                       $self->send(pc51($from, $to, '0'));
-               } else {
-                       # it's a reply, look in the ping list for this one
-                       my $ref = $pings{$from};
-                       if ($ref) {
-                               my $tochan =  DXChannel->get($from);
-                               while (@$ref) {
-                                       my $r = shift @$ref;
-                                       my $dxchan = DXChannel->get($r->{call});
-                                       next unless $dxchan;
-                                       my $t = tv_interval($r->{t}, [ gettimeofday ]);
-                                       if ($dxchan->is_user) {
-                                               my $s = sprintf "%.2f", $t; 
-                                               my $ave = sprintf "%.2f", $tochan ? ($tochan->{pingave} || $t) : $t;
-                                               $dxchan->send($dxchan->msg('pingi', $from, $s, $ave))
-                                       } elsif ($dxchan->is_node) {
-                                               if ($tochan) {
-                                                       my $nopings = $tochan->user->nopings || $obscount;
-                                                       push @{$tochan->{pingtime}}, $t;
-                                                       shift @{$tochan->{pingtime}} if @{$tochan->{pingtime}} > 6;
-                                                       
-                                                       # cope with a missed ping, this means you must set the pingint large enough
-                                                       if ($t > $tochan->{pingint}  && $t < 2 * $tochan->{pingint} ) {
-                                                               $t -= $tochan->{pingint};
-                                                       }
-                                                       
-                                                       # calc smoothed RTT a la TCP
-                                                       if (@{$tochan->{pingtime}} == 1) {
-                                                               $tochan->{pingave} = $t;
-                                                       } else {
-                                                               $tochan->{pingave} = $tochan->{pingave} + (($t - $tochan->{pingave}) / 6);
-                                                       }
-                                                       $tochan->{nopings} = $nopings; # pump up the timer
-                                                       if (my $ivp = Investigate::get($from, $origin)) {
-                                                               $ivp->handle_ping;
-                                                       }
-                                               } elsif (my $rref = Route::Node::get($r->{call})) {
-                                                       if (my $ivp = Investigate::get($from, $origin)) {
-                                                               $ivp->handle_ping;
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-               }
-       } else {
-
-               RouteDB::update($from, $origin);
-
-               if (eph_dup($line)) {
-                       dbg("PCPROT: dup PC51 detected") if isdbg('chanerr');
-                       return;
-               }
-               # route down an appropriate thingy
-               $self->route($to, $line);
+       if (eph_dup($line, 60)) {
+               dbg("PCPROT: dup PC51 detected") if isdbg('chanerr');
+               return;
        }
+
+       my $thing = Thingy::Ping->new(origin=>$main::mycall);
+       $thing->from_DXProt($self, $line, @_);
+       $thing->handle($self);
 }
 
 # dunno but route it
@@ -1628,7 +1647,7 @@ sub handle_default
 sub process
 {
        my $t = time;
-       my @dxchan = DXChannel->get_all();
+       my @dxchan = DXChannel::get_all();
        my $dxchan;
        my $pc50s;
        
@@ -1710,7 +1729,7 @@ sub send_wwv_spot
 {
        my $self = shift;
        my $line = shift;
-       my @dxchan = DXChannel->get_all();
+       my @dxchan = DXChannel::get_all();
        my $dxchan;
        my @dxcc = ((Prefix::cty_data($_[6]))[0..2], (Prefix::cty_data($_[7]))[0..2]);
 
@@ -1744,7 +1763,7 @@ sub send_wcy_spot
 {
        my $self = shift;
        my $line = shift;
-       my @dxchan = DXChannel->get_all();
+       my @dxchan = DXChannel::get_all();
        my $dxchan;
        my @dxcc = ((Prefix::cty_data($_[10]))[0..2], (Prefix::cty_data($_[11]))[0..2]);
        
@@ -1777,7 +1796,7 @@ sub send_announce
 {
        my $self = shift;
        my $line = shift;
-       my @dxchan = DXChannel->get_all();
+       my @dxchan = DXChannel::get_all();
        my $dxchan;
        my $target;
        my $to = 'To ';
@@ -1842,7 +1861,7 @@ sub send_chat
 {
        my $self = shift;
        my $line = shift;
-       my @dxchan = DXChannel->get_all();
+       my @dxchan = DXChannel::get_all();
        my $dxchan;
        my $target = $_[3];
        my $text = unpad($_[2]);
@@ -1938,7 +1957,7 @@ sub send_local_config
         # don't appear outside of this node
 
                # send locally connected nodes
-               my @dxchan = grep { $_->call ne $main::mycall && $_ != $self && !$_->{isolate} } DXChannel::get_all_nodes();
+               my @dxchan = grep { $_->call ne $main::mycall && $_ != $self && !$_->{isolate} && ($_->is_node || $_->is_aranea) } DXChannel::get_all();
                @localnodes = map { my $r = Route::Node::get($_->{call}); $r ? $r : () } @dxchan if @dxchan;
                $self->send_route($main::mycall, \&pc19, scalar(@localnodes)+1, $main::routeroot, @localnodes);
 
@@ -1983,7 +2002,7 @@ sub route
        }
 
        # always send it down the local interface if available
-       my $dxchan = DXChannel->get($call);
+       my $dxchan = DXChannel::get($call);
        if ($dxchan) {
                dbg("route: $call -> $dxchan->{call} direct" ) if isdbg('route');
        } else {
@@ -2006,7 +2025,7 @@ sub route
                                dbg("PCPROT: Trying to route back to source, dropped") if isdbg('chanerr');
                                return;
                        }
-                       $dxchan = DXChannel->get($rcall);
+                       $dxchan = DXChannel::get($rcall);
                        dbg("route: $call -> $rcall using RouteDB" ) if isdbg('route') && $dxchan;
                }
        }
@@ -2083,22 +2102,9 @@ sub load_hops
 sub addping
 {
        my ($from, $to, $via) = @_;
-       my $ref = $pings{$to} || [];
-       my $r = {};
-       $r->{call} = $from;
-       $r->{t} = [ gettimeofday ];
-       if ($via && (my $dxchan = DXChannel->get($via))) {
-               $dxchan->send(pc51($to, $main::mycall, 1));
-       } else {
-               route(undef, $to, pc51($to, $main::mycall, 1));
-       }
-       push @$ref, $r;
-       $pings{$to} = $ref;
-       my $u = DXUser->get_current($to);
-       if ($u) {
-               $u->lastping(($via || $from), $main::systime);
-               $u->put;
-       }
+       my $thing = Thingy::Ping->new_ping($from eq $main::mycall ? () : (user=>$from), $via ? (touser=> $to, group => $via) : (group => $to));
+       $thing->remember;
+       $thing->broadcast;
 }
 
 sub process_rcmd
@@ -2139,13 +2145,13 @@ sub process_rcmd_reply
        if ($tonode eq $main::mycall) {
                my $s = $rcmds{$fromnode};
                if ($s) {
-                       my $dxchan = DXChannel->get($s->{call});
-                       my $ref = $user eq $tonode ? $dxchan : (DXChannel->get($user) || $dxchan);
+                       my $dxchan = DXChannel::get($s->{call});
+                       my $ref = $user eq $tonode ? $dxchan : (DXChannel::get($user) || $dxchan);
                        $ref->send($line) if $ref;
                        delete $rcmds{$fromnode} if !$dxchan;
                } else {
                        # send unsolicited ones to the sysop
-                       my $dxchan = DXChannel->get($main::myalias);
+                       my $dxchan = DXChannel::get($main::myalias);
                        $dxchan->send($line) if $dxchan;
                }
        } else {
@@ -2245,11 +2251,14 @@ sub disconnect
        
        # broadcast to all other nodes that all the nodes connected to via me are gone
        unless ($pc39flag && $pc39flag == 2) {
+               my $thing = Thingy::Bye->new(user=>$call);
+               $thing->broadcast($self);
+
                $self->route_pc21($main::mycall, undef, @rout) if @rout;
        }
 
        # remove outstanding pings
-       delete $pings{$call};
+       Thingy::Ping::forget($call);
        
        # I was the last node visited
     $self->user->node($main::mycall);