File Coverage

blib/lib/Data/Session/Driver/File.pm
Criterion Covered Total %
statement 94 99 94.9
branch 27 78 34.6
condition 14 28 50.0
subroutine 16 16 100.0
pod 1 7 14.2
total 152 228 66.6


line stmt bran cond sub pod time code
1             package Data::Session::Driver::File;
2              
3 2     2   969 use parent 'Data::Session::Base';
  2         4  
  2         10  
4 2     2   131 no autovivification;
  2         4  
  2         14  
5 2     2   106 use strict;
  2         4  
  2         37  
6 2     2   8 use warnings;
  2         2  
  2         54  
7              
8 2     2   8 use Fcntl qw/:DEFAULT :flock :mode/;
  2         3  
  2         942  
9              
10 2     2   13 use File::Path;
  2         4  
  2         96  
11 2     2   11 use File::Spec;
  2         2  
  2         48  
12              
13 2     2   9 use Hash::FieldHash ':all';
  2         2  
  2         165  
14              
15 2     2   10 use Try::Tiny;
  2         4  
  2         2477  
16              
17             our $VERSION = '1.17';
18              
19             # -----------------------------------------------
20              
21             sub get_file_path
22             {
23 471     471 0 755 my($self, $sid) = @_;
24 471         1010 (my $id = $sid) =~ s|\\|/|g;
25              
26 471 50       1080 ($id =~ m|/|) && die __PACKAGE__ . ". Session ids cannot contain \\ or /: '$sid'";
27              
28 471         5411 return File::Spec -> catfile($self -> directory, sprintf($self -> file_name, $sid) );
29              
30             } # End of get_file_path.
31              
32             # -----------------------------------------------
33              
34             sub init
35             {
36 251     251 0 399 my($self, $arg) = @_;
37 251   50     1089 $$arg{debug} ||= 0;
38 251   33     1261 $$arg{directory} ||= File::Spec -> tmpdir;
39 251   50     2208 $$arg{file_name} ||= 'cgisess_%s';
40 251   100     686 $$arg{id} ||= 0;
41 251   50     857 $$arg{no_flock} ||= 0;
42 251   50     747 $$arg{no_follow} ||= eval { O_NOFOLLOW } || 0;
      33        
43 251   50     813 $$arg{umask} ||= 0660;
44 251   50     539 $$arg{verbose} ||= 0;
45              
46             } # End of init.
47              
48             # -----------------------------------------------
49              
50             sub new
51             {
52 251     251 1 1815 my($class, %arg) = @_;
53              
54 251         777 $class -> init(\%arg);
55              
56 251         9690 my($self) = from_hash(bless({}, $class), \%arg);
57              
58 251 50       1534 ($self -> file_name !~ /%s/) && die __PACKAGE__ . ". file_name must contain %s";
59              
60 251 50       1053 if (! -d $self -> directory)
61             {
62 0 0       0 if (! File::Path::mkpath($self -> directory) )
63             {
64 0         0 die __PACKAGE__ . ". Can't create directory '" . $self -> directory . "'";
65             }
66             }
67              
68 251         5807 return $self;
69              
70             } # End of new.
71              
72             # -----------------------------------------------
73              
74             sub remove
75             {
76 242     242 0 503 my($self, $id) = @_;
77 242         481 my($file_path) = $self -> get_file_path($id);
78              
79 242   50     120385 unlink $file_path || die __PACKAGE__ . ". Can't unlink file '$file_path'. " . ($self -> debug ? $! : '');
80              
81 242         779 return 1;
82              
83             } # End of remove.
84              
85             # -----------------------------------------------
86              
87             sub retrieve
88             {
89 137     137 0 244 my($self, $id) = @_;
90 137         279 my($file_path) = $self -> get_file_path($id);
91 137         1705 my($message) = __PACKAGE__ . ". Can't %s file '$file_path'. %s";
92              
93 137 100       1623 (! -e $file_path) && return '';
94              
95             # Remove symlinks if possible.
96              
97 132 50       801 if (-l $file_path)
98             {
99 0 0       0 unlink($file_path) || die sprintf($message, 'unlink', $self -> debug ? $! : '');
    0          
100             }
101              
102 132         550 my($mode) = (O_RDWR | $self -> no_follow);
103              
104 132         182 my($fh);
105              
106 132 0       2751 sysopen($fh, $file_path, $mode, $self -> umask) || die sprintf($message, 'open', $self -> debug ? $! : '');
    50          
107              
108             # Sanity check.
109              
110 132 50       844 (-l $file_path) && die sprintf($message, "open it. It's a link, not a", '');
111              
112 132 50       473 if (! $self -> no_flock)
113             {
114 132 0       752 flock($fh, LOCK_EX) || die sprintf($message, 'lock', $self -> debug ? $! : '');
    50          
115             }
116              
117 132         264 my($data) = '';
118              
119 132         1360 while (<$fh>)
120             {
121 415         1220 $data .= $_;
122             }
123              
124 132 0       814 close($fh) || die sprintf($message, 'close', $self -> debug ? $! : '');
    50          
125              
126 132         708 return $data;
127              
128             } # End of retrieve.
129              
130             # -----------------------------------------------
131              
132             sub store
133             {
134 92     92 0 216 my($self, $id, $data) = @_;
135 92         199 my($file_path) = $self -> get_file_path($id);
136 92         1210 my($message) = __PACKAGE__ . ". Can't %s file '$file_path'. %s";
137              
138             # Remove symlinks if possible.
139              
140 92 50       1530 if (-l $file_path)
141             {
142 0 0       0 unlink($file_path) || die sprintf($message, 'unlink', $self -> debug ? $! : '');
    0          
143             }
144              
145 92 100       512 my($mode) = -e $file_path ? (O_WRONLY | $self -> no_follow) : (O_RDWR | O_CREAT | O_EXCL);
146              
147 92         144 my($fh);
148              
149 92 0       4213 sysopen($fh, $file_path, $mode, $self -> umask) || die sprintf($message, 'open', $self -> debug ? $! : '');
    50          
150              
151             # Sanity check.
152              
153 92 50       737 (-l $file_path) && die sprintf($message, "create it. It's a link, not a", '');
154              
155 92 50       469 if (! $self -> no_flock)
156             {
157 92 0       570 flock($fh, LOCK_EX) || die sprintf($message, 'lock', $self -> debug ? $! : '');
    50          
158             }
159              
160 92 0       339 seek($fh, 0, 0) || die sprintf($message, 'seek', $self -> debug ? $! : '');
    50          
161 92 0       2265 truncate($fh, 0) || die sprintf($message, 'truncate', $self -> debug ? $! : '');
    50          
162 92         572 print $fh $data;
163 92 0       43230 close($fh) || die sprintf($message, 'close', $self -> debug ? $! : '');
    50          
164              
165 92         590 return 1;
166              
167             } # End of store.
168              
169             # -----------------------------------------------
170              
171             sub traverse
172             {
173 1     1 0 5 my($self, $sub) = @_;
174              
175 1 50 33     12 if (! $sub || ref($sub) ne 'CODE')
176             {
177 0         0 die __PACKAGE__ . '. traverse() called without subref';
178             }
179              
180 1         5 my($pattern) = $self -> file_name;
181 1         3 $pattern =~ s/\./\\./g; # Or /\Q.../.
182 1         5 $pattern =~ s/%s/(\.\+)/;
183 1         6 my($message) = __PACKAGE__ . ". Can't %s dir '" . $self -> directory . "' in traverse. %s";
184              
185 1 0       11 opendir(INX, $self -> directory) || die sprintf($message, 'open', $self -> debug ? $! : '');
    50          
186              
187 1         21 my($entry);
188              
189             # I do not use readdir(INX) || die .. here because I could not get it to work,
190             # even with: while ($entry = (readdir(INX) || die sprintf($message, 'read', $!) ) ).
191             # Every attempt triggered the call to die.
192              
193 1         13 while ($entry = readdir(INX) )
194             {
195 6 100 66     197 next if ($entry =~ /^\.\.?/ || -d $entry);
196              
197 4 50       54 ($entry =~ /$pattern/) && $sub -> ($1);
198             }
199              
200 1 0       31 closedir(INX) || die sprintf($message, 'close', $self -> debug ? $! : '');
    50          
201              
202 1         17 return 1;
203              
204             } # End of traverse.
205              
206             # -----------------------------------------------
207              
208             1;
209              
210             =pod
211              
212             =head1 NAME
213              
214             L - A persistent session manager
215              
216             =head1 Synopsis
217              
218             See L for details.
219              
220             =head1 Description
221              
222             L allows L to manipulate sessions via files.
223              
224             To use this module do this:
225              
226             =over 4
227              
228             =item o Specify a driver of type File, as Data::Session -> new(type => 'driver:File ...')
229              
230             =back
231              
232             =head1 Case-sensitive Options
233              
234             See L for important information.
235              
236             =head1 Method: new()
237              
238             Creates a new object of type L.
239              
240             C takes a hash of key/value pairs, some of which might mandatory. Further, some combinations
241             might be mandatory.
242              
243             The keys are listed here in alphabetical order.
244              
245             They are lower-case because they are (also) method names, meaning they can be called to set or get
246             the value at any time.
247              
248             =over 4
249              
250             =item o debug => $Boolean
251              
252             Specifies that debugging should be turned on (1) or off (0) in L and
253             L.
254              
255             When debug is 1, $! is included in error messages, but because this reveals directory names, it is 0
256             by default.
257              
258             This key is optional.
259              
260             Default: 0.
261              
262             =item o directory => $string
263              
264             Specifies the path to the directory which will contain the session files.
265              
266             This key is normally passed in as Data::Session -> new(directory => $string).
267              
268             Default: File::Spec -> tmpdir.
269              
270             This key is optional.
271              
272             =item o file_name => $string_containing_%s
273              
274             Specifies the pattern to use for session file names. It must contain 1 '%s', which will be replaced
275             by the session id before the pattern is used as a file name.
276              
277             This key is normally passed in as Data::Session -> new(file_name => $string_containing_%s).
278              
279             Default: 'cgisess_%s'.
280              
281             This key is optional.
282              
283             =item o no_flock => $boolean
284              
285             Specifies (no_flock => 1) to not use flock() to obtain a lock on a session file before processing
286             it, or (no_flock => 0) to use flock().
287              
288             This key is normally passed in as Data::Session -> new(no_flock => $boolean).
289              
290             Default: 0.
291              
292             This key is optional.
293              
294             =item o no_follow => $value
295              
296             Influences the mode to use when calling sysopen() on session files.
297              
298             'Influences' means the value is bit-wise ored with O_RDWR for reading and with O_WRONLY for writing.
299              
300             This key is normally passed in as Data::Session -> new(no_follow => $boolean).
301              
302             Default: eval{O_NOFOLLOW} || 0.
303              
304             This key is optional.
305              
306             =item o umask => $octal_value
307              
308             Specifies the mode to use when calling sysopen() on session files.
309              
310             This key is normally passed in as Data::Session -> new(umask => $octal_value).
311              
312             Default: 0660.
313              
314             This key is optional.
315              
316             =item o verbose => $integer
317              
318             Print to STDERR more or less information.
319              
320             Typical values are 0, 1 and 2.
321              
322             This key is normally passed in as Data::Session -> new(verbose => $integer).
323              
324             This key is optional.
325              
326             =back
327              
328             =head1 Method: remove($id)
329              
330             Deletes from storage the session identified by $id.
331              
332             Returns 1 if it succeeds, and dies if it can't.
333              
334             =head1 Method: retrieve($id)
335              
336             Retrieves from storage the session identified by $id, or dies if it can't.
337              
338             Returns the result of reading the session from the file identified by $id.
339              
340             This result is a frozen session. This value must be thawed by calling the appropriate serialization
341             driver's thaw() method.
342              
343             L calls the right thaw() automatically.
344              
345             =head1 Method: store($id => $data)
346              
347             Writes to storage the session identified by $id, together with its data $data.
348              
349             Storage is a file identified by $id.
350              
351             Returns 1 if it succeeds, and dies if it can't.
352              
353             =head1 Method: traverse($sub)
354              
355             Retrieves all ids via their file names, and for each id calls the supplied subroutine with the id as
356             the only parameter.
357              
358             Returns 1.
359              
360             =head1 Support
361              
362             Log a bug on RT: L.
363              
364             =head1 Author
365              
366             L was written by Ron Savage Iron@savage.net.auE> in 2010.
367              
368             Home page: L.
369              
370             =head1 Copyright
371              
372             Australian copyright (c) 2010, Ron Savage.
373              
374             All Programs of mine are 'OSI Certified Open Source Software';
375             you can redistribute them and/or modify them under the terms of
376             The Artistic License, a copy of which is available at:
377             http://www.opensource.org/licenses/index.html
378              
379             =cut