fix LTJake's always expiring second session issue
[catagits/Catalyst-Plugin-Session-State-Cookie.git] / lib / Catalyst / Plugin / Session / State / Cookie.pm
1 package Catalyst::Plugin::Session::State::Cookie;
2 use base qw/Catalyst::Plugin::Session::State Class::Accessor::Fast/;
3
4 use strict;
5 use warnings;
6
7 use NEXT;
8 use Catalyst::Utils ();
9
10 our $VERSION = "0.07";
11
12 BEGIN { __PACKAGE__->mk_accessors(qw/_deleted_session_id/) }
13
14 sub setup_session {
15     my $c = shift;
16
17     $c->NEXT::setup_session(@_);
18
19     $c->config->{session}{cookie_name}
20         ||= Catalyst::Utils::appprefix($c) . '_session';
21 }
22
23 sub extend_session_id {
24     my ( $c, $sid, $expires ) = @_;
25
26     if ( my $cookie = $c->get_session_cookie ) {
27         $c->update_session_cookie( $c->make_session_cookie( $sid ) );
28     }
29
30     $c->NEXT::extend_session_id( $sid, $expires );
31 }
32
33 sub set_session_id {
34     my ( $c, $sid ) = @_;
35
36     $c->update_session_cookie( $c->make_session_cookie( $sid ) );
37
38     return $c->NEXT::set_session_id($sid);
39 }
40
41 sub update_session_cookie {
42     my ( $c, $updated ) = @_;
43     
44     unless ( $c->cookie_is_rejecting( $updated ) ) {
45         my $cookie_name = $c->config->{session}{cookie_name};
46         $c->response->cookies->{$cookie_name} = $updated;
47     }
48 }
49
50 sub cookie_is_rejecting {
51     my ( $c, $cookie ) = @_;
52     
53     if ( $cookie->{path} ) {
54         return 1 if index '/'.$c->request->path, $cookie->{path};
55     }
56     
57     return 0;
58 }
59
60 sub make_session_cookie {
61     my ( $c, $sid, %attrs ) = @_;
62
63     my $cfg    = $c->config->{session};
64     my $cookie = {
65         value => $sid,
66         ( $cfg->{cookie_domain} ? ( domain => $cfg->{cookie_domain} ) : () ),
67         ( $cfg->{cookie_path} ? ( path => $cfg->{cookie_path} ) : () ),
68         %attrs,
69     };
70
71     unless ( exists $cookie->{expires} ) {
72         $cookie->{expires} = $c->calculate_session_cookie_expires();
73     }
74
75     $cookie->{secure} = 1 if $cfg->{cookie_secure};
76
77     return $cookie;
78 }
79
80 sub calc_expiry { # compat
81     my $c = shift;
82     $c->NEXT::calc_expiry( @_ ) || $c->calculate_session_cookie_expires( @_ );
83 }
84
85 sub calculate_session_cookie_expires {
86     my $c   = shift;
87     my $cfg = $c->config->{session};
88
89     my $value = $c->NEXT::calculate_session_cookie_expires(@_);
90     return $value if $value;
91
92     if ( exists $cfg->{cookie_expires} ) {
93         if ( $cfg->{cookie_expires} > 0 ) {
94             return time() + $cfg->{cookie_expires};
95         }
96         else {
97             return undef;
98         }
99     }
100     else {
101         return $c->session_expires;
102     }
103 }
104
105 sub get_session_cookie {
106     my $c = shift;
107
108     my $cookie_name = $c->config->{session}{cookie_name};
109
110     return $c->request->cookies->{$cookie_name};
111 }
112
113 sub get_session_id {
114     my $c = shift;
115
116     if ( !$c->_deleted_session_id and my $cookie = $c->get_session_cookie ) { 
117         my $sid = $cookie->value;
118         $c->log->debug(qq/Found sessionid "$sid" in cookie/) if $c->debug;
119         return $sid if $sid;
120     }
121
122     $c->NEXT::get_session_id(@_);
123 }
124
125 sub delete_session_id {
126     my ( $c, $sid ) = @_;
127     
128     $c->_deleted_session_id(1); # to prevent get_session_id from returning it
129
130     $c->update_session_cookie( $c->make_session_cookie( $sid, expires => 0 ) );
131
132     $c->NEXT::delete_session_id($sid);
133 }
134
135 __PACKAGE__
136
137 __END__
138
139 =pod
140
141 =head1 NAME
142
143 Catalyst::Plugin::Session::State::Cookie - Maintain session IDs using cookies.
144
145 =head1 SYNOPSIS
146
147     use Catalyst qw/Session Session::State::Cookie Session::Store::Foo/;
148
149 =head1 DESCRIPTION
150
151 In order for L<Catalyst::Plugin::Session> to work the session ID needs to be
152 stored on the client, and the session data needs to be stored on the server.
153
154 This plugin stores the session ID on the client using the cookie mechanism.
155
156 =head1 METHODS
157
158 =over 4
159
160 =item make_session_cookie
161
162 Returns a hash reference with the default values for new cookies.
163
164 =item update_session_cookie $hash_ref
165
166 Sets the cookie based on C<cookie_name> in the response object.
167
168 =back
169
170 =head1 EXTENDED METHODS
171
172 =over 4
173
174 =item prepare_cookies
175
176 Will restore if an appropriate cookie is found.
177
178 =item finalize_cookies
179
180 Will set a cookie called C<session> if it doesn't exist or if it's value is not
181 the current session id.
182
183 =item setup_session
184
185 Will set the C<cookie_name> parameter to it's default value if it isn't set.
186
187 =back
188
189 =head1 CONFIGURATION
190
191 =over 4
192
193 =item cookie_name
194
195 The name of the cookie to store (defaults to C<Catalyst::Utils::apprefix($c) . '_session'>).
196
197 =item cookie_domain
198
199 The name of the domain to store in the cookie (defaults to current host)
200
201 =item cookie_expires
202
203 Number of seconds from now you want to elapse before cookie will expire. 
204 Set to 0 to create a session cookie, ie one which will die when the 
205 user's browser is shut down.
206
207 =item cookie_secure
208
209 If this attribute set true, the cookie will only be sent via HTTPS.
210
211 =item cookie_path
212
213 The path of the request url where cookie should be baked.
214
215 =back
216
217 =head1 CAVEATS
218
219 Sessions have to be created before the first write to be saved. For example:
220
221         sub action : Local {
222                 my ( $self, $c ) = @_;
223                 $c->res->write("foo");
224                 $c->session( ... );
225                 ...
226         }
227
228 Will cause a session ID to not be set, because by the time a session is
229 actually created the headers have already been sent to the client.
230
231 =head1 SEE ALSO
232
233 L<Catalyst>, L<Catalyst::Plugin::Session>.
234
235 =head1 AUTHORS
236
237 This module is derived from L<Catalyst::Plugin::Session::FastMmap> code, and
238 has been heavily modified since.
239
240 Andrew Ford
241 Andy Grundman
242 Christian Hansen
243 Yuval Kogman, C<nothingmuch@woobling.org>
244 Marcus Ramberg
245 Sebastian Riedel
246
247 =head1 COPYRIGHT
248
249 This program is free software, you can redistribute it and/or modify it
250 under the same terms as Perl itself.
251
252 =cut
253
254 1;