File Coverage

blib/lib/CGI/Capture.pm
Criterion Covered Total %
statement 106 139 76.2
branch 18 58 31.0
condition 0 9 0.0
subroutine 19 22 86.3
pod 9 9 100.0
total 152 237 64.1


line stmt bran cond sub pod time code
1             package CGI::Capture; # git description: cc2391e
2             # ABSTRACT: Meticulously thorough capture and replaying of CGI calls
3              
4             #pod =pod
5             #pod
6             #pod =head1 SYNOPSIS
7             #pod
8             #pod # Capture the current CGI to a file, and replay it once created
9             #pod use CGI::Capture 'fileupload.dat';
10             #pod
11             #pod # Create an object and capture the state
12             #pod my $Capture = CGI::Capture->new->capture;
13             #pod
14             #pod # Store it in a file and load it back in
15             #pod $Capture->store('somefile.dat');
16             #pod my $second = CGI::Capture->apply('somefile.dat');
17             #pod
18             #pod # Apply the CGI call to the current environment
19             #pod $second->apply;
20             #pod
21             #pod =head1 DESCRIPTION
22             #pod
23             #pod L does a terribly bad job of saving CGI calls. C tries
24             #pod to resolve this and save a CGI call in as much painstaking detail as it
25             #pod possibly can.
26             #pod
27             #pod Because of this, C should work with server logins, cookies,
28             #pod file uploads, strange execution environments, special environment
29             #pod variables, the works.
30             #pod
31             #pod It does this by capturing a large amount of the perl environment
32             #pod BEFORE F itself gets a chance to look at it, and then restores
33             #pod it in the same way.
34             #pod
35             #pod So in essence, it grabs all of C, C<%ENV>, C<@INC>, and anything
36             #pod else it can think of. The things it can't replicate, it records anyway
37             #pod so that later in the debugger it can ensure that the execution
38             #pod environment is as close as possible to what it captured (and bitch at
39             #pod you about anything you are doing wrong).
40             #pod
41             #pod This is a huge help when resolving problems such as when a bug won't
42             #pod appear because you aren't debugging the script as the web user and in
43             #pod the same directory.
44             #pod
45             #pod =head2 Using CGI::Capture
46             #pod
47             #pod The brain-dead way is to use it as a pragma.
48             #pod
49             #pod Add the following to your web application BEFORE you load in CGI itself.
50             #pod
51             #pod use CGI::Capture 'cookiebug.dat';
52             #pod
53             #pod If the file C does not exist, CGI::Capture will take a
54             #pod snapshot of all the bits of the environment that matter to a CGI call, and
55             #pod freeze it to the file.
56             #pod
57             #pod If the file DOES exist however, CGI::Capture will load in the file and
58             #pod replace the current CGI call with the stored one.
59             #pod
60             #pod =head2 Security
61             #pod
62             #pod The actual captured CGI files are Storable CGI::Capture objects. If you
63             #pod want to use CGI::Capture in an environment where you have CODE references
64             #pod in your @INC path (such as with PAR files), you will need to disable
65             #pod security for Storable by setting $CGI::Capture::DEPARSE to true, which will
66             #pod enable B::Deparse and Eval support for stored objects.
67             #pod
68             #pod =head2 Hand-Crafting CGI Captures
69             #pod
70             #pod In its default usage, B takes an all or nothing approach,
71             #pod requiring you to capture absolutely every element of a CGI call.
72             #pod
73             #pod Sometimes you want to be a little more targeted, and for these situations
74             #pod an alternative methodology is provided.
75             #pod
76             #pod The C and C methods allow you to store and retrieve a
77             #pod CGI capture using L instead of L.
78             #pod
79             #pod Once you have stored the CGI capture as a YAML file, you can hand-edit the
80             #pod capture file, removing any keys you will not want to be restored, keeping
81             #pod only the useful parts.
82             #pod
83             #pod For example, to create a test file upload or CGI request involving
84             #pod cookies, you could discard everything except for the STDIN section of
85             #pod the capture file, which will then allow you to reuse the capture on
86             #pod other hosts, operating systems, and so on.
87             #pod
88             #pod =head1 METHODS
89             #pod
90             #pod In most cases, the above is all you probably need. However, if you want to
91             #pod get more fine-grained control, you can create and manipulate CGI::Capture
92             #pod object directly.
93             #pod
94             #pod =cut
95              
96 3     3   187814 use 5.006;
  3         31  
97 3     3   15 use strict;
  3         3  
  3         57  
98 3     3   11 use warnings;
  3         4  
  3         70  
99 3     3   12 use Carp ();
  3         5  
  3         40  
100 3     3   17 use Config ();
  3         3  
  3         58  
101 3     3   1483 use Storable 2.11 ();
  3         7831  
  3         77  
102 3     3   1112 use IO::Scalar 2.110 ();
  3         31435  
  3         79  
103 3     3   1289 use YAML::Tiny 1.36 ();
  3         13732  
  3         94  
104 3     3   1137 use Params::Util 0.37 qw{ _SCALAR0 _HASH0 _CODE _INSTANCE };
  3         9982  
  3         202  
105              
106             our $VERSION = '1.15';
107              
108 3     3   1025 use CGI::Capture::TieSTDIN ();
  3         7  
  3         3288  
109              
110             our $DEPARSE;
111              
112              
113              
114             #####################################################################
115             # Constructor and Accessors
116              
117             #pod =pod
118             #pod
119             #pod =head2 new
120             #pod
121             #pod The C only creates a new, empty, capture object.
122             #pod
123             #pod Because capturing is destructive to some values (STDIN for example) the
124             #pod capture method will capture and then immediately reapply the object, so that
125             #pod the current call can continue.
126             #pod
127             #pod Returns a CGI::Capture object. Never dies or returns an error, and so
128             #pod can be safely method-chained.
129             #pod
130             #pod =cut
131              
132             sub new {
133 5 50   5 1 969 my $class = ref $_[0] ? ref shift : shift;
134              
135             # Create the empty object
136 5         14 bless {}, $class;
137             }
138              
139             # The import expects a file name and does the following.
140             # 1. If the file does not exist, captures to it and continues.
141             # 2. If the file exists, restores from it and continues.
142             # 4. Does nothing if passed nothing.
143             sub import {
144 0 0   0   0 my $class = ref $_[0] ? ref shift : shift;
145 0 0       0 return 1 unless defined $_[0];
146 0 0       0 return (-f $_[0])
147             ? $class->apply(shift)
148             : $class->store(shift);
149             }
150              
151              
152              
153              
154              
155             #####################################################################
156             # Implement the Storable API
157              
158             #pod =pod
159             #pod
160             #pod =head2 store $filename
161             #pod
162             #pod This method behaves slightly differently in object and static context.
163             #pod
164             #pod In object context ( $object->store($filename) ) it stores the captured data
165             #pod to a file via Storable.
166             #pod
167             #pod In static context ( CGI::Capture->store($filename) ) automatically creates a
168             #pod new capture object, captures the CGI call, and then stores it, all in one hit.
169             #pod
170             #pod Returns as for Storable::store or dies if there is a problem storing the file.
171             #pod Also dies if it finds a CODE reference in @INC and you have not enabled
172             #pod C<$CGI::Capture::Deparse>.
173             #pod
174             #pod =cut
175              
176             sub store {
177 0 0   0 1 0 my $self = ref $_[0] ? shift : shift->capture;
178              
179             # Make sure we are allowed to use B::Deparse to serialise
180             # CODE refs in INC if needed.
181 0         0 my $any_CODE_refs = scalar grep { _CODE($_) } @{$self->{INC}};
  0         0  
  0         0  
182 0 0 0     0 if ( $any_CODE_refs and ! $DEPARSE ) {
183 0         0 die "Found a CODE reference in \@INC, but \$CGI::Capture::DEPARSE is not true";
184             }
185 0         0 local $Storable::Deparse = $any_CODE_refs;
186              
187 0         0 Storable::lock_nstore($self, shift);
188             }
189              
190             #pod =pod
191             #pod
192             #pod =head2 retrieve
193             #pod
194             #pod The C method is used identically to the Storable method of the
195             #pod same name, and wraps it.
196             #pod
197             #pod Loads in a stored CGI::Capture object from a file.
198             #pod
199             #pod If the stored object had a CODE ref in it's @INC, you will also need to
200             #pod enable $CGI::Capture::DEPARSE when loading the file.
201             #pod
202             #pod Returns a new CGI::Capture object, or dies on failure.
203             #pod
204             #pod =cut
205              
206             sub retrieve {
207 0 0   0 1 0 my $class = ref $_[0] ? ref shift : shift;
208 0         0 local $Storable::Eval = $DEPARSE;
209 0         0 my $self = Storable::lock_retrieve(shift);
210 0 0       0 return $self if _INSTANCE($self, $class);
211 0         0 die "Storable did not contains a $class object";
212             }
213              
214             #pod =pod
215             #pod
216             #pod =head2 as_yaml
217             #pod
218             #pod To allow for more portable storage and communication of the CGI
219             #pod environment, the C method can be used to generate a YAML
220             #pod document for the request (generated via L).
221             #pod
222             #pod Returns a YAML::Tiny object.
223             #pod
224             #pod =cut
225              
226             sub as_yaml {
227 2     2 1 5 my $self = shift;
228 2         7 my $yaml = YAML::Tiny->new;
229              
230             # Populate the YAML
231 2         165 $yaml->[0] = Storable::dclone( { %$self } );
232 2         10 $yaml->[0]->{STDIN} = ${$yaml->[0]->{STDIN}};
  2         7  
233              
234 2         6 return $yaml;
235             }
236              
237             #pod =pod
238             #pod
239             #pod =head2 from_yaml
240             #pod
241             #pod To allow for more portable storage and communication of the CGI
242             #pod environment, the C method can be used to restore a
243             #pod B object from a L object.
244             #pod
245             #pod Returns a new B object, or croaks if passed an
246             #pod invalid parameter.
247             #pod
248             #pod =cut
249              
250             sub from_yaml {
251 2     2 1 4 my $class = shift;
252              
253             # Check params
254 2         3 my $yaml = shift;
255 2 50       20 unless ( _INSTANCE($yaml, 'YAML::Tiny') ) {
256 0         0 Carp::croak("Did not provide a YAML::Tiny object to from_yaml");
257             }
258 2 50       7 unless ( _HASH0($yaml->[0]) ) {
259 0         0 Carp::croak("The YAML::Tiny object does not have a HASH as first element");
260             }
261              
262             # Create the object
263 2         5 my $self = $class->new;
264 2         3 %$self = %{$yaml->[0]};
  2         21  
265              
266             # Correct some nigglies
267 2 50       7 if ( exists $self->{STDIN} ) {
268 2         4 my $stdin = $self->{STDIN};
269 2         5 $self->{STDIN} = \$stdin;
270             }
271              
272 2         12 return $self;
273             }
274              
275             #pod =pod
276             #pod
277             #pod =head2 as_yaml_string
278             #pod
279             #pod To allow for more portable storage and communication of the CGI
280             #pod environment, the C method can be used to generate a YAML
281             #pod document for the request (generated via L).
282             #pod
283             #pod Returns a YAML document as a string.
284             #pod
285             #pod =cut
286              
287             sub as_yaml_string {
288 1     1 1 6901 $_[0]->as_yaml->write_string;
289             }
290              
291             #pod =pod
292             #pod
293             #pod =head2 from_yaml_string
294             #pod
295             #pod To allow for more portable storage and communication of the CGI
296             #pod environment, the C method can be used to
297             #pod restore a B object from a string containing a YAML
298             #pod document.
299             #pod
300             #pod Returns a new B object, or croaks if the YAML document
301             #pod is invalid.
302             #pod
303             #pod =cut
304              
305             sub from_yaml_string {
306 2     2 1 2501 my $class = shift;
307 2         4 my $string = shift;
308 2         7 my $yaml = YAML::Tiny->read_string( $string );
309 2         3119 return $class->from_yaml( $yaml );
310             }
311              
312              
313              
314              
315              
316             #####################################################################
317             # Main Methods
318              
319             #pod =pod
320             #pod
321             #pod =head2 capture
322             #pod
323             #pod Again, C can be used either as an object or static methods
324             #pod
325             #pod When called as an object method ( $object->capture ) it captures the
326             #pod current CGI call environment into the object, replacing the existing
327             #pod one if needed.
328             #pod
329             #pod When called as a static method ( CGI::Capture->capture ) it acts as a
330             #pod constructor, creating an object and capturing the CGI call into it
331             #pod before returning it.
332             #pod
333             #pod In both cases, returns the CGI::Capture object. This method will not
334             #pod die or return an error and can be safely method-chained.
335             #pod
336             #pod =cut
337              
338             sub capture {
339 2 100   2 1 715 my $self = ref $_[0] ? shift : shift->new;
340              
341             # Reset the object
342 2         13 %$self = (
343             CAPTURE_TIME => time,
344             CAPTURE_VERSION => $VERSION,
345             );
346              
347             # Capture the environment
348 2         43 $self->{ENV} = { %ENV };
349              
350             # Grab ARGV just to be on the safe side
351 2         6 $self->{ARGV} = [ @ARGV ];
352              
353 2 50       11 if ( -t STDIN ) {
354             # Interactive mode
355 0         0 $self->{STDIN} = \'';
356             } else {
357             # Grab the contents of STDIN
358 2         4 $self->{STDIN} = do { local $/; my $tmp = ; \$tmp };
  2         5  
  2         17  
  2         8  
359              
360             # Having captured it, restore it
361 2         6 $self->_stdin( $self->{STDIN} );
362             }
363              
364             # Grab the include path
365 2         8 $self->{INC} = [ @INC ];
366              
367             # Grab various environment-like state variables.
368             # Especially ones they might have changed.
369 2         5 $self->{OUTPUT_AUTOFLUSH} = $|;
370 2         13 $self->{REAL_USER_ID} = $<;
371 2         11 $self->{EFFECTIVE_USER_ID} = $>;
372 2         18 $self->{REAL_GROUP_ID} = $(;
373 2         13 $self->{EFFECTIVE_GROUP_ID} = $);
374 2         5 $self->{PROGRAM_NAME} = $0;
375 2         5 $self->{OSNAME} = $^O;
376 2         13 $self->{TAINT} = ${^TAINT};
377 2         6 $self->{PERL_VERSION} = $];
378              
379             # Capture the most critical %Config values
380 2         6 $self->{CONFIG_PATH} = $INC{'Config.pm'};
381 2         129 $self->{PERL_PATH} = $Config::Config{perlpath};
382              
383 2         13 $self;
384             }
385              
386             #pod =pod
387             #pod
388             #pod =head2 apply [ $filename ]
389             #pod
390             #pod Again, C works different when called as an object of static method.
391             #pod
392             #pod If called as an object method ( $object->apply ) it will take the CGI
393             #pod call the object contains, and apply it to the current environment.
394             #pod Because this works at the environment level, it needs to be done BEFORE
395             #pod CGI.pm attempts to create the CGI object.
396             #pod
397             #pod The C method will also check certain values against the current
398             #pod environment. In short, if it can't alter the environment, it won't run unless
399             #pod YOU alter the environment and try again.
400             #pod
401             #pod These include the real and effective user and group, the OS name, the perl
402             #pod version, and whether Tainting is on or off.
403             #pod
404             #pod The effect is to really make sure you are replaying the call in your console
405             #pod debugger exactly as it was from the browser, and you aren't accidentally using
406             #pod a different user, a different perl, or are making some other overlooked and
407             #pod hard to debug mistake.
408             #pod
409             #pod In the future, by request, I may add some options to selectively disable some
410             #pod of the tests. But unless someone asks, I'm leaving all of them on.
411             #pod
412             #pod In the static context, ( CGI::Capture->apply($file) ) it takes a filename
413             #pod argument, immediately retrieves the CGI call from the object and immediately
414             #pod applies it to the current environment.
415             #pod
416             #pod In both context, returns true on success or dies on error, or it your testing
417             #pod environment does not match.
418             #pod
419             #pod =cut
420              
421             sub apply {
422 1 50   1 1 785 my $self = ref $_[0] ? shift : shift->retrieve(shift);
423 1 50       3 $self->{CAPTURE_TIME} or die "Cannot apply empty capture object";
424              
425             # Update the environment
426 1 50       2 if ( exists $self->{ENV} ) {
427 1         2 %ENV = %{$self->{ENV}};
  1         29  
428             }
429              
430             # Set @ARGV
431 1 50       3 if ( exists $self->{ARGV} ) {
432 0         0 @ARGV = @{$self->{ARGV}};
  0         0  
433             }
434              
435             # Set STDIN
436 1 50       4 if ( exists $self->{STDIN} ) {
437 1         3 $self->_stdin( $self->{STDIN} );
438             }
439              
440             # Replace INC
441 1 50       3 if ( exists $self->{INC} ) {
442 0         0 @INC = @{$self->{INC}};
  0         0  
443             }
444              
445             # Replace the internal variables we are allowed to
446 1 50       2 if ( exists $self->{OUTPUT_AUTOFLUSH} ) {
447 0         0 $| = $self->{OUTPUT_AUTOFLUSH};
448             }
449 1 50       3 if ( exists $self->{PROGRAM_NAME} ) {
450 0         0 $0 = $self->{PROGRAM_NAME};
451             }
452              
453             # Check that the variables we can't control match
454 1         3 $self->_check( CAPTURE_VERSION => $VERSION );
455 1         3 $self->_check( OSNAME => $^O );
456 1         3 $self->_check( REAL_USER_ID => $< );
457 1         3 $self->_check( EFFECTIVE_USER_ID => $> );
458 1         3 $self->_check( REAL_GROUP_ID => $( );
459 1         2 $self->_check( EFFECTIVE_GROUP_ID => $) );
460 1         2 $self->_check( TAINT => ${^TAINT} );
461 1         2 $self->_check( PERL_VERSION => $] );
462 1         3 $self->_check( CONFIG_PATH => $INC{'Config.pm'} );
463 1         4 $self->_check( PERL_PATH => $Config::Config{perlpath} );
464              
465 1         3 1;
466             }
467              
468             # Checks a stored value against its current value
469             sub _check {
470 10     10   11 my $self = shift;
471 10 50       12 my $name = defined $_[0] ? shift : die "Var name not passed to ->_check";
472 10 50       16 unless ( exists $self->{$name} ) {
473             # Not defined in the capture, nothing to check
474 10         11 return;
475             }
476 0         0 my $value = shift;
477 0 0 0     0 unless ( defined $self->{$name} or defined $value ) {
478 0         0 return 1;
479             }
480 0 0 0     0 if ( defined $self->{$name} and defined $value ) {
481 0 0       0 return 1 if $self->{$name} eq $value;
482             }
483              
484             # Didn't match
485 0 0       0 my $current = defined $value ? '"' . quotemeta($value) . '"' : 'undef';
486 0 0       0 my $cgi = defined $self->{$name} ? '"' . quotemeta($self->{$name}) . '"' : 'undef';
487 0         0 die "Current $name $current does not match the captured CGI call $cgi";
488             }
489              
490             # Takes a scalar reference and sets STDIN to read from it
491             sub _stdin {
492 4     4   105 my $self = shift;
493 4 50       16 my $scalar_ref = _SCALAR0($_[0]) ? shift
494             : die "SCALAR reference not passed to ->_stdin";
495 4         28 tie *MYSTDIN, 'CGI::Capture::TieSTDIN', $scalar_ref;
496 4         15 *STDIN = *MYSTDIN;
497             }
498              
499             1;
500              
501             __END__