+ if ($_[1] eq $main::mycall) {
+ DXMsg::handle_49($self, @_);
+ } else {
+ $self->route($_[1], $line) unless $self->is_clx;
+ }
+}
+
+# keep alive/user list
+sub handle_50
+{
+ my $self = shift;
+ my $pcno = shift;
+ my $line = shift;
+ my $origin = shift;
+
+ my $call = $_[1];
+ my $node = Route::Node::get($call);
+ if ($node) {
+ return unless $node->call eq $self->{call};
+ $node->usercount($_[2]);
+
+ # input filter if required
+ return unless $self->in_filter_route($node);
+
+ $self->route_pc50($origin, $line, $node, $_[2], $_[3]) unless eph_dup($line);
+ }
+}
+
+# incoming ping requests/answers
+sub handle_51
+{
+ my $self = shift;
+ 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
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (eph_dup($line)) {
+ dbg("PCPROT: dup PC51 detected") if isdbg('chanerr');
+ return;
+ }
+ # route down an appropriate thingy
+ $self->route($to, $line);
+ }
+}
+
+# New style routing handler
+sub handle_59
+{
+ my $self = shift;
+ my $pcno = shift;
+ my $line = shift;
+ my $origin = shift;
+
+ my ($sort, $hextime, $ncall) = @_[1,2,3];
+ if ($ncall eq $main::mycall) {
+ dbg("PCPROT: ignoring PC59 for me") if isdbg('chan');
+ return;
+ }
+
+ # do this once for filtering with a throwaway routing entry if a new node
+ my $fnode = Route::Node::get($ncall) || Route::new($ncall);
+ return unless $self->in_filter_route($fnode);
+
+ return if eph_dup($line);
+
+ # mark myself as NewRoute if I get a PC59
+ $self->{newroute} = 1 if $ncall eq $self->{call};
+
+ # now do it properly for actions
+ my $node = Route::Node::get($ncall) || Route::Node->new($ncall);
+ $node->newroute(1);
+
+ # find each of the entries (or create new ones)
+ my @refs;
+ for my $ent (@_[4..$#_]) {
+ next if $ent =~ /^H\d+$/;
+
+ my ($esort, $ehere, $ecall) = unpack "A A A*", $ent;
+ my $ref;
+
+ next unless $esort && defined $ehere && $ecall;
+
+ # create user, if required
+ my $user = DXUser->get_current($ecall);
+ unless ($user) {
+ $user = DXUser->new($ecall);
+ $user->sort();
+ $user->priv(1); # I have relented and defaulted nodes
+ $user->lockout(1);
+ $user->homenode($ncall);
+ $user->node($ncall);
+ }
+ if ($esort eq 'U') {
+ $ref = Route::User::get($ecall);
+ unless ($ref) {
+ # create user, if required
+ my $user = DXUser->get_current($ecall);
+ unless ($user) {
+ $user = DXUser->new($ecall);
+ $user->sort('U');
+ $user->homenode($ncall);
+ $user->node($ncall);
+ $user->put;
+ }
+ $ref = Route::User->new($ecall, 0);
+ }
+ } elsif ($esort eq 'N') {
+ $ref = Route::Node::get($ecall);
+ unless ($ref) {
+ # create user, if required
+ my $user = DXUser->get_current($ecall);
+ unless ($user) {
+ $user = DXUser->new($ecall);
+ $user->priv(1); # I have relented and defaulted nodes
+ $user->lockout(1);
+ $user->sort('A');
+ $user->homenode($ncall);
+ $user->node($ncall);
+ $user->put;
+ }
+ $ref = Route::Node->new($ecall, 0);
+ }
+ } else {
+ dbg("DXPROT: unknown entity type '$esort' on $ecall for node $ncall") if isdbg('chan');
+ next;
+ }
+ $ref->here($ehere); # might as well set this here
+ $ref->lastseen($main::systime);
+ push @refs, $ref;
+ }
+
+ # if it is a delete, disconnect all the entries mentioned
+ # from this node (which is a parent in this context).
+ my @delnode;
+ my @deluser;
+ if ($sort eq 'D') {
+ for my $ref (@refs) {
+ next if $ref->call eq $ncall;
+ next if $ref->call eq $main::mycall;
+ if ($ref->isa('Route::Node')) {
+ push @delnode, $node->unlink_node($ref, $self);
+ } elsif ($ref->isa('Route::User')) {
+ push @deluser, $node->del_user($ref);
+ }
+ }
+ }
+
+ # if it is an add, connect all the entries
+ my @addnode;
+ my @adduser;
+ if ($sort eq 'A') {
+ for my $ref (@refs) {
+ next if $ref->call eq $ncall;
+ next if $ref->call eq $main::mycall;
+ if ($ref->isa('Route::Node')) {
+ my $new = $node->link_node($ref, $self);
+ push @addnode, $new if $new;
+ } elsif ($ref->isa('Route::User')) {
+ push @adduser, $node->del_user($ref);
+ }
+ }
+ }
+
+ # if it is a configure, unlink all the nodes and users that
+ # are not in @refs but are in the node, then add all the
+ # nodes and users that are @refs but not in the node.
+ #
+ if ($sort eq 'C') {
+ my @dn;
+ my @du;
+ my @an;
+ my @au;
+ for my $r (map {Route::Node::get($_)} $node->nodes) {
+ next unless $r;
+ next if $r->call eq $ncall;
+ next if $r->call eq $main::mycall;
+ push @dn, $r unless grep $_->call eq $r->call, @refs;
+ }
+ for my $r (map {Route::User::get($_)} $node->users) {
+ next unless $r;
+ push @du, $r unless grep $_->call eq $r->call, @refs;
+ }
+ for my $r (@refs) {
+ next unless $r;
+ next if $r->call eq $ncall;
+ next if $r->call eq $main::mycall;
+ if ($r->isa('Route::Node')) {
+ push @an, $r unless grep $r->call eq $_, $node->nodes;
+ } elsif ($r->isa('Route::User')) {
+ push @au, $r unless grep $r->call eq $_, $node->users;
+ }
+ }
+ push @delnode, $node->unlink_node($_, $self) for @dn;
+ push @deluser, $node->del_user($_) for @du;
+ push @addnode, $node->link_node($_, $self) for @an;
+ push @adduser, $node->add_user($_) for @au;
+ }
+
+ $self->route_pc21($origin, $line, @delnode) if @delnode;
+ $self->route_pc19($origin, $line, @addnode) if @addnode;
+ $self->route_pc17($origin, $line, @deluser) if @deluser;
+ $self->route_pc16($origin, $line, @adduser) if @adduser;