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   1949 use parent 'Data::Session::Base';
  2         5  
  2         18  
4 2     2   167 no autovivification;
  2         4  
  2         18  
5 2     2   108 use strict;
  2         6  
  2         50  
6 2     2   11 use warnings;
  2         4  
  2         81  
7              
8 2     2   12 use Fcntl qw/:DEFAULT :flock :mode/;
  2         3  
  2         1373  
9              
10 2     2   15 use File::Path;
  2         5  
  2         139  
11 2     2   13 use File::Spec;
  2         4  
  2         90  
12              
13 2     2   13 use Hash::FieldHash ':all';
  2         3  
  2         227  
14              
15 2     2   15 use Try::Tiny;
  2         4  
  2         3687  
16              
17             our $VERSION = '1.18';
18              
19             # -----------------------------------------------
20              
21             sub get_file_path
22             {
23 471     471 0 1036 my($self, $sid) = @_;
24 471         1387 (my $id = $sid) =~ s|\\|/|g;
25              
26 471 50       1587 ($id =~ m|/|) && die __PACKAGE__ . ". Session ids cannot contain \\ or /: '$sid'";
27              
28 471         8985 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 780 my($self, $arg) = @_;
37 251   50     1694 $$arg{debug} ||= 0;
38 251   33     2632 $$arg{directory} ||= File::Spec -> tmpdir;
39 251   50     3704 $$arg{file_name} ||= 'cgisess_%s';
40 251   100     1152 $$arg{id} ||= 0;
41 251   50     1396 $$arg{no_flock} ||= 0;
42 251   50     1093 $$arg{no_follow} ||= eval { O_NOFOLLOW } || 0;
      33        
43 251   50     1354 $$arg{umask} ||= 0660;
44 251   50     703 $$arg{verbose} ||= 0;
45              
46             } # End of init.
47              
48             # -----------------------------------------------
49              
50             sub new
51             {
52 251     251 1 2587 my($class, %arg) = @_;
53              
54 251         1481 $class -> init(\%arg);
55              
56 251         15227 my($self) = from_hash(bless({}, $class), \%arg);
57              
58 251 50       2126 ($self -> file_name !~ /%s/) && die __PACKAGE__ . ". file_name must contain %s";
59              
60 251 50       2061 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         10876 return $self;
69              
70             } # End of new.
71              
72             # -----------------------------------------------
73              
74             sub remove
75             {
76 242     242 0 534 my($self, $id) = @_;
77 242         563 my($file_path) = $self -> get_file_path($id);
78              
79 242   50     13984 unlink $file_path || die __PACKAGE__ . ". Can't unlink file '$file_path'. " . ($self -> debug ? $! : '');
80              
81 242         2033 return 1;
82              
83             } # End of remove.
84              
85             # -----------------------------------------------
86              
87             sub retrieve
88             {
89 137     137 0 398 my($self, $id) = @_;
90 137         469 my($file_path) = $self -> get_file_path($id);
91 137         2116 my($message) = __PACKAGE__ . ". Can't %s file '$file_path'. %s";
92              
93 137 100       2971 (! -e $file_path) && return '';
94              
95             # Remove symlinks if possible.
96              
97 132 50       1933 if (-l $file_path)
98             {
99 0 0       0 unlink($file_path) || die sprintf($message, 'unlink', $self -> debug ? $! : '');
    0          
100             }
101              
102 132         1116 my($mode) = (O_RDWR | $self -> no_follow);
103              
104 132         340 my($fh);
105              
106 132 0       6562 sysopen($fh, $file_path, $mode, $self -> umask) || die sprintf($message, 'open', $self -> debug ? $! : '');
    50          
107              
108             # Sanity check.
109              
110 132 50       2104 (-l $file_path) && die sprintf($message, "open it. It's a link, not a", '');
111              
112 132 50       1117 if (! $self -> no_flock)
113             {
114 132 0       2241 flock($fh, LOCK_EX) || die sprintf($message, 'lock', $self -> debug ? $! : '');
    50          
115             }
116              
117 132         474 my($data) = '';
118              
119 132         3485 while (<$fh>)
120             {
121 415         1911 $data .= $_;
122             }
123              
124 132 0       2079 close($fh) || die sprintf($message, 'close', $self -> debug ? $! : '');
    50          
125              
126 132         1091 return $data;
127              
128             } # End of retrieve.
129              
130             # -----------------------------------------------
131              
132             sub store
133             {
134 92     92 0 338 my($self, $id, $data) = @_;
135 92         404 my($file_path) = $self -> get_file_path($id);
136 92         1733 my($message) = __PACKAGE__ . ". Can't %s file '$file_path'. %s";
137              
138             # Remove symlinks if possible.
139              
140 92 50       3957 if (-l $file_path)
141             {
142 0 0       0 unlink($file_path) || die sprintf($message, 'unlink', $self -> debug ? $! : '');
    0          
143             }
144              
145 92 100       1363 my($mode) = -e $file_path ? (O_WRONLY | $self -> no_follow) : (O_RDWR | O_CREAT | O_EXCL);
146              
147 92         253 my($fh);
148              
149 92 0       10330 sysopen($fh, $file_path, $mode, $self -> umask) || die sprintf($message, 'open', $self -> debug ? $! : '');
    50          
150              
151             # Sanity check.
152              
153 92 50       1548 (-l $file_path) && die sprintf($message, "create it. It's a link, not a", '');
154              
155 92 50       815 if (! $self -> no_flock)
156             {
157 92 0       1558 flock($fh, LOCK_EX) || die sprintf($message, 'lock', $self -> debug ? $! : '');
    50          
158             }
159              
160 92 0       1062 seek($fh, 0, 0) || die sprintf($message, 'seek', $self -> debug ? $! : '');
    50          
161 92 0       3632 truncate($fh, 0) || die sprintf($message, 'truncate', $self -> debug ? $! : '');
    50          
162 92         1262 print $fh $data;
163 92 0       10525 close($fh) || die sprintf($message, 'close', $self -> debug ? $! : '');
    50          
164              
165 92         914 return 1;
166              
167             } # End of store.
168              
169             # -----------------------------------------------
170              
171             sub traverse
172             {
173 1     1 0 4 my($self, $sub) = @_;
174              
175 1 50 33     10 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         7 $pattern =~ s/%s/(\.\+)/;
183 1         8 my($message) = __PACKAGE__ . ". Can't %s dir '" . $self -> directory . "' in traverse. %s";
184              
185 1 0       13 opendir(INX, $self -> directory) || die sprintf($message, 'open', $self -> debug ? $! : '');
    50          
186              
187 1         35 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         23 while ($entry = readdir(INX) )
194             {
195 6 100 66     537 next if ($entry =~ /^\.\.?/ || -d $entry);
196              
197 4 50       77 ($entry =~ /$pattern/) && $sub -> ($1);
198             }
199              
200 1 0       122 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