File Coverage

blib/lib/Apache/Htaccess.pm
Criterion Covered Total %
statement 55 85 64.7
branch 8 26 30.7
condition n/a
subroutine 10 13 76.9
pod 7 8 87.5
total 80 132 60.6


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Apache::Htaccess - Create and modify Apache .htaccess files
4              
5             =head1 SYNOPSIS
6              
7             use Apache::Htaccess;
8              
9             my $obj = Apache::Htaccess->new("htaccess");
10             die($Apache::Htaccess::ERROR) if $Apache::Htaccess::ERROR;
11              
12             $obj->global_requires(@groups);
13              
14             $obj->add_global_require(@groups);
15              
16             $obj->directives(CheckSpelling => 'on');
17              
18             $obj->add_directive(CheckSpelling => 'on');
19            
20             $obj->requires('admin.cgi',@groups);
21              
22             $obj->add_require('admin.cgi',@groups);
23              
24             $obj->save();
25             die($Apache::Htaccess::ERROR) if $Apache::Htaccess::ERROR;
26              
27              
28             =head1 DESCRIPTION
29              
30             This module provides an object-oriented interface to Apache .htaccess
31             files. Currently the ability exists to read and write simple htaccess
32             files.
33              
34             =head1 METHODS
35              
36             =over 5
37              
38             =cut
39              
40             package Apache::Htaccess;
41              
42 2     2   920 use strict;
  2         4  
  2         60  
43 2     2   7 use warnings;
  2         2  
  2         45  
44 2     2   8 use vars qw($VERSION $ERROR);
  2         4  
  2         98  
45              
46 2     2   9 use Carp;
  2         2  
  2         2610  
47              
48             ( $VERSION ) = '1.5';
49              
50             #####################################################
51             # parse
52             # - Private function -
53             # In/Out Param: an Apache::Htaccess object
54             # Function: opens the content stored in $self->{HTACCESS} and converts it to
55             # Apache::Htaccess' internal data structure.
56             # Note: this will act on the object in place (note the prototype).
57              
58             my $parse = sub (\$) {
59             my $self = shift;
60              
61              
62             #Suck off comments
63             $self->{HTACCESS} =~ s/[\#\;].*?\n//sg;
64              
65              
66             #Suck off and store directives
67             my @files = $self->{HTACCESS} =~ m|()|sig;
68             $self->{HTACCESS} =~ s|||sig;
69              
70              
71             #Munge directives into the data structure
72             foreach my $directive (@files) {
73             my ($filelist) = $directive =~ //sig;
74             my @filelist = split(/\s+/,$filelist);
75            
76             my ($groups) = $directive =~ /require group\s+(.+?)\n/sig;
77             my @groups = split(/\s+/,$groups);
78            
79             foreach my $file (@filelist) {
80             foreach (@groups) {
81             $self->{REQUIRE}->{$file}->{$_}++;
82             }
83             }
84             }
85              
86             if( $self->{HTACCESS} =~ s/require group\s+(.+?)\n//is )
87             {
88             @{$self->{GLOBAL_REQ}} = split( /\s+/, $1);
89             }
90            
91             #Suck off and store all remaining directives
92             while($self->{HTACCESS} =~ /^(.+?)$/mg)
93             {
94             my( $directive, $value ) = split /\s+/, $1, 2;
95             $value = defined $value ? $value : '';
96             push @{$self->{DIRECTIVES}}, $directive, $value;
97             }
98              
99             chomp @{$self->{DIRECTIVES}};
100              
101              
102             #dump the remaining file bits
103             delete $self->{HTACCESS};
104             };
105              
106              
107              
108             #####################################################
109             # deparse
110             # - Private function -
111             # In/Out Param: an Apache::Htaccess object
112             # Function: takes the object's internal data structures
113             # and generates an htaccess file.
114             # The htaccess file contents are stored in $self->{HTACCESS}
115             # Note: this will act on the object in place (note the prototype).
116              
117             my $deparse = sub (\$) {
118             my $self = shift;
119             my $content;
120            
121             if( $self->{GLOBAL_REQ} ) {
122             $content .= "require group @{$self->{GLOBAL_REQ}}\n";
123             }
124            
125             if(exists($self->{DIRECTIVES})) {
126             my $i;
127             for($i = 0; $i < @{$self->{DIRECTIVES}}; $i++) {
128             my $key = $self->{DIRECTIVES}[$i];
129             my $value = $self->{DIRECTIVES}[++$i];
130             next unless defined $key && defined $value;
131             $content .= "$key";
132             $content .= " $value" if $value ne '';
133             $content .= "\n";
134             }
135             }
136            
137             # $content .= "\n";
138              
139             if(exists($self->{REQUIRE})) {
140             foreach (keys %{$self->{REQUIRE}}) {
141             next unless exists $self->{REQUIRE}->{$_};
142            
143             my $groups = join " " , sort keys %{$self->{REQUIRE}->{$_}};
144             next unless $groups;
145            
146             $content .= "\n";
147             $content .= "\trequire group $groups\n";
148             $content .= "\n";
149             }
150             }
151              
152             $self->{HTACCESS} = $content;
153              
154             };
155              
156              
157              
158             ##########################################################
159              
160             =head2 B
161              
162             my $obj = Apache::Htaccess->new($path_to_htaccess);
163              
164             Creates a new Htaccess object either with data loaded from an existing
165             htaccess file or from scratch
166              
167             =cut
168            
169             sub new {
170 1     1 1 43 undef $ERROR;
171 1         3 my $class = shift;
172 1         2 my $file = shift;
173            
174 1 50       6 unless($file) {
175 0         0 $ERROR = "Must provide a path to the .htaccess file";
176 0         0 return 0;
177             }
178            
179 1         3 my $self = {};
180 1         5 $self->{FILENAME} = $file;
181 1 50       27 if(-e $file) {
182 1 50       31 unless( open(FILE,$file) ) {
183 0         0 $ERROR = "Unable to open $file";
184 0         0 return 0;
185             }
186            
187 1         2 { local $/;
  1         5  
188 1         30 $self->{HTACCESS} = ;
189             }
190            
191 1         49 close FILE;
192 1         5 &$parse($self);
193             }
194              
195 1         3 bless $self, $class;
196 1         3 return $self;
197             }
198              
199              
200             =head2 B
201              
202             $obj->save();
203              
204             Saves the htaccess file to the filename designated at object creation.
205             This method is automatically called on object destruction.
206              
207             =cut
208              
209             sub save {
210 2     2 1 8 undef $ERROR;
211 2         3 my $self = shift;
212 2         4 &$deparse($self);
213 2 50       124 unless( open(FILE,"+>$self->{FILENAME}") ) {
214 0         0 $ERROR = "Unable to open $self->{FILENAME} for writing";
215 0         0 return 0;
216             }
217 2         19 print FILE $self->{HTACCESS};
218 2         51 close FILE;
219 2         30 return 1;
220             }
221              
222             sub DESTROY {
223 1     1   35 my $self = shift;
224 1         3 $self->save();
225             }
226              
227              
228             =head2 B
229              
230             $obj->global_requires(@groups);
231              
232             Sets the global group requirements. If no params are provided,
233             will return a list of the current groups listed in the global
234             require. Note: as of 0.3, passing this method a
235             parameter list causes the global requires list to be overwritten
236             with your parameters. see L.
237              
238             =cut
239              
240             sub global_requires {
241 1     1 1 15 undef $ERROR;
242 1         2 my $self = shift;
243            
244 1 50       4 if( @_ ) {
  0 0       0  
245 1         2 @{$self->{GLOBAL_REQ}} = @_
  1         16  
246             }
247             elsif( @{$self->{GLOBAL_REQ}} ) {
248 0         0 return @{$self->{GLOBAL_REQ}}
  0         0  
249             }
250             else {
251 0         0 return 0;
252             }
253              
254 1         3 return 1;
255             }
256              
257              
258             =head2 B
259              
260             $obj->add_global_require(@groups);
261              
262             Sets a global require (or requires) nondestructively. Use this
263             if you just want to add a few global requires without messing
264             with all of the global requires entries.
265              
266             =cut
267              
268             sub add_global_require {
269 0     0 1 0 undef $ERROR;
270 0         0 my $self = shift;
271 0 0       0 @_ ? push @{$self->{GLOBAL}}, @_
  0         0  
272             : return 0;
273 0         0 return 1;
274             }
275              
276              
277             =head2 B
278              
279             $obj->requires($file,@groups);
280              
281             Sets a group requirement for a file. If no params are given,
282             returns a list of the current groups listed in the files
283             require directive. Note: as of 0.3, passing this method a
284             parameter list causes the requires list to be overwritten
285             with your parameters. see L.
286              
287             =cut
288              
289             sub requires {
290 1     1 1 6 undef $ERROR;
291 1         2 my $self = shift;
292 1 50       3 my $file = shift or return 0;
293 1 50       4 if(@_) {
294 1         3 delete $self->{REQUIRE}->{$file};
295 1         2 foreach my $group (@_) {
296 2         6 $self->{REQUIRE}->{$file}->{$group}++;
297             }
298             } else {
299 0         0 return sort keys %{$self->{REQUIRE}->{$file}};
  0         0  
300             }
301 1         2 return 1;
302             }
303              
304              
305              
306             =head2 B
307              
308             $obj->add_require($file,@groups);
309              
310             Sets a require (or requires) nondestructively. Use this
311             if you just want to add a few requires without messing
312             with all of the requires entries.
313              
314             =cut
315              
316             sub add_requires {
317 0     0 0 0 undef $ERROR;
318 0         0 my $self = shift;
319 0 0       0 my $file = shift or return 0;
320 0 0       0 if(@_) {
321 0         0 foreach my $group (@_) {
322 0         0 $self->{REQUIRE}->{$file}->{$group}++;
323             }
324             } else {
325 0         0 return 0;
326             }
327             }
328              
329              
330              
331             =head2 B
332              
333             $obj->directives(CheckSpelling => 'on');
334              
335             Sets misc directives not directly supported by the API. If
336             no params are given, returns a list of current directives
337             and their values. Note: as of 0.2, passing this method a
338             parameter list causes the directive list to be overwritten
339             with your parameters. see L.
340              
341             =cut
342              
343             sub directives {
344 1     1 1 8 undef $ERROR;
345 1         2 my $self = shift;
346 1         3 @_ ? @{$self->{DIRECTIVES}} = @_
  0         0  
347 1 50       4 : return @{$self->{DIRECTIVES}};
348 1         2 return 1;
349             }
350              
351              
352             =head2 B
353              
354             $obj->add_directive(CheckSpelling => 'on');
355              
356             Sets a directive (or directives) nondestructively. Use this
357             if you just want to add a few directives without messing
358             with all of the directive entries.
359              
360             =cut
361              
362             sub add_directive {
363 0     0 1   undef $ERROR;
364 0           my $self = shift;
365 0 0         @_ ? push @{$self->{DIRECTIVES}}, @_
  0            
366             : return 0;
367 0           return 1;
368             }
369              
370              
371             1;
372              
373             =back
374              
375             =head1 TO DO
376              
377             * rewrite the parser to handle blocks
378              
379             =head1 SOURCE CODE
380              
381             This module is in GitHub:
382              
383             https://github.com/CPAN-Adopt-Me/apache-htaccess.git
384              
385             =head1 AUTHOR
386              
387             Matt Cashner originally created this module.
388             brian d foy maintained it for a long time.
389              
390             Now this module has no maintainer. You can takeover maintenance by
391             writing to modules@perl.org.
392              
393             =head1 COPYRIGHT
394              
395             Copyright (C) 2000 by The Creative Group.
396              
397             This module may be distributed under the terms of Perl itself.
398              
399             =cut