Imported Upstream version 0.3.1
SVN-Git Migration
8 years ago
1 | 1 | ================== |
2 | 2 | |
3 | 3 | |
4 | Version 0.3.1 | |
5 | ------------- | |
6 | (released on June 24th 2008) | |
7 | ||
8 | - fixed a security problem with `werkzeug.contrib.SecureCookie`. | |
9 | More details available in the `release announcement`_. | |
10 | ||
11 | .. _release announcement: http://lucumr.pocoo.org/cogitations/2008/06/24/werkzeug-031-released/ | |
12 | ||
4 | 13 | Version 0.3 |
5 | 14 | ----------- |
6 | (codename to be selected, release date somewhere around May 2008) | |
15 | (codename EUR325CAT6, released on June 14th 2008) | |
7 | 16 | |
8 | 17 | - added support for redirecting in url routing. |
9 | 18 | - added `Authorization` and `AuthorizationMixin` |
0 | 0 | Metadata-Version: 1.0 |
1 | 1 | Name: Werkzeug |
2 | Version: 0.3 | |
2 | Version: 0.3.1 | |
3 | 3 | Summary: The Swiss Army knife of Python web development |
4 | 4 | Home-page: http://werkzeug.pocoo.org/ |
5 | 5 | Author: Armin Ronacher |
0 | 0 | Metadata-Version: 1.0 |
1 | 1 | Name: Werkzeug |
2 | Version: 0.3 | |
2 | Version: 0.3.1 | |
3 | 3 | Summary: The Swiss Army knife of Python web development |
4 | 4 | Home-page: http://werkzeug.pocoo.org/ |
5 | 5 | Author: Armin Ronacher |
37 | 37 | docs/build/test.html |
38 | 38 | docs/build/tutorial.html |
39 | 39 | docs/build/tutorial_de.html |
40 | docs/build/unicode.html | |
41 | 40 | docs/build/utils.html |
42 | 41 | docs/build/werkzeug.png |
43 | 42 | docs/build/wrappers.html |
73 | 72 | examples/manage-shorty.py |
74 | 73 | examples/manage-simplewiki.py |
75 | 74 | examples/manage-webpylike.py |
76 | examples/objectapp.py | |
77 | 75 | examples/upload.py |
78 | 76 | examples/contrib/README |
79 | 77 | examples/contrib/securecookie.py |
96 | 94 | examples/couchy/utils.py |
97 | 95 | examples/couchy/views.py |
98 | 96 | examples/couchy/static/style.css |
99 | examples/couchy/templates/.DS_Store | |
100 | 97 | examples/couchy/templates/display.html |
101 | 98 | examples/couchy/templates/layout.html |
102 | 99 | examples/couchy/templates/list.html |
103 | 100 | examples/couchy/templates/new.html |
104 | 101 | examples/couchy/templates/not_found.html |
105 | 102 | examples/cupoftee/__init__.py |
106 | examples/cupoftee/__init__.pyc | |
107 | 103 | examples/cupoftee/application.py |
108 | examples/cupoftee/application.pyc | |
109 | 104 | examples/cupoftee/db.py |
110 | examples/cupoftee/db.pyc | |
111 | 105 | examples/cupoftee/network.py |
112 | examples/cupoftee/network.pyc | |
113 | 106 | examples/cupoftee/pages.py |
114 | examples/cupoftee/pages.pyc | |
115 | 107 | examples/cupoftee/utils.py |
116 | examples/cupoftee/utils.pyc | |
117 | 108 | examples/cupoftee/shared/content.png |
118 | 109 | examples/cupoftee/shared/down.png |
119 | 110 | examples/cupoftee/shared/favicon.ico |
137 | 128 | examples/partial/README |
138 | 129 | examples/partial/complex_routing.py |
139 | 130 | examples/plnt/__init__.py |
140 | examples/plnt/__init__.pyc | |
141 | 131 | examples/plnt/database.py |
142 | examples/plnt/database.pyc | |
143 | 132 | examples/plnt/sync.py |
144 | 133 | examples/plnt/utils.py |
145 | examples/plnt/utils.pyc | |
146 | 134 | examples/plnt/views.py |
147 | examples/plnt/views.pyc | |
148 | 135 | examples/plnt/webapp.py |
149 | examples/plnt/webapp.pyc | |
150 | 136 | examples/plnt/shared/style.css |
151 | 137 | examples/plnt/templates/about.html |
152 | 138 | examples/plnt/templates/index.html |
153 | 139 | examples/plnt/templates/layout.html |
154 | 140 | examples/shorty/__init__.py |
155 | examples/shorty/__init__.pyc | |
156 | 141 | examples/shorty/application.py |
157 | examples/shorty/application.pyc | |
158 | 142 | examples/shorty/models.py |
159 | examples/shorty/models.pyc | |
160 | 143 | examples/shorty/utils.py |
161 | examples/shorty/utils.pyc | |
162 | 144 | examples/shorty/views.py |
163 | examples/shorty/views.pyc | |
164 | 145 | examples/shorty/static/style.css |
165 | 146 | examples/shorty/templates/display.html |
166 | 147 | examples/shorty/templates/layout.html |
168 | 149 | examples/shorty/templates/new.html |
169 | 150 | examples/shorty/templates/not_found.html |
170 | 151 | examples/simplewiki/__init__.py |
171 | examples/simplewiki/__init__.pyc | |
172 | 152 | examples/simplewiki/actions.py |
173 | examples/simplewiki/actions.pyc | |
174 | 153 | examples/simplewiki/application.py |
175 | examples/simplewiki/application.pyc | |
176 | 154 | examples/simplewiki/database.py |
177 | examples/simplewiki/database.pyc | |
178 | 155 | examples/simplewiki/specialpages.py |
179 | examples/simplewiki/specialpages.pyc | |
180 | 156 | examples/simplewiki/utils.py |
181 | examples/simplewiki/utils.pyc | |
182 | 157 | examples/simplewiki/shared/style.css |
183 | 158 | examples/simplewiki/templates/action_diff.html |
184 | 159 | examples/simplewiki/templates/action_edit.html |
195 | 170 | examples/webpylike/webpylike.py |
196 | 171 | tests/conftest.py |
197 | 172 | tests/test_http.py |
198 | tests/test_http.pyc | |
199 | 173 | tests/test_routing.py |
200 | tests/test_routing.pyc | |
201 | 174 | tests/test_templates.py |
202 | tests/test_templates.pyc | |
203 | 175 | tests/test_utils.py |
204 | tests/test_utils.pyc | |
205 | 176 | tests/test_wrappers.py |
206 | tests/test_wrappers.pyc | |
207 | 177 | tests/contrib/test_testtools.py |
208 | tests/contrib/test_testtools.pyc | |
209 | 178 | tests/res/test.txt |
210 | 179 | werkzeug/__init__.py |
211 | 180 | werkzeug/_internal.py |
0 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> | |
1 | <html> | |
2 | <head> | |
3 | <title>Unicode // Werkzeug Documentation</title> | |
4 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
5 | <link rel="stylesheet" type="text/css" href="style.css"> | |
6 | <link rel="stylesheet" type="text/css" href="pygments.css"> | |
7 | <link rel="stylesheet" type="text/css" media="print" href="print.css"> | |
8 | <link rel="shortcut icon" type="image/x-icon" href="favicon.ico"> | |
9 | </head> | |
10 | <body> | |
11 | <div class="page"> | |
12 | <div class="header"> | |
13 | <h1><a href="index.html"><span>Werkzeug</span></a></h1> | |
14 | <p><span>The Swiss Army Knife For Python Web Developers</span></p> | |
15 | </div> | |
16 | <div class="body"> | |
17 | <h2>Unicode</h2> | |
18 | ||
19 | <div class="toc"> | |
20 | <h4>Table of Contents</h4> | |
21 | <ul><li><a class="reference" href="#unicode-in-python" id="id1" name="id1">Unicode in Python</a></li> | |
22 | <li><a class="reference" href="#unicode-in-http" id="id2" name="id2">Unicode in HTTP</a></li> | |
23 | <li><a class="reference" href="#error-handling" id="id3" name="id3">Error Handling</a></li> | |
24 | <li><a class="reference" href="#request-and-response-objects" id="id4" name="id4">Request and Response Objects</a></li> | |
25 | </ul> | |
26 | </div> | |
27 | ||
28 | <p>Since early Python 2 days unicode was part of all default Python builds. It | |
29 | allows developers to write applications that deal with non-ASCII characters | |
30 | in a straightforward way. But working with unicode requires a basic knowledge | |
31 | about that matter, especially when working with libraries that do not support | |
32 | it.</p> | |
33 | <p>Werkzeug uses unicode internally everywhere text data is assumed, even if the | |
34 | HTTP standard is not unicode aware as it. Basically all incoming data is | |
35 | decoded from the charset specified (per default <cite>utf-8</cite>) so that you don’t | |
36 | operate on bytestrings any more. Outgoing unicode data is then encoded into | |
37 | the target charset again.</p> | |
38 | <div class="section"> | |
39 | <h3 id="unicode-in-python">Unicode in Python</h3> | |
40 | <p>In Python 2 there are two basic string types: <cite>str</cite> and <cite>unicode</cite>. <cite>str</cite> may | |
41 | carry encoded unicode data but it’s always represented in bytes whereas the | |
42 | <cite>unicode</cite> type does not contain bytes but charpoints. What does this mean? | |
43 | Imagine you have the German Umlaut <cite>ö</cite>. In ASCII you cannot represent that | |
44 | character, but in the <cite>latin-1</cite> and <cite>utf-8</cite> character sets you can represent | |
45 | it, but they look differently when encoded:</p> | |
46 | <div class="syntax"><pre><span class="gp">>>> </span><span class="s">u'ö'</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s">'latin1'</span><span class="p">)</span> | |
47 | <span class="go">'\xf6'</span> | |
48 | <span class="gp">>>> </span><span class="s">u'ö'</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">)</span> | |
49 | <span class="go">'\xc3\xb6'</span> | |
50 | </pre></div> | |
51 | <p>So an <cite>ö</cite> might look totally different depending on the encoding which makes | |
52 | it hard to work with it. The solution is using the <cite>unicode</cite> type (as we did | |
53 | above, note the <cite>u</cite> prefix before the string). The unicode type does not | |
54 | store the bytes for <cite>ö</cite> but the information, that this is a | |
55 | <tt class="docutils literal"><span class="pre">LATIN</span> <span class="pre">SMALL</span> <span class="pre">LETTER</span> <span class="pre">O</span> <span class="pre">WITH</span> <span class="pre">DIAERESIS</span></tt>.</p> | |
56 | <p>Doing <tt class="docutils literal"><span class="pre">len(u'ö')</span></tt> will always give us the expected “1” but <tt class="docutils literal"><span class="pre">len('ö')</span></tt> | |
57 | might give different results depending on the encoding of <tt class="docutils literal"><span class="pre">'ö'</span></tt>.</p> | |
58 | </div> | |
59 | <div class="section"> | |
60 | <h3 id="unicode-in-http">Unicode in HTTP</h3> | |
61 | <p>The problem with unicode is that HTTP does not know what unicode is. HTTP | |
62 | is limited to bytes but this is not a big problem as Werkzeug decodes and | |
63 | encodes for us automatically all incoming and outgoing data. Basically what | |
64 | this means is that data sent from the browser to the web application is per | |
65 | default decoded from an utf-8 bytestring into a <cite>unicode</cite> string. Data sent | |
66 | from the application back to the browser that is not yet a bytestring is then | |
67 | encoded back to utf-8.</p> | |
68 | <p>Usually this “just works” and we don’t have to worry about it, but there are | |
69 | situations where this behavior is problematic. For example the Python 2 IO | |
70 | layer is not unicode aware. This means that whenever you work with data from | |
71 | the file system you have to properly decode it. The correct way to load | |
72 | a text file from the file system looks like this:</p> | |
73 | <div class="syntax"><pre><span class="n">f</span> <span class="o">=</span> <span class="nb">file</span><span class="p">(</span><span class="s">'/path/to/the_file.txt'</span><span class="p">,</span> <span class="s">'r'</span><span class="p">)</span> | |
74 | <span class="k">try</span><span class="p">:</span> | |
75 | <span class="n">text</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">)</span> <span class="c"># assuming the file is utf-8 encoded</span> | |
76 | <span class="k">finally</span><span class="p">:</span> | |
77 | <span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> | |
78 | </pre></div> | |
79 | <p>There is also the codecs module which provides an open function that decodes | |
80 | automatically from the given encoding.</p> | |
81 | </div> | |
82 | <div class="section"> | |
83 | <h3 id="error-handling">Error Handling</h3> | |
84 | <p>With Werkzeug 0.3 onwards you can further control the way Werkzeug works with | |
85 | unicode. In the past Werkzeug ignored encoding errors silently on incoming | |
86 | data. This decision was made to avoid internal server errors if the user | |
87 | tampered with the submitted data. However there are situations where you | |
88 | want to abort with a <cite>400 BAD REQUEST</cite> instead of silently ignoring the error.</p> | |
89 | <p>All the functions that do internal decoding now accept an <cite>errors</cite> keyword | |
90 | argument that behaves like the <cite>errors</cite> parameter of the builtin string method | |
91 | <cite>decode</cite>. The following values are possible:</p> | |
92 | <dl> | |
93 | <dt><cite>ignore</cite></dt> | |
94 | <dd>This is the default behavior and tells the codec to ignore characters that | |
95 | it doesn’t understand silently.</dd> | |
96 | <dt><cite>replace</cite></dt> | |
97 | <dd>The codec will replace unknown characters with a replacement character | |
98 | (<cite>U+FFFD</cite> <tt class="docutils literal"><span class="pre">REPLACEMENT</span> <span class="pre">CHARACTER</span></tt>)</dd> | |
99 | <dt><cite>strict</cite></dt> | |
100 | <dd>Raise an exception if decoding fails.</dd> | |
101 | </dl> | |
102 | <p>Unlike the regular python decoding Werkzeug does not raise an | |
103 | <cite>UnicodeDecodeError</cite> if the decoding failed but an <cite>HTTPUnicodeError</cite> which | |
104 | is a direct subclass of <cite>UnicodeError</cite> and the <cite>BadRequest</cite> HTTP exception. | |
105 | The reason is that if this exception is not caught by the application but | |
106 | a catch-all for HTTP exceptions exists a default <cite>400 BAD REQUEST</cite> error | |
107 | page is displayed.</p> | |
108 | <p>There is additional error handling available which is a Werkzeug extension | |
109 | to the regular codec error handling which is called <cite>fallback</cite>. Often you | |
110 | want to use utf-8 but support latin1 as legacy encoding too if decoding | |
111 | failed. For this case you can use the <cite>fallback</cite> error handling. For | |
112 | example you can specify <tt class="docutils literal"><span class="pre">'fallback:iso-8859-15'</span></tt> to tell Werkzeug it should | |
113 | try with <cite>iso-8859-15</cite> if <cite>utf-8</cite> failed. If this decoding fails too (which | |
114 | should not happen for most legacy charsets such as <cite>iso-8859-15</cite>) the error | |
115 | is silently ignored as if the error handling was <cite>ignore</cite>.</p> | |
116 | <p>Further details are available as part of the API documentation of the concrete | |
117 | implementations of the functions or classes working with unicode.</p> | |
118 | </div> | |
119 | <div class="section"> | |
120 | <h3 id="request-and-response-objects">Request and Response Objects</h3> | |
121 | <p>As request and response objects usually are the central entities of Werkzeug | |
122 | powered applications you can change the default encoding Werkzeug operates on | |
123 | by subclassing these two classes. For example you can easily set the | |
124 | application to utf-7 and strict error handling:</p> | |
125 | <div class="syntax"><pre><span class="k">from</span> <span class="nn">werkzeug</span> <span class="k">import</span> <span class="n">BaseRequest</span><span class="p">,</span> <span class="n">BaseResponse</span> | |
126 | ||
127 | <span class="k">class</span> <span class="nc">Request</span><span class="p">(</span><span class="n">BaseRequest</span><span class="p">):</span> | |
128 | <span class="n">charset</span> <span class="o">=</span> <span class="s">'utf-7'</span> | |
129 | <span class="n">encoding_errors</span> <span class="o">=</span> <span class="s">'strict'</span> | |
130 | ||
131 | <span class="k">class</span> <span class="nc">Response</span><span class="p">(</span><span class="n">BaseResponse</span><span class="p">):</span> | |
132 | <span class="n">charset</span> <span class="o">=</span> <span class="s">'utf-7'</span> | |
133 | </pre></div> | |
134 | <p>Keep in mind that the error handling is only customizable for all decoding | |
135 | but not encoding. If Werkzeug encounters an encoding error it will raise a | |
136 | <cite>UnicodeEncodeError</cite>. It’s your responsibility to not create data that is | |
137 | not present in the target charset (a non issue with all unicode encodings | |
138 | such as utf-8).</p> | |
139 | </div> | |
140 | ||
141 | <div style="clear:both"></div> | |
142 | </div> | |
143 | <div class="footer"> | |
144 | Werkzeug is a <a href="http://pocoo.org/">Pocoo</a> project licensed under the | |
145 | BSD license. | |
146 | </div> | |
147 | </div> | |
148 | </body> | |
149 | </html>⏎ |
0 | # -*- coding: utf-8 -*- | |
1 | """ | |
2 | Object Based Dispatching | |
3 | ~~~~~~~~~~~~~~~~~~~~~~~~ | |
4 | ||
5 | Colubrid inspired object based dispatching. | |
6 | ||
7 | :copyright: Copyright 2008 by Armin Ronacher. | |
8 | :license: GNU GPL. | |
9 | """ | |
10 | import inspect | |
11 | from werkzeug import Request, Response, responder, redirect | |
12 | from werkzeug.exceptions import HTTPException, NotFound | |
13 | ||
14 | ||
15 | def fix_slash(request, is_container): | |
16 | path = request.path | |
17 | new_path = None | |
18 | ends_with_slash = path.endswith('/') | |
19 | if ends_with_slash and not is_container: | |
20 | new_path = path.rstrip('/') | |
21 | elif not ends_with_slash and is_container: | |
22 | new_path = path + '/' | |
23 | if new_path is not None: | |
24 | qs = request.environ.get('QUERY_STRING') | |
25 | if qs: | |
26 | new_path += '?' + qs | |
27 | return redirect(new_path) | |
28 | ||
29 | ||
30 | class Controller(object): | |
31 | ||
32 | def __init__(self, request): | |
33 | self.request = request | |
34 | ||
35 | ||
36 | class ObjectApplication(object): | |
37 | ||
38 | root = None | |
39 | ||
40 | def dispatch_request(self, request): | |
41 | handler = self.root | |
42 | container = None | |
43 | args = [] | |
44 | for part in request.path.strip('/').split('/'): | |
45 | if part.startswith('_'): | |
46 | raise NotFound() | |
47 | node = getattr(handler, part, None) | |
48 | if node is None: | |
49 | if part: | |
50 | args.append(part) | |
51 | else: | |
52 | handler = node | |
53 | ||
54 | if inspect.ismethod(handler): | |
55 | if handler.__name__ == 'index': | |
56 | container = False | |
57 | else: | |
58 | index = getattr(handler, 'index', None) | |
59 | if index is not None: | |
60 | if not hasattr(index, '_is_container'): | |
61 | container = True | |
62 | handler = index | |
63 | else: | |
64 | raise NotFound() | |
65 | ||
66 | if container is None and hasattr(handler, '_is_container'): | |
67 | container = handler._is_container | |
68 | ||
69 | handler_args, varargs, _, defaults = inspect.getargspec(handler) | |
70 | if defaults is None: | |
71 | defaults = 0 | |
72 | else: | |
73 | defaults = len(defaults) | |
74 | ||
75 | max_len = len(handler_args) - 1 | |
76 | min_len = max_len - defaults | |
77 | cur_len = len(args) | |
78 | if varargs: | |
79 | max_len = -1 | |
80 | ||
81 | # check if the number of arguments fits our handler | |
82 | if max_len < 0: | |
83 | if cur_len < min_len: | |
84 | raise NotFound() | |
85 | elif min_len <= cur_len <= max_len: | |
86 | if container is None: | |
87 | container = cur_len < max_len | |
88 | else: | |
89 | raise NotFound() | |
90 | ||
91 | # redirect if necessary | |
92 | response = fix_slash(request, container) | |
93 | if response is not None: | |
94 | return response | |
95 | ||
96 | # call handler | |
97 | return handler(handler.im_class(request), *args) | |
98 | ||
99 | @responder | |
100 | def __call__(self, environ, start_response): | |
101 | request = Request(environ) | |
102 | try: | |
103 | rv = self.dispatch_request(request) | |
104 | if isinstance(rv, basestring): | |
105 | rv = Response(rv, mimetype='text/html') | |
106 | return rv | |
107 | except HTTPException, e: | |
108 | return e | |
109 | ||
110 | ||
111 | class BlogController(Controller): | |
112 | ||
113 | def index(self): | |
114 | return 'Hello World' | |
115 | ||
116 | ||
117 | class AdminController(Controller): | |
118 | ||
119 | def hello(self, name='World'): | |
120 | times = self.request.args['times'] | |
121 | return 'Hello %s (%s times)!' % (name, times) | |
122 | ||
123 | ||
124 | application = ObjectApplication() | |
125 | application.root = BlogController | |
126 | application.root.admin = AdminController | |
127 | ||
128 | ||
129 | if __name__ == '__main__': | |
130 | from werkzeug import run_simple | |
131 | run_simple('localhost', 4000, application, use_reloader=True) |
70 | 70 | |
71 | 71 | setup( |
72 | 72 | name='Werkzeug', |
73 | version='0.3', | |
73 | version='0.3.1', | |
74 | 74 | url='http://werkzeug.pocoo.org/', |
75 | 75 | license='BSD', |
76 | 76 | author='Armin Ronacher', |
73 | 73 | try: |
74 | 74 | from hashlib import sha1 |
75 | 75 | except ImportError: |
76 | from sha import new as sha1 | |
77 | from binascii import Error as BinASCIIError | |
76 | import sha as sha1 | |
77 | from hmac import new as hmac | |
78 | 78 | from datetime import datetime |
79 | 79 | from time import time, mktime, gmtime |
80 | 80 | from random import Random |
109 | 109 | |
110 | 110 | class SecureCookie(ModificationTrackingDict): |
111 | 111 | """Represents a secure cookie. You can subclass this class and provide |
112 | an alternative hash method. The import thing is that the hash method | |
112 | an alternative mac method. The import thing is that the mac method | |
113 | 113 | is a function with a similar interface to the hashlib. Required |
114 | 114 | methods are update() and digest(). |
115 | 115 | """ |
116 | __slots__ = ModificationTrackingDict.__slots__ + ('secret_key', 'new') | |
117 | ||
116 | ||
117 | # the hash method to use. In python 2.5 and higher this is a callable | |
118 | # that returns a new hashlib object or a module with a new method that | |
119 | # creates such an object. In python 2.4 and earlier only the module | |
120 | # is supported. | |
118 | 121 | hash_method = sha1 |
119 | 122 | |
120 | 123 | def __init__(self, data=None, secret_key=None, new=True): |
133 | 136 | """True if the session should be saved.""" |
134 | 137 | return self.modified |
135 | 138 | should_save = property(should_save) |
136 | ||
137 | def new_salt(self, secret_key): | |
138 | """Return a new salt Return value must be 4 bytes long.""" | |
139 | return generate_key()[:4] | |
140 | 139 | |
141 | 140 | def serialize(self, expires=None): |
142 | 141 | """Serialize the secure cookie into a string. |
154 | 153 | expires = gmtime(expires) |
155 | 154 | self['_expires'] = int(mktime(expires)) |
156 | 155 | result = [] |
157 | salt = self.new_salt(self.secret_key) | |
158 | hash = self.hash_method(self.secret_key + salt) | |
156 | mac = hmac(self.secret_key, None, self.hash_method) | |
159 | 157 | for key, value in self.iteritems(): |
160 | 158 | result.append('%s=%s' % ( |
161 | 159 | url_quote_plus(key), |
162 | 160 | pickle_quote(value) |
163 | 161 | )) |
164 | hash.update('|' + result[-1]) | |
165 | return '%s%s?%s' % ( | |
166 | salt, | |
167 | hash.digest().encode('base64').strip(), | |
162 | mac.update('|' + result[-1]) | |
163 | return '%s?%s' % ( | |
164 | mac.digest().encode('base64').strip(), | |
168 | 165 | '&'.join(result) |
169 | 166 | ) |
170 | 167 | |
172 | 169 | """Load the secure cookie from a serialized string.""" |
173 | 170 | if isinstance(string, unicode): |
174 | 171 | string = string.encode('utf-8', 'ignore') |
175 | salt = string[:4] | |
176 | 172 | try: |
177 | base64_hash, data = string[4:].split('?', 1) | |
173 | base64_hash, data = string.split('?', 1) | |
178 | 174 | except (ValueError, IndexError): |
179 | 175 | items = () |
180 | 176 | else: |
181 | 177 | items = {} |
182 | hash = sha1(secret_key + salt) | |
178 | mac = hmac(secret_key, None, cls.hash_method) | |
183 | 179 | for item in data.split('&'): |
184 | hash.update('|' + item) | |
180 | mac.update('|' + item) | |
185 | 181 | if not '=' in item: |
186 | 182 | items = None |
187 | 183 | break |
188 | 184 | key, value = item.split('=', 1) |
189 | items[url_unquote_plus(key)] = value | |
190 | ||
191 | # no parsing error and the hash looks okay, we can now | |
185 | # try to make the key a string | |
186 | key = url_unquote_plus(key) | |
187 | try: | |
188 | key = str(key) | |
189 | except UnicodeError: | |
190 | pass | |
191 | items[key] = value | |
192 | ||
193 | # no parsing error and the mac looks okay, we can now | |
192 | 194 | # sercurely unpickle our cookie. |
193 | 195 | try: |
194 | 196 | client_hash = base64_hash.decode('base64') |
195 | 197 | except Exception: |
196 | 198 | items = client_hash = None |
197 | if items is not None and client_hash == hash.digest(): | |
199 | if items is not None and client_hash == mac.digest(): | |
198 | 200 | try: |
199 | 201 | for key, value in items.iteritems(): |
200 | 202 | items[key] = pickle_unquote(value) |