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