1+ # ====================================================================
2+ # The Apache Software License, Version 1.1
3+ #
4+ # Copyright (c) 2000-2003 The Apache Software Foundation. All rights
5+ # reserved.
6+ #
7+ # Redistribution and use in source and binary forms, with or without
8+ # modification, are permitted provided that the following conditions
9+ # are met:
10+ #
11+ # 1. Redistributions of source code must retain the above copyright
12+ # notice, this list of conditions and the following disclaimer.
13+ #
14+ # 2. Redistributions in binary form must reproduce the above copyright
15+ # notice, this list of conditions and the following disclaimer in
16+ # the documentation and/or other materials provided with the
17+ # distribution.
18+ #
19+ # 3. The end-user documentation included with the redistribution,
20+ # if any, must include the following acknowledgment:
21+ # "This product includes software developed by the
22+ # Apache Software Foundation (http://www.apache.org/)."
23+ # Alternately, this acknowledgment may appear in the software itself,
24+ # if and wherever such third-party acknowledgments normally appear.
25+ #
26+ # 4. The names "Apache" and "Apache Software Foundation" must
27+ # not be used to endorse or promote products derived from this
28+ # software without prior written permission. For written
29+ # permission, please contact apache@apache.org.
30+ #
31+ # 5. Products derived from this software may not be called "Apache",
32+ # "mod_python", or "modpython", nor may these terms appear in their
33+ # name, without prior written permission of the Apache Software
34+ # Foundation.
35+ #
36+ # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37+ # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39+ # DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40+ # ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43+ # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46+ # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47+ # SUCH DAMAGE.
48+ # ====================================================================
49+ #
50+ # This software consists of voluntary contributions made by many
51+ # individuals on behalf of the Apache Software Foundation. For more
52+ # information on the Apache Software Foundation, please see
53+ # <http://www.apache.org/>.
54+ #
55+ # Originally developed by Gregory Trubetskoy.
56+ #
57+ # $Id: Cookie.py,v 1.2 2003/06/17 02:35:15 grisha Exp $
158
259"""
360
4- (C) 2003 Apache Software Foundation
61+ This module contains classes to support HTTP State Management
62+ Mechanism, also known as Cookies. The classes provide simple
63+ ways for creating, parsing and digitally signing cookies, as
64+ well as the ability to store simple Python objects in Cookies
65+ (using marshalling).
66+
67+ The behaviour of the classes is designed to be most useful
68+ within mod_python applications.
69+
70+ The current state of HTTP State Management standardization is
71+ rather unclear. It appears that the de-facto standard is the
72+ original Netscape specification, even though already two RFC's
73+ have been put out (RFC2109 (1997) and RFC2965 (2000)). The
74+ RFC's add a couple of useful features (e.g. using Max-Age instead
75+ of Expires, but my limited tests show that Max-Age is ignored
76+ by the two browsers tested (IE and Safari). As a result of this,
77+ perhaps trying to be RFC-compliant (by automatically providing
78+ Max-Age and Version) could be a waste of cookie space...
579
6- $Id: Cookie.py,v 1.1 2003/06/14 03:03:37 grisha Exp $
780
881Sample usage:
982
2699 File "<stdin>", line 1, in ?
27100 AttributeError: 'Cookie' object has no attribute 'eggs'
28101
29- parsing (note the result is a dict of cookies)
102+ * parsing (note the result is a dict of cookies)
30103
31104>>> Cookie.parse(str(c))
32105{'spam': <Cookie: spam=eggs; version=1; expires=Sat, 14-Jun-2003 02:42:36 GMT; max_age=3>}
73146import marshal
74147import base64
75148
149+ from mod_python import apache
150+
76151class CookieError (Exception ):
77152 pass
78153
79154class Cookie (object ):
80155 """
81- This class reflects the sorry state that standardizaion of
82- HTTP State Management is currently in. Even though RFC2109
83- has been out since 1997, and was even upgraded by RFC2165 in
84- 2000, most browsers out there still support the buggy Netscape
85- specification...
86- Here we try to be RFC2109 compliant while making all necessary
87- adjustments for those clients that only understand the NS spec.
88-
89-
156+ This class implements the basic Cookie functionality. Note that
157+ unlike the Python Standard Library Cookie class, this class represents
158+ a single cookie (not a list of Morsels).
90159 """
91160
92161 _valid_attr = (
@@ -102,27 +171,48 @@ class Cookie(object):
102171 "_expires" , "_max_age" )
103172
104173 def parse (Class , str ):
174+ """
175+ Parse a Cookie or Set-Cookie header value, and return
176+ a dict of Cookies. Note: the string should NOT include the
177+ header name, only the value.
178+ """
105179
106180 dict = _parseCookie (str , Class )
107-
108181 return dict
109182
110183 parse = classmethod (parse )
111184
185+
112186 def __init__ (self , name , value , ** kw ):
113187
188+ """
189+ This constructor takes at least a name and value as the
190+ arguments, as well as optionally any of allowed cookie attributes
191+ as defined in the existing cookie standards.
192+ """
193+
114194 self .name , self .value = name , value
115195
116196 for k in kw :
117- setattr (self , k , kw [k ])
197+ setattr (self , k . lower () , kw [k ])
118198
119199 if not hasattr (self , "version" ):
120200 self .version = "1"
121201
202+
122203 def __str__ (self ):
123204
124- # NOTE: quoting the value is up to the user!
205+ """
206+ Provides the string representation of the Cookie suitable for
207+ sending to the browser. Note that the actual header name will
208+ not be part of the string.
125209
210+ This method makes no attempt to automatically double-quote
211+ strings that contain special characters, even though the RFC's
212+ dictate this. This is because doing so seems to confuse most
213+ browsers out there.
214+ """
215+
126216 result = ["%s=%s" % (self .name , self .value )]
127217 for name in self ._valid_attr :
128218 if hasattr (self , name ):
@@ -182,6 +272,15 @@ def get_max_age(self):
182272 max_age = property (fget = get_max_age , fset = set_max_age )
183273
184274class SignedCookie (Cookie ):
275+ """
276+ This is a variation of Cookie that provides automatic
277+ cryptographic signing of cookies and verification. It uses
278+ the HMAC support in the Python standard library. This ensures
279+ that the cookie has not been tamprered with on the client side.
280+
281+ Note that this class does not encrypt cookie data, thus it
282+ is still plainly visible as part of the cookie.
283+ """
185284
186285 def parse (Class , secret , str ):
187286
@@ -240,13 +339,20 @@ def unsign(self, secret):
240339
241340class MarshalCookie (SignedCookie ):
242341
243- # It is wrong to assume that unmarshalling data is safe, though
244- # here is a link to a sugesstion that it is at least safer than unpickling
245- # http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=7xn0hcugmy.fsf%40ruckus.brouhaha.com
246- #
247- # This class unmarshals only signed cookies, so we're pretty safe
248- #
249-
342+ """
343+ This is a variation of SignedCookie that can store more than
344+ just strings. It will automatically marshal the cookie value,
345+ therefore any marshallable object can be used as value.
346+
347+ The standard library Cookie module provides the ability to pickle
348+ data, which is a major security problem. It is believed that unmarshalling
349+ (as opposed to unpickling) is safe, yet we still err on the side of caution
350+ which is why this class is a subclass of SignedCooke making sure what
351+ we are about to unmarshal passes the digital signature test.
352+
353+ Here is a link to a sugesstion that marshalling is safer than unpickling
354+ http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=7xn0hcugmy.fsf%40ruckus.brouhaha.com
355+ """
250356 __slots__ = SignedCookie .__slots__
251357
252358 expires = property (fget = Cookie .get_expires , fset = Cookie .set_expires )
@@ -303,6 +409,8 @@ def unmarshal(self, secret):
303409
304410def _parseCookie (str , Class ):
305411
412+ # XXX problem is we should allow duplicate
413+ # strings
306414 result = {}
307415
308416 # max-age is a problem because of the '-'
0 commit comments