+
+ if (AnnTalk::dup($_[0], $_[1], $_[2])) {
+ dbg("PCPROT: Duplicate Announce ignored") if isdbg('chanerr');
+ return;
+ }
+
+ Log('ann', $target, $_[0], $text);
+
+ # send it if it isn't the except list and isn't isolated and still has a hop count
+ # taking into account filtering and so on
+ foreach $dxchan (@dxchan) {
+ next if $dxchan == $main::me;
+ next if $dxchan == $self && $self->is_node;
+ $dxchan->announce($line, $self->{isolate}, $to, $target, $text, @_, $self->{call},
+ @a[0..2], @b[0..2]);
+ }
+}
+
+my $msgid = 0;
+
+sub nextchatmsgid
+{
+ $msgid++;
+ $msgid = 1 if $msgid > 999;
+ return $msgid;
+}
+
+# send a chat line
+sub send_chat
+{
+ my $self = shift;
+ my $line = shift;
+ my @dxchan = DXChannel::get_all();
+ my $dxchan;
+ my $target = $_[3];
+ my $text = unpad($_[2]);
+ my $ak1a_line;
+
+ # munge the group and recast the line if required
+ if ($target =~ s/\.LST$//) {
+ $ak1a_line = $line;
+ }
+
+ # obtain country codes etc
+ my @a = Prefix::cty_data($_[0]);
+ my @b = Prefix::cty_data($_[4]);
+ if ($self->{inannfilter}) {
+ my ($filter, $hops) =
+ $self->{inannfilter}->it(@_, $self->{call},
+ @a[0..2],
+ @b[0..2], $a[3], $b[3]);
+ unless ($filter) {
+ dbg("PCPROT: Rejected by input announce filter") if isdbg('chanerr');
+ return;
+ }
+ }
+
+ if (AnnTalk::dup($_[0], $_[1], $_[2], $chatdupeage)) {
+ dbg("PCPROT: Duplicate Announce ignored") if isdbg('chanerr');
+ return;
+ }
+
+
+ Log('chat', $target, $_[0], $text);
+
+ # send it if it isn't the except list and isn't isolated and still has a hop count
+ # taking into account filtering and so on
+ foreach $dxchan (@dxchan) {
+ my $is_ak1a = $dxchan->is_ak1a;
+
+ if ($dxchan->is_node) {
+ next if $dxchan == $main::me;
+ next if $dxchan == $self;
+ next unless $dxchan->is_spider || $is_ak1a;
+ next if $target eq 'LOCAL';
+ if (!$ak1a_line && $is_ak1a) {
+ $ak1a_line = DXProt::pc12($_[0], $text, $_[1], "$target.LST");
+ }
+ }
+
+ $dxchan->chat($is_ak1a ? $ak1a_line : $line, $self->{isolate}, $target, $_[1],
+ $text, @_, $self->{call}, @a[0..2], @b[0..2]);
+ }
+}
+
+sub announce
+{
+ my $self = shift;
+ my $line = shift;
+ my $isolate = shift;
+ my $to = shift;
+ my $target = shift;
+ my $text = shift;
+ my ($filter, $hops);
+
+ if ($self->{annfilter}) {
+ ($filter, $hops) = $self->{annfilter}->it(@_);
+ return unless $filter;
+ }
+ send_prot_line($self, $filter, $hops, $isolate, $line) unless $_[1] eq $main::mycall;
+}
+
+sub chat
+{
+ goto &announce;
+}
+
+
+sub send_local_config
+{
+ my $self = shift;
+ my $node;
+ my @nodes;
+ my @localnodes;
+ my @remotenodes;
+
+ dbg('DXProt::send_local_config') if isdbg('trace');
+
+ # send our nodes
+ if ($self->{isolate}) {
+ @localnodes = ( $main::routeroot );
+ $self->send_route($main::mycall, \&pc19, 1, $main::routeroot);
+ } else {
+ # create a list of all the nodes that are not connected to this connection
+ # and are not themselves isolated, this to make sure that isolated nodes
+ # don't appear outside of this node
+
+ # send locally connected nodes
+ my @dxchan = grep { $_->call ne $main::mycall && $_ != $self && !$_->{isolate} } DXChannel::get_all_nodes();
+ @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);
+
+ my $node;
+ my @rawintcalls = map { $_->nodes } @localnodes if @localnodes;
+ my @intcalls;
+ for $node (@rawintcalls) {
+ push @intcalls, $node unless grep $node eq $_, @intcalls;
+ }
+ my $ref = Route::Node::get($self->{call});
+ my @rnodes = $ref->nodes;
+ for $node (@intcalls) {
+ push @remotenodes, Route::Node::get($node) unless grep $node eq $_, @rnodes, @remotenodes;
+ }
+ $self->send_route($main::mycall, \&pc19, scalar(@remotenodes), @remotenodes);
+ }
+
+ # get all the users connected on the above nodes and send them out
+ foreach $node ($main::routeroot, @localnodes, @remotenodes) {
+ if ($node) {
+ my @rout = map {my $r = Route::User::get($_); $r ? ($r) : ()} $node->users;
+ $self->send_route($main::mycall, \&pc16, 1, $node, @rout) if @rout && $self->user->wantsendpc16;
+ } else {
+ dbg("sent a null value") if isdbg('chanerr');
+ }
+ }
+}
+
+#
+# route a message down an appropriate interface for a callsign
+#
+# is called route(to, pcline);
+#
+
+sub route
+{
+ my ($self, $call, $line) = @_;
+
+ if (ref $self && $call eq $self->{call}) {
+ dbg("PCPROT: Trying to route back to source, dropped") if isdbg('chanerr');
+ return;
+ }
+
+ # always send it down the local interface if available
+ my $dxchan = DXChannel::get($call);
+ if ($dxchan) {
+ dbg("route: $call -> $dxchan->{call} direct" ) if isdbg('route');
+ } else {
+ my $cl = Route::get($call);
+ $dxchan = $cl->dxchan if $cl;
+ if (ref $dxchan) {
+ if (ref $self && $dxchan eq $self) {
+ dbg("PCPROT: Trying to route back to source, dropped") if isdbg('chanerr');
+ return;
+ }
+ dbg("route: $call -> $dxchan->{call} using normal route" ) if isdbg('route');
+ }
+ }
+
+ # try the backstop method
+ unless ($dxchan) {
+ my $rcall = RouteDB::get($call);
+ if ($rcall) {
+ if ($self && $rcall eq $self->{call}) {
+ dbg("PCPROT: Trying to route back to source, dropped") if isdbg('chanerr');
+ return;
+ }
+ $dxchan = DXChannel::get($rcall);
+ dbg("route: $call -> $rcall using RouteDB" ) if isdbg('route') && $dxchan;
+ }
+ }
+
+ if ($dxchan) {
+ my $routeit = adjust_hops($dxchan, $line); # adjust its hop count by node name
+ if ($routeit) {
+ $dxchan->send($routeit) unless $dxchan == $main::me;
+ }
+ } else {
+ dbg("PCPROT: No route available, dropped") if isdbg('chanerr');
+ }
+}
+
+#
+# obtain the hops from the list for this callsign and pc no
+#
+
+sub get_hops
+{
+ my $pcno = shift;
+ my $hops = $DXProt::hopcount{$pcno};
+ $hops = $DXProt::def_hopcount if !$hops;
+ return "H$hops";
+}
+
+#
+# adjust the hop count on a per node basis using the user loadable
+# hop table if available or else decrement an existing one
+#
+
+sub adjust_hops
+{
+ my $self = shift;
+ my $s = shift;
+ my $call = $self->{call};
+ my $hops;
+
+ if (($hops) = $s =~ /\^H(\d+)\^~?$/o) {
+ my ($pcno) = $s =~ /^PC(\d\d)/o;
+ confess "$call called adjust_hops with '$s'" unless $pcno;
+ my $ref = $nodehops{$call} if %nodehops;
+ if ($ref) {
+ my $newhops = $ref->{$pcno};
+ return "" if defined $newhops && $newhops == 0;
+ $newhops = $ref->{default} unless $newhops;
+ return "" if defined $newhops && $newhops == 0;
+ $newhops = $hops if !$newhops;
+ $s =~ s/\^H(\d+)(\^~?)$/\^H$newhops$2/ if $newhops;
+ } else {
+ # simply decrement it
+ $hops--;
+ return "" if !$hops;
+ $s =~ s/\^H(\d+)(\^~?)$/\^H$hops$2/ if $hops;
+ }
+ }
+ return $s;
+}
+
+#
+# load hop tables
+#
+sub load_hops
+{
+ my $self = shift;
+ return $self->msg('lh1') unless -e "$main::data/hop_table.pl";
+ do "$main::data/hop_table.pl";
+ return $@ if $@;
+ return ();
+}
+
+
+# add a ping request to the ping queues
+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;
+ }
+}
+
+sub process_rcmd
+{
+ my ($self, $tonode, $fromnode, $user, $cmd) = @_;
+ if ($tonode eq $main::mycall) {
+ my $ref = DXUser->get_current($fromnode);
+ my $cref = Route::Node::get($fromnode);
+ Log('rcmd', 'in', $ref->{priv}, $fromnode, $cmd);
+ if ($cmd !~ /^\s*rcmd/i && $cref && $ref && $cref->call eq $ref->homenode) { # not allowed to relay RCMDS!
+ if ($ref->{priv}) { # you have to have SOME privilege, the commands have further filtering
+ $self->{remotecmd} = 1; # for the benefit of any command that needs to know
+ my $oldpriv = $self->{priv};
+ $self->{priv} = $ref->{priv}; # assume the user's privilege level
+ my @in = (DXCommandmode::run_cmd($self, $cmd));
+ $self->{priv} = $oldpriv;
+ $self->send_rcmd_reply($main::mycall, $fromnode, $user, @in);
+ delete $self->{remotecmd};
+ } else {
+ $self->send_rcmd_reply($main::mycall, $fromnode, $user, "sorry...!");
+ }
+ } else {
+ $self->send_rcmd_reply($main::mycall, $fromnode, $user, "your attempt is logged, Tut tut tut...!");
+ }
+ } else {
+ my $ref = DXUser->get_current($tonode);
+ if ($ref && $ref->is_clx) {
+ $self->route($tonode, pc84($fromnode, $tonode, $user, $cmd));
+ } else {
+ $self->route($tonode, pc34($fromnode, $tonode, $cmd));
+ }
+ }
+}
+
+sub process_rcmd_reply
+{
+ my ($self, $tonode, $fromnode, $user, $line) = @_;
+ 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);
+ $ref->send($line) if $ref;
+ delete $rcmds{$fromnode} if !$dxchan;
+ } else {
+ # send unsolicited ones to the sysop
+ my $dxchan = DXChannel::get($main::myalias);
+ $dxchan->send($line) if $dxchan;
+ }
+ } else {
+ my $ref = DXUser->get_current($tonode);
+ if ($ref && $ref->is_clx) {
+ $self->route($tonode, pc85($fromnode, $tonode, $user, $line));
+ } else {
+ $self->route($tonode, pc35($fromnode, $tonode, $line));
+ }
+ }
+}
+
+sub send_rcmd_reply
+{
+ my $self = shift;
+ my $tonode = shift;
+ my $fromnode = shift;
+ my $user = shift;
+ while (@_) {
+ my $line = shift;
+ $line =~ s/\s*$//;
+ Log('rcmd', 'out', $fromnode, $line);
+ if ($self->is_clx) {
+ $self->send(pc85($main::mycall, $fromnode, $user, "$main::mycall:$line"));
+ } else {
+ $self->send(pc35($main::mycall, $fromnode, "$main::mycall:$line"));
+ }
+ }
+}
+
+# add a rcmd request to the rcmd queues
+sub addrcmd
+{
+ my ($self, $to, $cmd) = @_;
+
+ my $r = {};
+ $r->{call} = $self->{call};
+ $r->{t} = $main::systime;
+ $r->{cmd} = $cmd;
+ $rcmds{$to} = $r;
+
+ my $ref = Route::Node::get($to);
+ my $dxchan = $ref->dxchan;
+ if ($dxchan && $dxchan->is_clx) {
+ route(undef, $to, pc84($main::mycall, $to, $self->{call}, $cmd));
+ } else {
+ route(undef, $to, pc34($main::mycall, $to, $cmd));