add sh/fdx command.
[spider.git] / perl / DXCommandmode.pm
index ea9282afdcc03489a2b86262f4a1eb9a67c67472..aa6f743d39d3657546cee962364193c26b4ac044 100644 (file)
@@ -31,10 +31,12 @@ use WCY;
 use Sun;
 use Internet;
 use Script;
-
+use Net::Telnet;
+use QSL;
+use DB_File;
 
 use strict;
-use vars qw(%Cache %cmd_cache $errstr %aliases $scriptbase $maxerrors %nothereslug);
+use vars qw(%Cache %cmd_cache $errstr %aliases $scriptbase $maxerrors %nothereslug $maxbadcount);
 
 %Cache = ();                                   # cache of dynamically loaded routine's mod times
 %cmd_cache = ();                               # cache of short names
@@ -42,10 +44,12 @@ $errstr = ();                                       # error string from eval
 %aliases = ();                                 # aliases for (parts of) commands
 $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
+
 
 use vars qw($VERSION $BRANCH);
 $VERSION = sprintf( "%d.%03d", q$Revision$ =~ /(\d+)\.(\d+)/ );
-$BRANCH = sprintf( "%d.%03d", q$Revision$ =~ /\d+\.\d+\.(\d+)\.(\d+)/ ) || 0;
+$BRANCH = sprintf( "%d.%03d", q$Revision$ =~ /\d+\.\d+\.(\d+)\.(\d+)/  || (0,0));
 $main::build += $VERSION;
 $main::branch += $BRANCH;
 
@@ -61,7 +65,10 @@ sub new
        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;
+
+       # ALWAYS output the user
+       my $ref = Route::User::get($call);
+       $main::me->route_pc16($main::mycall, undef, $main::routeroot, $ref) if $ref;
 
        return $self;
 }
@@ -83,13 +90,11 @@ sub start
 
        $self->{name} = $name ? $name : $call;
        $self->send($self->msg('l2',$self->{name}));
-       $self->send_file($main::motd) if (-e $main::motd);
        $self->state('prompt');         # a bit of room for further expansion, passwords etc
        $self->{priv} = $user->priv || 0;
        $self->{lang} = $user->lang || $main::lang || 'en';
        $self->{pagelth} = $user->pagelth || 20;
-       $self->{priv} = 0 if $line =~ /^(ax|te)/; # set the connection priv to 0 - can be upgraded later
-       ($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,6 +109,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 == 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};
+       $motd = $main::motd unless $motd && -e $motd;
+       $self->send_file($motd) if -e $motd;
+
+       # sort out privilege reduction
+       $self->{priv} = 0 if $line =~ /^(ax|te)/ && !$self->conn->{usedpasswd};
 
        # get the filters
        $self->{spotsfilter} = Filter::read_in('spots', $call, 0) || Filter::read_in('spots', 'user_default', 0);
@@ -121,9 +150,11 @@ sub start
        }
 
        # decide on echo
-       if (!$user->wantecho) {
+       my $echo = $user->wantecho;
+       unless ($echo) {
                $self->send_now('E', "0");
                $self->send($self->msg('echow'));
+               $self->conn->echo($echo) if $self->conn->can('echo');
        }
        
        $self->tell_login('loginu');
@@ -131,9 +162,9 @@ 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));
        }
 
        # run a script send the output to the punter
@@ -162,6 +193,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/;
@@ -193,8 +227,8 @@ sub normal
                }
        } elsif ($self->{state} eq 'sysop') {
                my $passwd = $self->{user}->passwd;
-               my @pw = split / */, $passwd;
                if ($passwd) {
+                       my @pw = grep {$_ !~ /\s/} split //, $passwd;
                        my @l = @{$self->{passwd}};
                        my $str = "$pw[$l[0]].*$pw[$l[1]].*$pw[$l[2]].*$pw[$l[3]].*$pw[$l[4]]";
                        if ($cmdline =~ /$str/) {
@@ -205,7 +239,31 @@ sub normal
                } else {
                        $self->send($self->msg('sorry'));
                }
-               delete $self->{passwd};
+               $self->state('prompt');
+       } elsif ($self->{state} eq 'passwd') {
+               my $passwd = $self->{user}->passwd;
+               if ($passwd && $cmdline eq $passwd) {
+                       $self->send($self->msg('pw1'));
+                       $self->state('passwd1');
+               } else {
+                       $self->conn->{echo} = $self->conn->{decho};
+                       delete $self->conn->{decho};
+                       $self->send($self->msg('sorry'));
+                       $self->state('prompt');
+               }
+       } elsif ($self->{state} eq 'passwd1') {
+               $self->{passwd} = $cmdline;
+               $self->send($self->msg('pw2'));
+               $self->state('passwd2');
+       } elsif ($self->{state} eq 'passwd2') {
+               if ($cmdline eq $self->{passwd}) {
+                       $self->{user}->passwd($cmdline);
+                       $self->send($self->msg('pw3'));
+               } else {
+                       $self->send($self->msg('pw4'));
+               }
+               $self->conn->{echo} = $self->conn->{decho};
+               delete $self->conn->{decho};
                $self->state('prompt');
        } elsif ($self->{state} eq 'talk') {
                if ($cmdline =~ m{^(?:/EX|/ABORT)}i) {
@@ -214,14 +272,35 @@ 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
-                       for (@{$self->{talklist}}) {
-                               $self->send_talks($_, $cmdline);
+                       my @bad;
+                       if (@bad = BadWords::check($cmdline)) {
+                               $self->badcount(($self->badcount||0) + @bad);
+                               Log('DXCommand', "$self->{call} swore: $cmdline");
+                       } else {
+                               for (@{$self->{talklist}}) {
+                                       $self->send_talks($_, $rawline);
+                               }
                        }
                        $self->send($self->talk_prompt) if $self->{state} eq 'talk';
                } else {
@@ -232,15 +311,28 @@ 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) };
                }
-               $self->send_ans("Syserr: on stored func $self->{func}", $@) if $@;
+               if ($@) {
+                       $self->send_ans("Syserr: on stored func $self->{func}", $@);
+                       delete $self->{func};
+                       $self->state('prompt');
+                       undef $@;
+               }
+               $self->send_ans(@ans);
        } else {
                $self->send_ans(run_cmd($self, $cmdline));
        } 
-       
+
+       # check for excessive swearing
+       if ($self->{badcount} && $self->{badcount} >= $maxbadcount) {
+               Log('DXCommand', "$self->{call} logged out for excessive swearing");
+               $self->disconnect;
+               return;
+       }
+
        # send a prompt only if we are in a prompt state
        $self->prompt() if $self->{state} =~ /^prompt/o;
 }
@@ -385,7 +477,7 @@ sub run_cmd
                        return ();
                }
        }
-       return (@ans);
+       return map {s/([^\s])\s+$/$1/; $_} @ans;
 }
 
 #
@@ -432,13 +524,13 @@ 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);
        } 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);
                
@@ -457,7 +549,11 @@ sub disconnect
 sub prompt
 {
        my $self = shift;
-       $self->send($self->msg($self->here ? 'pr' : 'pr2', $self->call, cldate($main::systime), ztime($main::systime)));
+       if ($self->{prompt}) {
+               $self->send($self->{prompt});
+       } else {
+               $self->send($self->msg($self->here ? 'pr' : 'pr2', $self->call, cldate($main::systime), ztime($main::systime)));
+       }
 }
 
 # broadcast a message to all users [except those mentioned after buffer]
@@ -557,7 +653,8 @@ sub clear_cmd_cache
        no strict 'refs';
        
        for (keys %Cache) {
-               undef *{$_};
+               undef *{$_} unless /cmd_cache/;
+               dbg("Undefining cmd $_") if isdbg('command');
        }
        %cmd_cache = ();
        %Cache = ();
@@ -625,8 +722,12 @@ sub find_cmd_name {
                # get rid of any existing sub and try to compile the new one
                no strict 'refs';
 
-               dbg("[Re]defining $package") if isdbg('command');
-               undef *$package;
+               if (exists $Cache{$package}) {
+                       dbg("Redefining $package") if isdbg('command');
+                       undef *$package;
+               } else {
+                       dbg("Defining $package") if isdbg('command');
+               }
                eval $eval;
                
                $Cache{$package} = {mtime => $mtime };
@@ -656,7 +757,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";
@@ -703,6 +804,56 @@ sub announce
        $self->local_send($target eq 'WX' ? 'W' : 'N', $buf);
 }
 
+# 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 grep uc $_ eq $target, @{$self->{user}->{group}};
+       
+       $text =~ s/^\#\d+ //;
+       my $buf = "$target de $_[0]: $text";
+       $buf =~ s/\%5E/^/g;
+       $buf .= "\a\a" if $self->{beep};
+       $self->local_send('C', $buf);
+}
+
+sub format_dx_spot
+{
+       my $self = shift;
+
+       my $t = ztime($_[2]);
+       my $loc;
+       my $clth = $self->{consort} eq 'local' ? 29 : 30;
+       my $comment = substr $_[3], 0, $clth; 
+       $comment .= ' ' x ($clth - length($comment));
+       my $ref = DXUser->get_current($_[4]);
+       if ($ref) {
+               $loc = $ref->qra || '' if $self->{user}->wantgrid; 
+               $loc = ' ' . substr($loc, 0, 4) if $loc;
+       } 
+       $loc = "" unless $loc;
+
+       if ($self->{user}->wantdxitu) {
+               $loc = ' ' . sprintf("%2d", $_[10]) if defined $_[10];
+               $comment = substr($comment, 0,  $self->{consort} eq 'local' ? 26 : 27) . ' ' . sprintf("%2d", $_[8]) if defined $_[8]; 
+       } elsif ($self->{user}->wantdxcq) {
+               $loc = ' ' . sprintf("%2d", $_[11]) if defined $_[11];
+               $comment = substr($comment, 0,  $self->{consort} eq 'local' ? 26 : 27) . ' ' . sprintf("%2d", $_[9]) if defined $_[9]; 
+       } elsif ($self->{user}->wantusstate) {
+               $loc = ' ' . $_[13] if $_[13];
+               $comment = substr($comment, 0,  $self->{consort} eq 'local' ? 26 : 27) . ' ' . $_[12] if $_[12]; 
+       }
+
+       return sprintf "DX de %-7.7s%11.1f  %-12.12s %-s $t$loc", "$_[4]:", $_[0], $_[1], $comment;
+}
+
 # send a dx spot
 sub dx_spot
 {
@@ -718,7 +869,9 @@ sub dx_spot
                return unless $filter;
        }
 
-       my $buf = Spot::formatb($self->{user}->wantgrid, $_[0], $_[1], $_[2], $_[3], $_[4]);
+       dbg('spot: "' . join('","', @_) . '"') if isdbg('dxspot');
+       
+       my $buf = $self->format_dx_spot(@_);
        $buf .= "\a\a" if $self->{beep};
        $buf =~ s/\%5E/^/g;
        $self->local_send('X', $buf);