simplified
[spider.git] / perl / DXCommandmode.pm
index c01085a548693a996576b80b16732209435e8be0..5f89ac2369f7e28b48bec355ceacb12ad0627278 100644 (file)
@@ -31,10 +31,17 @@ use WCY;
 use Sun;
 use Internet;
 use Script;
-
+use Net::Telnet;
+use QSL;
+use DB_File;
+use VE7CC;
+use Thingy;
+use Thingy::Dx;
+use Thingy::Hello;
+use Thingy::Bye;
 
 use strict;
-use vars qw(%Cache %cmd_cache $errstr %aliases $scriptbase $maxerrors %nothereslug $maxbadcount);
+use vars qw(%Cache %cmd_cache $errstr %aliases $scriptbase $maxerrors %nothereslug $maxbadcount $msgpolltime);
 
 %Cache = ();                                   # cache of dynamically loaded routine's mod times
 %cmd_cache = ();                               # cache of short names
@@ -43,13 +50,12 @@ $errstr = ();                                       # error string from eval
 $scriptbase = "$main::root/scripts"; # the place where all users start scripts go
 $maxerrors = 20;                               # the maximum number of concurrent errors allowed before disconnection
 $maxbadcount = 3;                              # no of bad words allowed before disconnection
+$msgpolltime = 3600;                   # the time between polls for new messages 
 
 
 use vars qw($VERSION $BRANCH);
-$VERSION = sprintf( "%d.%03d", q$Revision$ =~ /(\d+)\.(\d+)/ );
-$BRANCH = sprintf( "%d.%03d", q$Revision$ =~ /\d+\.\d+\.(\d+)\.(\d+)/ ) || 0;
-$main::build += $VERSION;
-$main::branch += $BRANCH;
+
+main::mkver($VERSION = q$Revision$);
 
 #
 # obtain a new connection this is derived from dxchannel
@@ -62,9 +68,11 @@ sub new
        # routing, this must go out here to prevent race condx
        my $pkg = shift;
        my $call = shift;
-       my @rout = $main::routeroot->add_user($call, Route::here(1));
-       DXProt::route_pc16($main::me, $main::routeroot, @rout) if @rout;
+       my @rout = $main::routeroot->add_user($call, 1);
 
+       
+       my $ref = Route::User::get($call);
+       $main::me->route_pc16($main::mycall, undef, $main::routeroot, $ref) if $ref;
        return $self;
 }
 
@@ -89,7 +97,7 @@ sub start
        $self->{priv} = $user->priv || 0;
        $self->{lang} = $user->lang || $main::lang || 'en';
        $self->{pagelth} = $user->pagelth || 20;
-       ($self->{width}) = $line =~ /width=(\d+)/;
+       ($self->{width}) = $line =~ /width=(\d+)/; $line =~ s/\s*width=\d+\s*//;
        $self->{width} = 80 unless $self->{width} && $self->{width} > 80;
        $self->{consort} = $line;       # save the connection type
        
@@ -104,17 +112,30 @@ sub start
        $self->{logininfo} = $user->wantlogininfo;
        $self->{ann_talk} = $user->wantann_talk;
        $self->{here} = 1;
+       $self->{prompt} = $user->prompt if $user->prompt;
+
+       # sort out new dx spot stuff
+       $user->wantdxcq(0) unless defined $user->{wantdxcq};
+       $user->wantdxitu(0) unless defined $user->{wantdxitu};  
+       $user->wantusstate(0) unless defined $user->{wantusstate};
 
        # sort out registration
-       if ($main::reqreg) {
+       if ($main::reqreg == 1) {
                $self->{registered} = $user->registered;
+       } elsif ($main::reqreg == 2) {
+               $self->{registered} = !$user->registered;
        } else {
                $self->{registered} = 1;
        }
 
 
        # decide which motd to send
-       my $motd = "${main::motd}_nor" unless $self->{registered};
+       my $motd;
+       unless ($self->{registered}) {
+               $motd = "${main::motd}_nor_$self->{lang}";
+               $motd = "${main::motd}_nor" unless -e $motd;
+       }
+       $motd = "${main::motd}_$self->{lang}" unless $motd && -e $motd;
        $motd = $main::motd unless $motd && -e $motd;
        $self->send_file($motd) if -e $motd;
 
@@ -149,11 +170,16 @@ sub start
        # do we need to send a forward/opernam?
        my $lastoper = $user->lastoper || 0;
        my $homenode = $user->homenode || ""; 
-       if ($homenode eq $main::mycall && $lastoper + $DXUser::lastoperinterval < $main::systime) {
+       if ($homenode eq $main::mycall && $main::systime >= $lastoper + $DXUser::lastoperinterval) {
                run_cmd($main::me, "forward/opernam $call");
-               $user->lastoper($main::systime);
+               $user->lastoper($main::systime + ((int rand(10)) * 86400));
        }
 
+       # ALWAYS output the user
+       my $thing = Thingy::Hello->new(user => $call, h => $self->{here});
+       $thing->broadcast($self);
+       $self->lasthello($main::systime);
+
        # run a script send the output to the punter
        my $script = new Script(lc $call) || new Script('user_default');
        $script->run($self) if $script;
@@ -168,6 +194,7 @@ sub start
        $self->send($self->msg('qll')) if !$user->qra || (!$user->lat && !$user->long);
        $self->send($self->msg('hnodee1')) if !$user->qth;
        $self->send($self->msg('m9')) if DXMsg::for_me($call);
+       $self->lastmsgpoll($main::systime);
        $self->prompt;
 }
 
@@ -180,6 +207,9 @@ sub normal
        my $self = shift;
        my $cmdline = shift;
        my @ans;
+
+       # save this for them's that need it
+       my $rawline = $cmdline;
        
        # remove leading and trailing spaces
        $cmdline =~ s/^\s*(.*)\s*$/$1/;
@@ -256,9 +286,24 @@ sub normal
                        }
                        $self->state('prompt');
                        delete $self->{talklist};
-               } elsif ($cmdline =~ m(^/\w+)) {
-                       $cmdline =~ s(^/)();
-                       $self->send_ans(run_cmd($self, $cmdline));
+               } elsif ($cmdline =~ m|^/+\w+|) {
+                       $cmdline =~ s|^/||;
+                       my $sendit = $cmdline =~ s|^/+||;
+                       my @in = $self->run_cmd($cmdline);
+                       $self->send_ans(@in);
+                       if ($sendit && $self->{talklist} && @{$self->{talklist}}) {
+                               foreach my $l (@in) {
+                                       my @bad;
+                                       if (@bad = BadWords::check($l)) {
+                                               $self->badcount(($self->badcount||0) + @bad);
+                                               Log('DXCommand', "$self->{call} swore: $l");
+                                       } else {
+                                               for (@{$self->{talklist}}) {
+                                                       $self->send_talks($_, $l);
+                                               }
+                                       }
+                               }
+                       }
                        $self->send($self->talk_prompt);
                } elsif ($self->{talklist} && @{$self->{talklist}}) {
                        # send what has been said to whoever is in this person's talk list
@@ -268,7 +313,7 @@ sub normal
                                Log('DXCommand', "$self->{call} swore: $cmdline");
                        } else {
                                for (@{$self->{talklist}}) {
-                                       $self->send_talks($_, $cmdline);
+                                       $self->send_talks($_, $rawline);
                                }
                        }
                        $self->send($self->talk_prompt) if $self->{state} eq 'talk';
@@ -280,11 +325,16 @@ sub normal
                no strict 'refs';
                my @ans;
                if (ref $self->{edit}) {
-                       eval { @ans = $self->{edit}->$func($self, $cmdline)};
+                       eval { @ans = $self->{edit}->$func($self, $rawline)};
                } else {
-                       eval {  @ans = &{$self->{func}}($self, $cmdline) };
+                       eval {  @ans = &{$self->{func}}($self, $rawline) };
+               }
+               if ($@) {
+                       $self->send_ans("Syserr: on stored func $self->{func}", $@);
+                       delete $self->{func};
+                       $self->state('prompt');
+                       undef $@;
                }
-               $self->send_ans("Syserr: on stored func $self->{func}", $@) if $@;
                $self->send_ans(@ans);
        } else {
                $self->send_ans(run_cmd($self, $cmdline));
@@ -382,15 +432,16 @@ sub run_cmd
 
        return () if length $cmdline == 0;
                
-       # strip out //
-       $cmdline =~ s|//|/|og;
-               
        # split the command line up into parts, the first part is the command
        my ($cmd, $args) = split /\s+/, $cmdline, 2;
        $args = "" unless defined $args;
                
        if ($cmd) {
-                       
+               # strip out // and .. on command only
+               $cmd =~ s|//|/|g;
+               $cmd =~ s|^/||g;                # no leading / either
+               $cmd =~ s|[^-\w/]||g;   # and no funny characters
+                                       
                my ($path, $fcmd);
                        
                dbg("cmd: $cmd") if isdbg('command');
@@ -405,7 +456,7 @@ sub run_cmd
                        
                # first expand out the entry to a command
                ($path, $fcmd) = search($main::localcmd, $cmd, "pl");
-               ($path, $fcmd) = search($main::cmd, $cmd, "pl") if !$path || !$fcmd;
+               ($path, $fcmd) = search($main::cmd, $cmd, "pl") unless $path && $fcmd;
 
                if ($path && $cmd) {
                        dbg("path: $cmd cmd: $fcmd") if isdbg('command');
@@ -441,7 +492,7 @@ sub run_cmd
                        return ();
                }
        }
-       return (@ans);
+       return map {s/([^\s])\s+$/$1/; $_} @ans;
 }
 
 #
@@ -451,11 +502,17 @@ sub run_cmd
 sub process
 {
        my $t = time;
-       my @dxchan = DXChannel->get_all();
+       my @dxchan = DXChannel::get_all();
        my $dxchan;
        
        foreach $dxchan (@dxchan) {
                next if $dxchan->sort ne 'U';  
+       
+               # send a outstanding message prompt if required
+               if ($t >= $dxchan->lastmsgpoll + $msgpolltime) {
+                       $dxchan->send($dxchan->msg('m9')) if DXMsg::for_me($dxchan->call);
+                       $dxchan->lastmsgpoll($t);
+               }
                
                # send a prompt if no activity out on this channel
                if ($t >= $dxchan->t + $main::user_interval) {
@@ -488,19 +545,25 @@ sub disconnect
        if ($uref) {
                @rout = $main::routeroot->del_user($uref);
                dbg("B/C PC17 on $main::mycall for: $call") if isdbg('route');
+
+               # issue a pc17 to everybody interested
+               $main::me->route_pc17($main::mycall, undef, $main::routeroot, $uref);
+
+               my $thing = Thingy::Bye->new(user=>$call);
+               $thing->broadcast($self);
        } else {
                confess "trying to disconnect a non existant user $call";
        }
 
-       # issue a pc17 to everybody interested
-       DXProt::route_pc17($main::me, $main::routeroot, @rout) if @rout;
-
        # I was the last node visited
     $self->user->node($main::mycall);
                
        # send info to all logged in thingies
        $self->tell_login('logoutu');
 
+       # remove any outstanding pings I have sent
+       Thingy::Ping::forget($call);
+       
        Log('DXCommand', "$call disconnected");
 
        $self->SUPER::disconnect;
@@ -513,7 +576,18 @@ sub disconnect
 sub prompt
 {
        my $self = shift;
-       $self->send($self->msg($self->here ? 'pr' : 'pr2', $self->call, cldate($main::systime), ztime($main::systime)));
+       my $call = $self->call;
+       my $date = cldate($main::systime);
+       my $time = ztime($main::systime);
+       my $prompt = $self->{prompt} || $self->msg('pr');
+
+       $call = "($call)" unless $self->here;
+       $prompt =~ s/\%C/$call/g;
+       $prompt =~ s/\%D/$date/g;
+       $prompt =~ s/\%T/$time/g;
+       $prompt =~ s/\%M/$main::mycall/g;
+       
+       $self->send($prompt);
 }
 
 # broadcast a message to all users [except those mentioned after buffer]
@@ -522,7 +596,7 @@ sub broadcast
        my $pkg = shift;                        # ignored
        my $s = shift;                          # the line to be rebroadcast
        
-    foreach my $dxchan (DXChannel->get_all()) {
+    foreach my $dxchan (DXChannel::get_all()) {
                next unless $dxchan->{sort} eq 'U'; # only interested in user channels  
                next if grep $dxchan == $_, @_;
                $dxchan->send($s);                      # send it
@@ -532,7 +606,7 @@ sub broadcast
 # gimme all the users
 sub get_all
 {
-       return grep {$_->{sort} eq 'U'} DXChannel->get_all();
+       return grep {$_->{sort} eq 'U'} DXChannel::get_all();
 }
 
 # run a script for this user
@@ -570,32 +644,33 @@ sub search
        my @parts = split '/', $short_cmd;
        my $dirfn;
        my $curdir = $path;
-       my $p;
-       my $i;
-       my @lparts;
        
-       for ($i = 0; $i < @parts; $i++) {
-               my  $p = $parts[$i];
+       while (my $p = shift @parts) {
                opendir(D, $curdir) or confess "can't open $curdir $!";
                my @ls = readdir D;
                closedir D;
-               my $l;
-               foreach $l (sort @ls) {
-                       next if $l =~ /^\./;
-                       if ($i < $#parts) {             # we are dealing with directories
+
+               # if this isn't the last part
+               if (@parts) {
+                       my $found;
+                       foreach my $l (sort @ls) {
+                               next if $l =~ /^\./;
                                if ((-d "$curdir/$l") && $p eq substr($l, 0, length $p)) {
                                        dbg("got dir: $curdir/$l\n") if isdbg('command');
                                        $dirfn .= "$l/";
                                        $curdir .= "/$l";
+                                       $found++;
                                        last;
                                }
-                       } else {                        # we are dealing with commands
-                               @lparts = split /\./, $l;                  
-                               next if $lparts[$#lparts] ne $suffix;        # only look for .$suffix files
+                       }
+                       # only proceed if we find the directory asked for
+                       return () unless $found;
+               } else {
+                       foreach my $l (sort @ls) {
+                               next if $l =~ /^\./;
+                               next unless $l =~ /\.$suffix$/;
                                if ($p eq substr($l, 0, length $p)) {
-                                       pop @lparts; #  remove the suffix
-                                       $l = join '.', @lparts;
-                                       #                 chop $dirfn;               # remove trailing /
+                                       $l =~ s/\.$suffix$//;
                                        $dirfn = "" unless $dirfn;
                                        $cmd_cache{$short_cmd} = join(',', ($path, "$dirfn$l")); # cache it
                                        dbg("got path: $path cmd: $dirfn$l\n") if isdbg('command');
@@ -613,7 +688,7 @@ sub clear_cmd_cache
        no strict 'refs';
        
        for (keys %Cache) {
-               undef *{$_};
+               undef *{$_} unless /cmd_cache/;
                dbg("Undefining cmd $_") if isdbg('command');
        }
        %cmd_cache = ();
@@ -717,7 +792,7 @@ sub talk
        my ($self, $from, $to, $via, $line) = @_;
        $line =~ s/\\5E/\^/g;
        $self->local_send('T', "$to de $from: $line") if $self->{talk};
-       Log('talk', $to, $from, $main::mycall, $line);
+       Log('talk', $to, $from, $via?$via:$main::mycall, $line);
        # send a 'not here' message if required
        unless ($self->{here} && $from ne $to) {
                my $key = "$to$from";
@@ -764,27 +839,27 @@ sub announce
        $self->local_send($target eq 'WX' ? 'W' : 'N', $buf);
 }
 
-# send a dx spot
-sub dx_spot
+# send a chat
+sub chat
 {
        my $self = shift;
        my $line = shift;
        my $isolate = shift;
+       my $target = shift;
+       my $to = shift;
+       my $text = shift;
        my ($filter, $hops);
 
-       return unless $self->{dx};
+       return unless grep uc $_ eq $target, @{$self->{user}->{group}};
        
-       if ($self->{spotsfilter}) {
-               ($filter, $hops) = $self->{spotsfilter}->it(@_ );
-               return unless $filter;
-       }
-
-       my $buf = Spot::formatb($self->{user}->wantgrid, $_[0], $_[1], $_[2], $_[3], $_[4]);
-       $buf .= "\a\a" if $self->{beep};
+       $text =~ s/^\#\d+ //;
+       my $buf = "$target de $_[0]: $text";
        $buf =~ s/\%5E/^/g;
-       $self->local_send('X', $buf);
+       $buf .= "\a\a" if $self->{beep};
+       $self->local_send('C', $buf);
 }
 
+
 sub wwv
 {
        my $self = shift;
@@ -828,12 +903,60 @@ sub broadcast_debug
 {
        my $s = shift;                          # the line to be rebroadcast
        
-       foreach my $dxchan (DXChannel->get_all) {
+       foreach my $dxchan (DXChannel::get_all) {
                next unless $dxchan->{enhanced} && $dxchan->{senddbg};
                $dxchan->send_later('L', $s);
        }
 }
 
+sub do_entry_stuff
+{
+       my $self = shift;
+       my $line = shift;
+       my @out;
+       
+       if ($self->state eq 'enterbody') {
+               my $loc = $self->{loc} || confess "local var gone missing" ;
+               if ($line eq "\032" || $line eq '%1A' || uc $line eq "/EX") {
+                       no strict 'refs';
+                       push @out, &{$loc->{endaction}}($self);          # like this for < 5.8.0
+                       $self->func(undef);
+                       $self->state('prompt');
+               } elsif ($line eq "\031" || uc $line eq "/ABORT" || uc $line eq "/QUIT") {
+                       push @out, $self->msg('m10');
+                       delete $loc->{lines};
+                       delete $self->{loc};
+                       $self->func(undef);
+                       $self->state('prompt');
+               } else {
+                       push @{$loc->{lines}}, length($line) > 0 ? $line : " ";
+                       # i.e. it ain't and end or abort, therefore store the line
+               }
+       } else {
+               confess "Invalid state $self->{state}";
+       }
+       return @out;
+}
+
+sub store_startup_script
+{
+       my $self = shift;
+       my $loc = $self->{loc} || confess "local var gone missing" ;
+       my @out;
+       my $call = $loc->{call} || confess "callsign gone missing";
+       confess "lines array gone missing" unless ref $loc->{lines};
+       my $r = Script::store($call, $loc->{lines});
+       if (defined $r) {
+               if ($r) {
+                       push @out, $self->msg('m19', $call, $r);
+               } else {
+                       push @out, $self->msg('m20', $call);
+               }
+       } else {
+               push @out, "error opening startup script $call $!";
+       } 
+       return @out;
+}
 
 1;
 __END__