File Coverage

inc/CGI/Application/Plugin/Session.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             #line 1
2             package CGI::Application::Plugin::Session;
3 4     4   5695  
  0            
  0            
4             use CGI::Session ();
5             use File::Spec ();
6             use CGI::Application 3.21;
7             use Carp qw(croak);
8             use Scalar::Util ();
9              
10             use strict;
11             use vars qw($VERSION @EXPORT);
12              
13             require Exporter;
14              
15             @EXPORT = qw(
16             session
17             session_config
18             session_cookie
19             session_delete
20             );
21             sub import { goto &Exporter::import }
22              
23             $VERSION = '1.02';
24              
25             sub session {
26             my $self = shift;
27              
28             if (!$self->{__CAP__SESSION_OBJ}) {
29             # define the config hash if it doesn't exist to save some checks later
30             $self->{__CAP__SESSION_CONFIG} = {} unless $self->{__CAP__SESSION_CONFIG};
31              
32             # gather parameters for the CGI::Session module from the user,
33             # or use some sane defaults
34             my @params = ($self->{__CAP__SESSION_CONFIG}->{CGI_SESSION_OPTIONS}) ?
35             @{ $self->{__CAP__SESSION_CONFIG}->{CGI_SESSION_OPTIONS} } :
36             ('driver:File', $self->query, {Directory=>File::Spec->tmpdir});
37              
38              
39             # CGI::Session only works properly with CGI.pm so extract the sid manually if
40             # another module is being used
41             if (Scalar::Util::blessed($params[1]) && ! $params[1]->isa('CGI')) {
42             my $sid = $params[1]->cookie(CGI::Session->name) || $params[1]->param(CGI::Session->name);
43             $params[1] = $sid;
44             }
45              
46             # create CGI::Session object or die with an error
47             $self->{__CAP__SESSION_OBJ} = CGI::Session->new(@params);
48             if (! $self->{__CAP__SESSION_OBJ} ) {
49             my $errstr = CGI::Session->errstr || 'Unknown';
50             croak "Failed to Create CGI::Session object :: Reason: $errstr";
51             }
52              
53             # Set the default expiry if requested and if this is a new session
54             if ($self->{__CAP__SESSION_CONFIG}->{DEFAULT_EXPIRY} && $self->{__CAP__SESSION_OBJ}->is_new) {
55             $self->{__CAP__SESSION_OBJ}->expire($self->{__CAP__SESSION_CONFIG}->{DEFAULT_EXPIRY});
56             }
57              
58             # add the cookie to the outgoing headers under the following conditions
59             # if the cookie doesn't exist,
60             # or if the session ID doesn't match what is in the current cookie,
61             # or if the session has an expiry set on it
62             # but don't send it if SEND_COOKIE is set to 0
63             if (!defined $self->{__CAP__SESSION_CONFIG}->{SEND_COOKIE} || $self->{__CAP__SESSION_CONFIG}->{SEND_COOKIE}) {
64             my $cid = $self->query->cookie(CGI::Session->name);
65             if (!$cid || $cid ne $self->{__CAP__SESSION_OBJ}->id || $self->{__CAP__SESSION_OBJ}->expire()) {
66             session_cookie($self);
67             }
68             }
69             }
70              
71             return $self->{__CAP__SESSION_OBJ};
72             }
73              
74             sub session_config {
75             my $self = shift;
76              
77             if (@_) {
78             die "Calling session_config after the session has already been created" if (defined $self->{__CAP__SESSION_OBJ});
79             my $props;
80             if (ref($_[0]) eq 'HASH') {
81             my $rthash = %{$_[0]};
82             $props = $self->_cap_hash($_[0]);
83             } else {
84             $props = $self->_cap_hash({ @_ });
85             }
86              
87             # Check for CGI_SESSION_OPTIONS
88             if ($props->{CGI_SESSION_OPTIONS}) {
89             die "session_config error: parameter CGI_SESSION_OPTIONS is not an array reference" if ref $props->{CGI_SESSION_OPTIONS} ne 'ARRAY';
90             $self->{__CAP__SESSION_CONFIG}->{CGI_SESSION_OPTIONS} = delete $props->{CGI_SESSION_OPTIONS};
91             }
92              
93             # Check for COOKIE_PARAMS
94             if ($props->{COOKIE_PARAMS}) {
95             die "session_config error: parameter COOKIE_PARAMS is not a hash reference" if ref $props->{COOKIE_PARAMS} ne 'HASH';
96             $self->{__CAP__SESSION_CONFIG}->{COOKIE_PARAMS} = delete $props->{COOKIE_PARAMS};
97             }
98              
99             # Check for SEND_COOKIE
100             if (defined $props->{SEND_COOKIE}) {
101             $self->{__CAP__SESSION_CONFIG}->{SEND_COOKIE} = (delete $props->{SEND_COOKIE}) ? 1 : 0;
102             }
103              
104             # Check for DEFAULT_EXPIRY
105             if (defined $props->{DEFAULT_EXPIRY}) {
106             $self->{__CAP__SESSION_CONFIG}->{DEFAULT_EXPIRY} = delete $props->{DEFAULT_EXPIRY};
107             }
108              
109             # If there are still entries left in $props then they are invalid
110             die "Invalid option(s) (".join(', ', keys %$props).") passed to session_config" if %$props;
111             }
112              
113             $self->{__CAP__SESSION_CONFIG};
114             }
115              
116             sub session_cookie {
117             my $self = shift;
118             my %options = @_;
119              
120             # merge in any parameters set by config_session
121             if ($self->{__CAP__SESSION_CONFIG}->{COOKIE_PARAMS}) {
122             %options = (%{ $self->{__CAP__SESSION_CONFIG}->{COOKIE_PARAMS} }, %options);
123             }
124            
125             if (!$self->{__CAP__SESSION_OBJ}) {
126             # The session object has not been created yet, so make sure we at least call it once
127             my $tmp = $self->session;
128             }
129              
130             $options{'-name'} ||= CGI::Session->name;
131             $options{'-value'} ||= $self->session->id;
132             if(defined($self->session->expires()) && !defined($options{'-expires'})) {
133             $options{'-expires'} = _build_exp_time( $self->session->expires() );
134             }
135             my $cookie = $self->query->cookie(%options);
136             $self->header_add(-cookie => [$cookie]);
137             }
138              
139             sub _build_exp_time {
140             my $secs_until_expiry = shift;
141             return unless defined $secs_until_expiry;
142              
143             # Add a plus sign unless the number is negative
144             my $prefix = ($secs_until_expiry >= 0) ? '+' : '';
145              
146             # Add an 's' for "seconds".
147             return $prefix.$secs_until_expiry.'s';
148             }
149              
150             sub session_delete {
151             my $self = shift;
152              
153             if ( my $session = $self->session ) {
154             $session->delete;
155             if ( $self->{'__CAP__SESSION_CONFIG'}->{'SEND_COOKIE'} ) {
156             my %options;
157             if ( $self->{'__CAP__SESSION_CONFIG'}->{'COOKIE_PARAMS'} ) {
158             %options = ( %{ $self->{'__CAP__SESSION_CONFIG'}->{'COOKIE_PARAMS'} }, %options );
159             }
160             $options{'name'} ||= CGI::Session->name;
161             $options{'value'} = '';
162             $options{'-expires'} = '-1d';
163             my $newcookie = $self->query->cookie(%options);
164              
165             # See if a session cookie has already been set (this will happen if
166             # this is a new session). We keep all existing cookies except the
167             # session cookie, which we replace with the timed out session
168             # cookie
169             my @keep;
170             my %headers = $self->header_props;
171             my $cookies = $headers{'-cookie'} || [];
172             $cookies = [$cookies] unless ref $cookies eq 'ARRAY';
173             foreach my $cookie (@$cookies) {
174             if ( ref($cookie) ne 'CGI::Cookie' || $cookie->name ne CGI::Session->name ) {
175             # keep this cookie
176             push @keep, $cookie;
177             }
178             }
179             push @keep, $newcookie;
180              
181             # We have to set the cookies this way, because CGI::Application has
182             # an annoying interface to the headers (why can't we have
183             # 'header_set as well as header_add?). The first call replaces all
184             # cookie headers with the one new cookie header, and the next call
185             # adds in the rest of the cookies if there are any.
186             $self->header_add( -cookie => shift @keep );
187             $self->header_add( -cookie => \@keep ) if @keep;
188             }
189             }
190             }
191              
192             1;
193             __END__