added a forward/latlong command
[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         my $data;
159         unless ($dbm->get($call, $data)) {
160                 return decode($data);
161         }
162         return undef;
163 }
164
165 #
166 # get an existing either from the channel (if there is one) or from the database
167 #
168 # It is important to note that if you have done a get (for the channel say) and you
169 # want access or modify that you must use this call (and you must NOT use get's all
170 # over the place willy nilly!)
171 #
172
173 sub get_current
174 {
175         my $pkg = shift;
176         my $call = uc shift;
177   
178         my $dxchan = DXChannel->get($call);
179         return $dxchan->user if $dxchan;
180         my $data;
181         unless ($dbm->get($call, $data)) {
182                 return decode($data);
183         }
184         return undef;
185 }
186
187 #
188 # get all callsigns in the database 
189 #
190
191 sub get_all_calls
192 {
193         return (sort keys %u);
194 }
195
196 #
197 # put - put a user
198 #
199
200 sub put
201 {
202         my $self = shift;
203         confess "Trying to put nothing!" unless $self && ref $self;
204         my $call = $self->{call};
205         # delete all instances of this 
206         for ($dbm->get_dup($call)) {
207                 $dbm->del_dup($call, $_);
208         }
209         delete $self->{annok} if $self->{annok};
210         delete $self->{dxok} if $self->{dxok};
211         $dbm->put($call, $self->encode);
212 }
213
214
215 # create a string from a user reference
216 #
217 sub encode
218 {
219         my $self = shift;
220         my $dd = new Data::Dumper([$self]);
221         $dd->Indent(0);
222         $dd->Terse(1);
223     $dd->Quotekeys($] < 5.005 ? 1 : 0);
224         return $dd->Dumpxs;
225 }
226
227 #
228 # create a hash from a string
229 #
230 sub decode
231 {
232         my $s = shift;
233         my $ref;
234         eval '$ref = ' . $s;
235         if ($@) {
236                 dbg('err', $@) if $@;
237                 Log('err', $@) if $@;
238                 $ref = undef;
239         }
240         return $ref;
241 }
242
243 #
244 # del - delete a user
245 #
246
247 sub del
248 {
249         my $self = shift;
250         my $call = $self->{call};
251         # delete all instances of this 
252         for ($dbm->get_dup($call)) {
253                 $dbm->del_dup($call, $_);
254         }
255 }
256
257 #
258 # close - close down a user
259 #
260
261 sub close
262 {
263         my $self = shift;
264         $self->{lastin} = time;
265         $self->put();
266 }
267
268 #
269 # sync the database
270 #
271
272 sub sync
273 {
274         $dbm->sync;
275 }
276
277 #
278 # return a list of valid elements 
279
280
281 sub fields
282 {
283         return keys(%valid);
284 }
285
286
287 #
288 # export the database to an ascii file
289 #
290
291 sub export
292 {
293         my $fn = shift;
294         
295         # save old ones
296         rename "$fn.oooo", "$fn.ooooo" if -e "$fn.oooo";
297         rename "$fn.ooo", "$fn.oooo" if -e "$fn.ooo";
298         rename "$fn.oo", "$fn.ooo" if -e "$fn.oo";
299         rename "$fn.o", "$fn.oo" if -e "$fn.o";
300         rename "$fn", "$fn.o" if -e "$fn";
301
302         my $count = 0;
303         my $fh = new IO::File ">$fn" or return "cannot open $fn ($!)";
304         if ($fh) {
305                 my $ref;
306                 my $key;
307                 my $action;
308                 my $t = scalar localtime;
309                 print $fh "#!/usr/bin/perl
310 #
311 # The exported userfile for a DXSpider System
312 #
313 # Input file: $filename
314 #       Time: $t
315 #
316
317 package DXUser;
318
319 %u = (
320 ";
321
322                 for ($action = R_FIRST; !$dbm->seq($key, $ref, $action); $action = R_NEXT) {
323                         print $fh "'$key' => $ref,\n";
324                         ++$count;
325                 } 
326                 print $fh ");\n#\n# there were $count records\n#\n";
327                 $fh->close;
328         } 
329         return $count;
330 }
331
332 #
333 # group handling
334 #
335
336 # add one or more groups
337 sub add_group
338 {
339         my $self = shift;
340         my $ref = $self->{group} || [ 'local' ];
341         $self->{group} = $ref if !$self->{group};
342         push @$ref, @_ if @_;
343 }
344
345 # remove one or more groups
346 sub del_group
347 {
348         my $self = shift;
349         my $ref = $self->{group} || [ 'local' ];
350         my @in = @_;
351         
352         $self->{group} = $ref if !$self->{group};
353         
354         @$ref = map { my $a = $_; return (grep { $_ eq $a } @in) ? () : $a } @$ref;
355 }
356
357 # does this thing contain all the groups listed?
358 sub union
359 {
360         my $self = shift;
361         my $ref = $self->{group};
362         my $n;
363         
364         return 0 if !$ref || @_ == 0;
365         return 1 if @$ref == 0 && @_ == 0;
366         for ($n = 0; $n < @_; ) {
367                 for (@$ref) {
368                         my $a = $_;
369                         $n++ if grep $_ eq $a, @_; 
370                 }
371         }
372         return $n >= @_;
373 }
374
375 # simplified group test just for one group
376 sub in_group
377 {
378         my $self = shift;
379         my $s = shift;
380         my $ref = $self->{group};
381         
382         return 0 if !$ref;
383         return grep $_ eq $s, $ref;
384 }
385
386 # set up a default group (only happens for them's that connect direct)
387 sub new_group
388 {
389         my $self = shift;
390         $self->{group} = [ 'local' ];
391 }
392
393 #
394 # return a prompt for a field
395 #
396
397 sub field_prompt
398
399         my ($self, $ele) = @_;
400         return $valid{$ele};
401 }
402
403 # some variable accessors
404 sub sort
405 {
406         my $self = shift;
407         @_ ? $self->{'sort'} = shift : $self->{'sort'} ;
408 }
409
410 # some accessors
411 sub _want
412 {
413         my $n = shift;
414         my $self = shift;
415         my $val = shift;
416         my $s = "want$n";
417         $self->{$s} = $val if defined $val;
418         return exists $self->{$s} ? $self->{$s} : 1;
419 }
420
421 sub wantbeep
422 {
423         return _want('beep', @_);
424 }
425
426 sub wantann
427 {
428         return _want('ann', @_);
429 }
430
431 sub wantwwv
432 {
433         return _want('wwv', @_);
434 }
435
436 sub wantwcy
437 {
438         return _want('wcy', @_);
439 }
440
441 sub wantecho
442 {
443         return _want('echo', @_);
444 }
445
446 sub wantwx
447 {
448         return _want('wx', @_);
449 }
450
451 sub wantdx
452 {
453         return _want('dx', @_);
454 }
455
456 sub wanttalk
457 {
458         return _want('talk', @_);
459 }
460
461 sub wantgrid
462 {
463         return _want('grid', @_);
464 }
465
466 sub wantlogininfo
467 {
468         my $self = shift;
469         my $n = shift;
470         $self->{wantlogininfo} = $n if $n;
471         return exists $self->{wantlogininfo} ? $self->{wantlogininfo} : 0;
472 }
473
474 sub is_node
475 {
476         my $self = shift;
477         return $self->{sort} =~ /[ACRSX]/;
478 }
479
480 sub is_user
481 {
482         my $self = shift;
483         return $self->{sort} eq 'U';
484 }
485
486 sub is_bbs
487 {
488         my $self = shift;
489         return $self->{sort} eq 'B';
490 }
491
492 sub is_spider
493 {
494         my $self = shift;
495         return $self->{sort} eq 'S';
496 }
497
498 sub is_clx
499 {
500         my $self = shift;
501         return $self->{sort} eq 'C';
502 }
503
504 sub is_dxnet
505 {
506         my $self = shift;
507         return $self->{sort} eq 'X';
508 }
509
510 sub is_arcluster
511 {
512         my $self = shift;
513         return $self->{sort} eq 'R';
514 }
515
516 sub is_ak1a
517 {
518         my $self = shift;
519         return $self->{sort} eq 'A';
520 }
521 1;
522 __END__
523
524
525
526
527