fixed Aliases so that shutdown works again.
[spider.git] / perl / DXUser.pm
1 #
2 # DX cluster user routines
3 #
4 # Copyright (c) 1998 - Dirk Koopman G1TLH
5 #
6 # $Id$
7 #
8
9 package DXUser;
10
11 use DXLog;
12 use DB_File;
13 use Data::Dumper;
14 use Fcntl;
15 use IO::File;
16 use DXDebug;
17
18 use strict;
19
20 use vars qw($VERSION $BRANCH);
21 $VERSION = sprintf( "%d.%03d", q$Revision$ =~ /(\d+)\.(\d+)/ );
22 $BRANCH = sprintf( "%d.%03d", q$Revision$ =~ /\d+\.\d+\.(\d+)\.(\d+)/ ) || 0;
23 $main::build += $VERSION;
24 $main::branch += $BRANCH;
25
26 use vars qw(%u $dbm $filename %valid $lastoperinterval $lasttime);
27
28 %u = ();
29 $dbm = undef;
30 $filename = undef;
31 $lastoperinterval = 30*24*60*60;
32 $lasttime = 0;
33
34 # hash of valid elements and a simple prompt
35 %valid = (
36                   call => '0,Callsign',
37                   alias => '0,Real Callsign',
38                   name => '0,Name',
39                   qth => '0,Home QTH',
40                   lat => '0,Latitude,slat',
41                   long => '0,Longitude,slong',
42                   qra => '0,Locator',
43                   email => '0,E-mail Address',
44                   priv => '9,Privilege Level',
45                   lastin => '0,Last Time in,cldatetime',
46                   passwd => '9,Password',
47                   addr => '0,Full Address',
48                   'sort' => '0,Type of User', # A - ak1a, U - User, S - spider cluster, B - BBS
49                   xpert => '0,Expert Status,yesno',
50                   bbs => '0,Home BBS',
51                   node => '0,Last Node',
52                   homenode => '0,Home Node',
53                   lockout => '9,Locked out?,yesno',     # won't let them in at all
54                   dxok => '9,Accept DX Spots?,yesno', # accept his dx spots?
55                   annok => '9,Accept Announces?,yesno', # accept his announces?
56                   reg => '0,Registered?,yesno', # is this user registered?
57                   lang => '0,Language',
58                   hmsgno => '0,Highest Msgno',
59                   group => '0,Access Group,parray',     # used to create a group of users/nodes for some purpose or other
60                   isolate => '9,Isolate network,yesno',
61                   wantbeep => '0,Rec Beep,yesno',
62                   wantann => '0,Rec Announce,yesno',
63                   wantwwv => '0,Rec WWV,yesno',
64                   wantwcy => '0,Rec WCY,yesno',
65                   wantecho => '0,Rec Echo,yesno',
66                   wanttalk => '0,Rec Talk,yesno',
67                   wantwx => '0,Rec WX,yesno',
68                   wantdx => '0,Rec DX Spots,yesno',
69                   pagelth => '0,Current Pagelth',
70                   pingint => '9,Node Ping interval',
71                   nopings => '9,Ping Obs Count',
72                   wantlogininfo => '9,Login info req,yesno',
73           wantgrid => '0,DX Grid Info,yesno',
74                   wantann_talk => '0,Talklike Anns,yesno',
75                   lastoper => '9,Last for/oper,cldatetime',
76                   nothere => '0,Not Here Text',
77                   registered => '9,Registered?,yesno',
78                  );
79
80 no strict;
81 sub AUTOLOAD
82 {
83         my $self = shift;
84         my $name = $AUTOLOAD;
85   
86         return if $name =~ /::DESTROY$/;
87         $name =~ s/.*:://o;
88   
89         confess "Non-existant field '$AUTOLOAD'" if !$valid{$name};
90         # this clever line of code creates a subroutine which takes over from autoload
91         # from OO Perl - Conway
92         *{$AUTOLOAD} = sub {@_ > 1 ? $_[0]->{$name} = $_[1] : $_[0]->{$name}} ;
93         if (@_) {
94                 $self->{$name} = shift;
95         }
96         return $self->{$name};
97 }
98
99 use strict;
100
101 #
102 # initialise the system
103 #
104 sub init
105 {
106         my ($pkg, $fn, $mode) = @_;
107   
108         confess "need a filename in User" if !$fn;
109         $fn .= ".v2";
110         if ($mode) {
111                 $dbm = tie (%u, 'DB_File', $fn, O_CREAT|O_RDWR, 0666, $DB_BTREE) or confess "can't open user file: $fn ($!)";
112         } else {
113                 $dbm = tie (%u, 'DB_File', $fn, O_RDONLY, 0666, $DB_BTREE) or confess "can't open user file: $fn ($!)";
114         }
115         
116         $filename = $fn;
117 }
118
119 sub del_file
120 {
121         my ($pkg, $fn) = @_;
122   
123         confess "need a filename in User" if !$fn;
124         $fn .= ".v2";
125         unlink $fn;
126 }
127
128 #
129 # periodic processing
130 #
131 sub process
132 {
133         if ($main::systime > $lasttime + 15) {
134                 $dbm->sync;
135                 $lasttime = $main::systime;
136         }
137 }
138
139 #
140 # close the system
141 #
142
143 sub finish
144 {
145         undef $dbm;
146         untie %u;
147 }
148
149 #
150 # new - create a new user
151 #
152
153 sub new
154 {
155         my $pkg = shift;
156         my $call = uc shift;
157         #  $call =~ s/-\d+$//o;
158   
159 #       confess "can't create existing call $call in User\n!" if $u{$call};
160
161         my $self = bless {}, $pkg;
162         $self->{call} = $call;
163         $self->{'sort'} = 'U';
164         $self->put;
165         return $self;
166 }
167
168 #
169 # get - get an existing user - this seems to return a different reference everytime it is
170 #       called - see below
171 #
172
173 sub get
174 {
175         my $pkg = shift;
176         my $call = uc shift;
177         my $data;
178         unless ($dbm->get($call, $data)) {
179                 return decode($data);
180         }
181         return undef;
182 }
183
184 #
185 # get an existing either from the channel (if there is one) or from the database
186 #
187 # It is important to note that if you have done a get (for the channel say) and you
188 # want access or modify that you must use this call (and you must NOT use get's all
189 # over the place willy nilly!)
190 #
191
192 sub get_current
193 {
194         my $pkg = shift;
195         my $call = uc shift;
196   
197         my $dxchan = DXChannel->get($call);
198         return $dxchan->user if $dxchan;
199         my $data;
200         unless ($dbm->get($call, $data)) {
201                 return decode($data);
202         }
203         return undef;
204 }
205
206 #
207 # get all callsigns in the database 
208 #
209
210 sub get_all_calls
211 {
212         return (sort keys %u);
213 }
214
215 #
216 # put - put a user
217 #
218
219 sub put
220 {
221         my $self = shift;
222         confess "Trying to put nothing!" unless $self && ref $self;
223         my $call = $self->{call};
224         # delete all instances of this 
225 #       for ($dbm->get_dup($call)) {
226 #               $dbm->del_dup($call, $_);
227 #       }
228         $dbm->del($call);
229         delete $self->{annok} if $self->{annok};
230         delete $self->{dxok} if $self->{dxok};
231         $dbm->put($call, $self->encode);
232 }
233
234
235 # create a string from a user reference
236 #
237 sub encode
238 {
239         my $self = shift;
240         my $dd = new Data::Dumper([$self]);
241         $dd->Indent(0);
242         $dd->Terse(1);
243     $dd->Quotekeys($] < 5.005 ? 1 : 0);
244         return $dd->Dumpxs;
245 }
246
247 #
248 # create a hash from a string
249 #
250 sub decode
251 {
252         my $s = shift;
253         my $ref;
254         eval '$ref = ' . $s;
255         if ($@) {
256                 dbg($@);
257                 Log('err', $@);
258                 $ref = undef;
259         }
260         return $ref;
261 }
262
263 #
264 # del - delete a user
265 #
266
267 sub del
268 {
269         my $self = shift;
270         my $call = $self->{call};
271         # delete all instances of this 
272 #       for ($dbm->get_dup($call)) {
273 #               $dbm->del_dup($call, $_);
274 #       }
275         $dbm->del($call);
276 }
277
278 #
279 # close - close down a user
280 #
281
282 sub close
283 {
284         my $self = shift;
285         $self->{lastin} = time;
286         $self->put();
287 }
288
289 #
290 # sync the database
291 #
292
293 sub sync
294 {
295         $dbm->sync;
296 }
297
298 #
299 # return a list of valid elements 
300
301
302 sub fields
303 {
304         return keys(%valid);
305 }
306
307
308 #
309 # export the database to an ascii file
310 #
311
312 sub export
313 {
314         my $fn = shift;
315         
316         # save old ones
317         rename "$fn.oooo", "$fn.ooooo" if -e "$fn.oooo";
318         rename "$fn.ooo", "$fn.oooo" if -e "$fn.ooo";
319         rename "$fn.oo", "$fn.ooo" if -e "$fn.oo";
320         rename "$fn.o", "$fn.oo" if -e "$fn.o";
321         rename "$fn", "$fn.o" if -e "$fn";
322
323         my $count = 0;
324         my $fh = new IO::File ">$fn" or return "cannot open $fn ($!)";
325         if ($fh) {
326                 my $ref = 0;
327                 my $key = 0;
328                 my $action;
329                 my $t = scalar localtime;
330                 print $fh q{#!/usr/bin/perl
331 #
332 # The exported userfile for a DXSpider System
333 #
334 # Input file: $filename
335 #       Time: $t
336 #
337                         
338 package main;
339                         
340 # search local then perl directories
341 BEGIN {
342         umask 002;
343                                 
344         # root of directory tree for this system
345         $root = "/spider"; 
346         $root = $ENV{'DXSPIDER_ROOT'} if $ENV{'DXSPIDER_ROOT'};
347         
348         unshift @INC, "$root/perl";     # this IS the right way round!
349         unshift @INC, "$root/local";
350         
351         # try to detect a lockfile (this isn't atomic but 
352         # should do for now
353         $lockfn = "$root/perl/cluster.lck";       # lock file name
354         if (-e $lockfn) {
355                 open(CLLOCK, "$lockfn") or die "Can't open Lockfile ($lockfn) $!";
356                 my $pid = <CLLOCK>;
357                 chomp $pid;
358                 die "Lockfile ($lockfn) and process $pid exists - cluster must be stopped first\n" if kill 0, $pid;
359                 close CLLOCK;
360         }
361 }
362
363 package DXUser;
364
365 use DXVars;
366 use DXUser;
367
368 if (@ARGV) {
369         $main::userfn = shift @ARGV;
370         print "user filename now $userfn\n";
371 }
372
373 DXUser->del_file($main::userfn);
374 DXUser->init($main::userfn, 1);
375
376 %u = (
377   };
378
379         for ($action = R_FIRST; !$dbm->seq($key, $ref, $action); $action = R_NEXT) {
380                         print $fh "'$key' => q{$ref},\n";
381                         ++$count;
382                 } 
383         print $fh ");\n#\nprint \"there were $count records\\n\";\n#\n";
384         print $fh "DXUser->sync; DXUser->finish;\n#\n";
385         $fh->close;
386     } 
387         return $count;
388 }
389
390 #
391 # group handling
392 #
393
394 # add one or more groups
395 sub add_group
396 {
397         my $self = shift;
398         my $ref = $self->{group} || [ 'local' ];
399         $self->{group} = $ref if !$self->{group};
400         push @$ref, @_ if @_;
401 }
402
403 # remove one or more groups
404 sub del_group
405 {
406         my $self = shift;
407         my $ref = $self->{group} || [ 'local' ];
408         my @in = @_;
409         
410         $self->{group} = $ref if !$self->{group};
411         
412         @$ref = map { my $a = $_; return (grep { $_ eq $a } @in) ? () : $a } @$ref;
413 }
414
415 # does this thing contain all the groups listed?
416 sub union
417 {
418         my $self = shift;
419         my $ref = $self->{group};
420         my $n;
421         
422         return 0 if !$ref || @_ == 0;
423         return 1 if @$ref == 0 && @_ == 0;
424         for ($n = 0; $n < @_; ) {
425                 for (@$ref) {
426                         my $a = $_;
427                         $n++ if grep $_ eq $a, @_; 
428                 }
429         }
430         return $n >= @_;
431 }
432
433 # simplified group test just for one group
434 sub in_group
435 {
436         my $self = shift;
437         my $s = shift;
438         my $ref = $self->{group};
439         
440         return 0 if !$ref;
441         return grep $_ eq $s, $ref;
442 }
443
444 # set up a default group (only happens for them's that connect direct)
445 sub new_group
446 {
447         my $self = shift;
448         $self->{group} = [ 'local' ];
449 }
450
451 #
452 # return a prompt for a field
453 #
454
455 sub field_prompt
456
457         my ($self, $ele) = @_;
458         return $valid{$ele};
459 }
460
461 # some variable accessors
462 sub sort
463 {
464         my $self = shift;
465         @_ ? $self->{'sort'} = shift : $self->{'sort'} ;
466 }
467
468 # some accessors
469 sub _want
470 {
471         my $n = shift;
472         my $self = shift;
473         my $val = shift;
474         my $s = "want$n";
475         $self->{$s} = $val if defined $val;
476         return exists $self->{$s} ? $self->{$s} : 1;
477 }
478
479 sub wantbeep
480 {
481         return _want('beep', @_);
482 }
483
484 sub wantann
485 {
486         return _want('ann', @_);
487 }
488
489 sub wantwwv
490 {
491         return _want('wwv', @_);
492 }
493
494 sub wantwcy
495 {
496         return _want('wcy', @_);
497 }
498
499 sub wantecho
500 {
501         return _want('echo', @_);
502 }
503
504 sub wantwx
505 {
506         return _want('wx', @_);
507 }
508
509 sub wantdx
510 {
511         return _want('dx', @_);
512 }
513
514 sub wanttalk
515 {
516         return _want('talk', @_);
517 }
518
519 sub wantgrid
520 {
521         return _want('grid', @_);
522 }
523
524 sub wantann_talk
525 {
526         return _want('ann_talk', @_);
527 }
528
529 sub wantlogininfo
530 {
531         my $self = shift;
532         my $val = shift;
533         $self->{wantlogininfo} = $val if defined $val;
534         return $self->{wantlogininfo};
535 }
536
537 sub is_node
538 {
539         my $self = shift;
540         return $self->{sort} =~ /[ACRSX]/;
541 }
542
543 sub is_user
544 {
545         my $self = shift;
546         return $self->{sort} eq 'U';
547 }
548
549 sub is_bbs
550 {
551         my $self = shift;
552         return $self->{sort} eq 'B';
553 }
554
555 sub is_spider
556 {
557         my $self = shift;
558         return $self->{sort} eq 'S';
559 }
560
561 sub is_clx
562 {
563         my $self = shift;
564         return $self->{sort} eq 'C';
565 }
566
567 sub is_dxnet
568 {
569         my $self = shift;
570         return $self->{sort} eq 'X';
571 }
572
573 sub is_arcluster
574 {
575         my $self = shift;
576         return $self->{sort} eq 'R';
577 }
578
579 sub is_ak1a
580 {
581         my $self = shift;
582         return $self->{sort} eq 'A';
583 }
584 1;
585 __END__
586
587
588
589
590