File Coverage

lib/HTTPD/AdminBase.pm
Criterion Covered Total %
statement 112 174 64.3
branch 28 74 37.8
condition 6 30 20.0
subroutine 21 33 63.6
pod 0 17 0.0
total 167 328 50.9


line stmt bran cond sub pod time code
1             # $Id: AdminBase.pm,v 1.1.1.1 2001/02/20 02:32:11 lstein Exp $
2             package HTTPD::AdminBase;
3 2     2   11 use strict;
  2         3  
  2         81  
4              
5 2     2   10 use Carp ();
  2         3  
  2         24  
6 2     2   9 use Fcntl ();
  2         2  
  2         36  
7 2     2   1527 use Symbol qw(gensym);
  2         2010  
  2         178  
8 2     2   17 use File::Basename;
  2         3  
  2         163  
9 2     2   8 use Fcntl qw(:DEFAULT :flock);
  2         4  
  2         1100  
10 2     2   12 use vars qw($VERSION);
  2         2  
  2         1290  
11             $VERSION = (qw$Revision: 1.1.1.1 $)[1];
12              
13             #generic contructor stuff
14              
15             my $Debug = 0;
16             my %Default = (DBTYPE => "DBM",
17             SERVER => "_generic",
18             DEBUG => $Debug,
19             LOCKING => 1,
20             READONLY => 0,
21             );
22              
23             my %ImplementedBy = ();
24              
25             sub new {
26 8     8 0 17 my($class) = shift;
27 8         67 my $attrib = { %Default, @_ };
28 8         45 for (keys %$attrib) { $attrib->{"\U$_"} = delete $attrib->{$_}; }
  92         187  
29 8 50       47 $Debug = $attrib->{DEBUG} if defined $attrib->{DEBUG};
30              
31             #who's gonna do all the work?
32 8         14 my $impclass = $class->implementor(@{$attrib}{qw(DBTYPE SERVER)});
  8         43  
33 8 50       29 unless ($impclass) {
34 0         0 Carp::croak(sprintf "%s not implemented for Server '%s' and DBType '%s'",
35 0         0 $class, @{$attrib}{qw(SERVER DBTYPE)});
36             }
37             #the final product
38 8         9 return new $impclass ( %{$attrib} );
  8         80  
39             }
40              
41 4     4 0 12 sub close { $_[0] = undef }
42              
43             sub dbtype {
44 0     0 0 0 my($self,$dbtype) = @_;
45 0         0 my $old = $self->{DBTYPE};
46 0 0       0 return $old unless $dbtype;
47 0         0 Carp::croak("Can't modify DBType attribute");
48             #I think it makes more sense
49             #just to create a new instance in your script
50 0         0 my $base = $self->baseclass(3); #snag HTTPD::(UserAdmin|GroupAdmin)::(DBM|Text|SQL)
51 0         0 $self->close;
52 0         0 $self = $base->new( %{$self}, DBType => $dbtype );
  0         0  
53 0         0 return $old;
54             }
55              
56             #implementor code derived from URI::URL
57             sub implementor {
58 8     8 0 16 my($self,$dbtype,$server,$impclass) = @_;
59 8   33     46 my $class = ref $self || $self;
60 8         10 my $ic;
61 8 50       18 if(ref $self) {
62 0         0 ($server,$dbtype) = @{$self}{qw(SERVER DBTYPE)};
  0         0  
63             }
64              
65 8 50       28 $server = (defined $server) ? lc($server) : '_generic';
66 8 50       17 $dbtype = (defined $dbtype) ? $dbtype : 'DBM';
67 8         22 my $modclass = join('::', $class,$dbtype,$server);
68 8 50       20 if ($impclass) {
69 0         0 $ImplementedBy{$modclass} = $impclass;
70             }
71              
72 8 100       76 return $ic if $ic = $ImplementedBy{$modclass};
73              
74             #first load the database class
75 4         23 $ic = $self->load($class, $dbtype);
76              
77             # now look for a server subclass
78 4         19 $ic = $self->load($ic, $server);
79              
80 4 50       13 if ($ic) {
81 4         10 $ImplementedBy{$ic} = $ic;
82             }
83 4         10 $ic;
84             }
85              
86             sub load {
87 16     16 0 23 my($self) = shift;
88 16         19 my($ic,$module);
89 16 100       48 if(@_ > 1) { $ic = join('::', @_) }
  8         22  
90 8         16 else { $ic = $_[0] }
91 2     2   14 no strict 'refs';
  2         52  
  2         979  
92 16 100       13 unless (defined @{"${ic}::ISA"}) {
  16         99  
93             # Try to load it
94 8         38 ($module = $ic) =~ s,::,/,g;
95 8         36 $module =~ /^[^<>|;]+$/; $module = $&; #untaint
  8         21  
96 8         9 eval { require "$module.pm"; };
  8         3965  
97 8 50       39 print STDERR "loading $ic $@\n" if $Debug;
98 8 50       10 $ic = '' unless defined @{"${ic}::ISA"};
  8         40  
99             }
100 16         42 $ic;
101             }
102              
103             sub support {
104 4     4 0 13 my($self,%support) = @_;
105 4   33     28 my $class = ref $self || $self;
106 4         8 my($code,$db,$srv);
107 4         13 foreach $srv (keys %support) {
108 2     2   13 no strict 'refs';
  2         4  
  2         16693  
109 10         10 foreach $db (@{$support{$srv}}) {
  10         23  
110 20         37 @{"$class\:\:$db\:\:$srv\:\:ISA"} = qq($class\:\:$db\:\:_generic);
  20         387  
111             }
112             }
113             }
114              
115             sub _check {
116 0     0   0 my($self) = shift;
117 0         0 foreach (@_) {
118 0 0       0 next if defined $self->{$_};
119 0   0     0 Carp::croak(sprintf "cannot construct new %s object without '%s'", ref $self || $self, $_);
120             }
121             }
122              
123             sub _elem {
124 8     8   14 my($self, $element, $val) = @_;
125 8         17 my $old = $self->{$element};
126 8 50       43 return $old unless $val;
127 0         0 $self->{$element} = $val;
128 0         0 return $old;
129             }
130              
131             #DBM stuff
132             sub _tie {
133 0     0   0 my($self, $key, $file) = @_;
134 0 0 0     0 printf STDERR "%s->_tie($file)\n", ref $self || $self if $Debug;
135 0 0 0     0 Carp::confess
136             qq{Invalid HTTPD::AdminBase call: self="$self" key="$key" file="$file" \$self->{$key}="$self->{$key}"}
137             unless defined $key and defined $file;
138 0   0     0 $self->{$key} ||= {};
139 0         0 my($d,$f,$fl,$m) = ($self->{'_DBMPACK'}, $file, @{$self}{qw(_FLAGS MODE)});
  0         0  
140              
141 0 0       0 tie %{$self->{$key}}, $d, $f, $fl, $m
  0         0  
142             or Carp::croak("tie failed (args[$d,$f,$fl,$m]): $!");
143             }
144              
145             sub _untie {
146 0     0   0 my($self, $key) = @_;
147 0         0 untie %{$self->{$key}};
  0         0  
148             }
149              
150             my(%DBMFiles) = ();
151             my(%DBMFlags) = (
152             GDBM => {
153             rwc => sub { GDBM_File::GDBM_WRCREAT() },
154             rw => sub { GDBM_File::GDBM_READER()|GDBM_File::GDBM_WRITER() },
155             w => sub { GDBM_File::GDBM_WRITER() },
156             r => sub { GDBM_File::GDBM_READER() },
157             },
158             DEFAULT => {
159             rwc => sub { O_RDWR|O_CREAT },
160             rw => sub { O_RDWR },
161             w => sub { O_WRONLY },
162             r => sub { O_RDONLY },
163             },
164             );
165              
166             sub _dbm_init {
167 0     0   0 my($self,$dbmf) = @_;
168 0 0       0 $self->{DBMF} = $dbmf if defined $dbmf;
169 0         0 my($flags, $dbmpack);
170 0 0       0 unless($dbmpack = $DBMFiles{$self->{DBMF}}) {
171 0         0 $DBMFiles{$dbmpack} = $dbmpack = "$self->{DBMF}_File";
172 0 0       0 $self->load($dbmpack) or Carp::croak("can't load '$dbmpack'");
173             }
174              
175 0         0 @{$self}{qw(_DBMPACK _FLAGS)} = ($dbmpack, $self->flags);
  0         0  
176 0         0 1;
177             }
178              
179             sub lock {
180 8     8 0 15 my($self,$timeout,$file) = @_;
181 8         54 my($FH) = $self->{'_LOCKFH'} = $self->gensym;
182 8 100       130 return 1 unless $self->{LOCKING};
183 4   50     18 $timeout = $timeout || 10;
184              
185 4 50 33     25 unless($file = $file || "$self->{DB}.lock") {
186 0         0 Carp::croak("can't set lock, no file specified!");
187             }
188 4 50       263 unless ( -w dirname($self->{'_LOCKFILE'} = $file)) {
189 0 0       0 print STDERR "lock: can't write to '$file' " if $Debug;
190             #for writing lock files under CGI and such
191 0         0 $self->{'_LOCKFILE'} = $file =
192             sprintf "%s/%s-%s", $self->tmpdir(), "HTTPD", basename($file);
193 0 0       0 print STDERR "trying '$file' instead\n" if $Debug;
194             }
195              
196 4 50       22 $file =~ /^([^<>;|]+)$/ or Carp::croak("Bad file name '$file'"); $file = $1; #untaint
  4         11  
197              
198 4 50       394 open($FH, ">$file") || Carp::croak("can't open '$file' $!");
199              
200 4         42 while(! flock($FH, LOCK_EX|LOCK_NB) ) {
201 0         0 sleep 1;
202 0 0       0 if(--$timeout < 0) {
203 0         0 print STDERR "lock: timeout, can't lock $file \n";
204 0         0 return 0;
205             }
206             }
207 4 50       11 print STDERR "lock-> $file\n" if $Debug;
208 4         18 1;
209             }
210              
211             sub unlock {
212 4     4 0 6 my($self) = @_;
213 4 100       27 return 1 unless $self->{LOCKING};
214 2         4 my $FH = $self->{'_LOCKFH'};
215 2         17 flock($FH, LOCK_UN);
216 2         23 CORE::close($FH);
217 2         105 unlink $self->{'_LOCKFILE'};
218 2 50       7 print STDERR "unlock-> $self->{'_LOCKFILE'}\n" if $Debug;
219 2         30 1;
220             }
221              
222             #hmm, this doesn't seem right
223             sub tmpdir {
224 0     0 0 0 my($self) = @_;
225 0 0       0 return $self->{TMPDIR} if defined $self->{TMPDIR};
226 0         0 my $dir;
227 0         0 foreach ( qw(/tmp /usr/tmp /var/tmp) ) {
228 0 0       0 last if -d ($dir = $_);
229             }
230 0         0 $self->{TMPDIR} = $dir;
231             }
232              
233 8     8   391 sub import {}
234 0     0   0 sub DESTROY { warn "in AdminBase::DESTROY" }
235 0 0   0 0 0 sub class { ref $_[0] || $_[0] }
236 19     19 0 54 sub readonly { shift->flags == Fcntl::O_RDONLY() }
237 8     8 0 50 sub debug { shift->_elem('DEBUG', @_) }
238 0     0 0 0 sub path { shift->_elem('PATH', @_) }
239 0     0 0 0 sub locking { shift->_elem('LOCKING', @_) }
240             sub flags {
241 19     19 0 25 my($self, $mode) = @_;
242 19         17 my $flags;
243 19   50     69 my $key = $self->{DBMF} || "DEFAULT";
244 19   33     59 $mode ||= $self->{FLAGS};
245 19         38 $self->{FLAGS} = $mode;
246 19 50       40 $key = "DEFAULT" unless defined $DBMFlags{$key};
247 19 50       43 if(defined $DBMFlags{$key}->{$mode}) {
248 19         20 $flags = &{$DBMFlags{$key}->{$mode}};
  19         43  
249             }
250 19         78 return $flags;
251             }
252             #fallback, only implemented with DBType => Text
253 0     0 0   sub commit { (1,''); }
254              
255             sub baseclass {
256 0     0 0   my($self, $n) = @_;
257 0   0       my $class = join '::', (split(/::/, (ref $self || $self)))[0 .. $n - 1];
258             #print "baseclass got '$class' from '$self'\n";
259 0           $class;
260             }
261              
262             1;
263