# HG changeset patch # User paulb # Date 1095373185 0 # Node ID aa4871518508963ad9d7278b21766f413ed5da2f # Parent 5d9003caabc6d398275aeb9b6f155c6954c74a78 [project @ 2004-09-16 22:19:45 by paulb] Fixed cookie handling to permit Unicode objects to be set in cookies, both as names and as values. This requires the usage of URL-encoded, UTF-8 text. diff -r 5d9003caabc6 -r aa4871518508 WebStack/BaseHTTPRequestHandler.py --- a/WebStack/BaseHTTPRequestHandler.py Thu Sep 16 22:18:33 2004 +0000 +++ b/WebStack/BaseHTTPRequestHandler.py Thu Sep 16 22:19:45 2004 +0000 @@ -5,12 +5,12 @@ """ import Generic -from Helpers.Request import MessageBodyStream, get_body_fields, get_storage_items +from Helpers.Request import MessageBodyStream, get_body_fields, get_storage_items, Cookie from Helpers.Response import ConvertingStream from Helpers.Auth import UserInfo from Helpers.Session import SessionStore from cgi import parse_qs, FieldStorage -import Cookie +from Cookie import SimpleCookie from StringIO import StringIO class Transaction(Generic.Transaction): @@ -34,12 +34,12 @@ self.response_code = 200 self.content = StringIO() self.headers_out = {} - self.cookies_out = Cookie.SimpleCookie() + self.cookies_out = SimpleCookie() self.user = None # Define the incoming cookies. - self.cookies_in = Cookie.SimpleCookie(self.get_headers().get("cookie")) + self.cookies_in = SimpleCookie(self.get_headers().get("cookie")) # Cached information. @@ -304,7 +304,7 @@ Returns a dictionary mapping cookie names to cookie objects. """ - return self.cookies_in + return self.process_cookies(self.cookies_in) def get_cookie(self, cookie_name): @@ -315,7 +315,11 @@ cookie exists. """ - return self.cookies_in.get(cookie_name) + cookie = self.cookies_in.get(self.encode_cookie_value(cookie_name)) + if cookie is not None: + return Cookie(cookie_name, self.decode_cookie_value(cookie.value)) + else: + return None # Response-related methods. @@ -395,7 +399,7 @@ # NOTE: If multiple cookies of the same name could be specified, this # NOTE: could need changing. - self.cookies_out[cookie.name] = cookie.value + self.set_cookie_value(cookie.name, cookie.value) def set_cookie_value(self, name, value, path=None, expires=None): @@ -407,7 +411,8 @@ time.time function, and indicates the expiry date/time of the cookie. """ - self.cookies_out[name] = value + name = self.encode_cookie_value(name) + self.cookies_out[name] = self.encode_cookie_value(value) if path is not None: self.cookies_out[name]["path"] = path if expires is not None: @@ -424,10 +429,11 @@ # has been sent the cookie or not. # NOTE: Magic discovered in Webware. - self.cookies_out[cookie_name] = "" - self.cookies_out[cookie_name]["path"] = "/" - self.cookies_out[cookie_name]["expires"] = 0 - self.cookies_out[cookie_name]["max-age"] = 0 + name = self.encode_cookie_value(cookie_name) + self.cookies_out[name] = "" + self.cookies_out[name]["path"] = "/" + self.cookies_out[name]["expires"] = 0 + self.cookies_out[name]["max-age"] = 0 # Session-related methods. diff -r 5d9003caabc6 -r aa4871518508 WebStack/CGI.py --- a/WebStack/CGI.py Thu Sep 16 22:18:33 2004 +0000 +++ b/WebStack/CGI.py Thu Sep 16 22:19:45 2004 +0000 @@ -6,13 +6,13 @@ import Generic import os, sys -from Helpers.Request import MessageBodyStream, get_body_fields, get_storage_items +from Helpers.Request import MessageBodyStream, get_body_fields, get_storage_items, Cookie from Helpers.Response import ConvertingStream from Helpers.Auth import UserInfo from Helpers.Session import SessionStore from Helpers import Environment from cgi import parse_qs, FieldStorage -import Cookie +from Cookie import SimpleCookie from StringIO import StringIO class Transaction(Generic.Transaction): @@ -39,12 +39,12 @@ self.response_code = 200 self.content = StringIO() self.headers_out = {} - self.cookies_out = Cookie.SimpleCookie() + self.cookies_out = SimpleCookie() self.user = None # Define the incoming cookies. - self.cookies_in = Cookie.SimpleCookie(self.env.get("HTTP_COOKIE")) + self.cookies_in = SimpleCookie(self.env.get("HTTP_COOKIE")) # Cached information. @@ -294,7 +294,7 @@ Returns a dictionary mapping cookie names to cookie objects. """ - return self.cookies_in + return self.process_cookies(self.cookies_in) def get_cookie(self, cookie_name): @@ -305,7 +305,11 @@ cookie exists. """ - return self.cookies_in.get(cookie_name) + cookie = self.cookies_in.get(self.encode_cookie_value(cookie_name)) + if cookie is not None: + return Cookie(cookie_name, self.decode_cookie_value(cookie.value)) + else: + return None # Response-related methods. @@ -385,7 +389,7 @@ # NOTE: If multiple cookies of the same name could be specified, this # NOTE: could need changing. - self.cookies_out[cookie.name] = cookie.value + self.set_cookie_value(cookie.name, cookie.value) def set_cookie_value(self, name, value, path=None, expires=None): @@ -397,7 +401,8 @@ time.time function, and indicates the expiry date/time of the cookie. """ - self.cookies_out[name] = value + name = self.encode_cookie_value(name) + self.cookies_out[name] = self.encode_cookie_value(value) if path is not None: self.cookies_out[name]["path"] = path if expires is not None: @@ -414,10 +419,11 @@ # has been sent the cookie or not. # NOTE: Magic discovered in Webware. - self.cookies_out[cookie_name] = "" - self.cookies_out[cookie_name]["path"] = "/" - self.cookies_out[cookie_name]["expires"] = 0 - self.cookies_out[cookie_name]["max-age"] = 0 + name = self.encode_cookie_value(cookie_name) + self.cookies_out[name] = "" + self.cookies_out[name]["path"] = "/" + self.cookies_out[name]["expires"] = 0 + self.cookies_out[name]["max-age"] = 0 # Session-related methods. diff -r 5d9003caabc6 -r aa4871518508 WebStack/Generic.py --- a/WebStack/Generic.py Thu Sep 16 22:18:33 2004 +0000 +++ b/WebStack/Generic.py Thu Sep 16 22:19:45 2004 +0000 @@ -15,6 +15,9 @@ directors. """ +import urllib +from WebStack.Helpers.Request import Cookie + class HeaderValue: "A container for header information." @@ -140,6 +143,46 @@ return value.encode("US-ASCII") + def encode_cookie_value(self, value): + + """ + Encode the given cookie 'value'. This ensures the usage of US-ASCII + through the encoding of Unicode objects as URL-encoded UTF-8 text. + """ + + return urllib.quote(value.encode("UTF-8")).encode("US-ASCII") + + def decode_cookie_value(self, value): + + """ + Decode the given cookie 'value'. + """ + + return unicode(urllib.unquote(value), "UTF-8") + + def process_cookies(self, cookie_dict, using_strings=0): + + """ + Process the given 'cookie_dict', returning a dictionary mapping cookie names + to cookie objects where the names and values have been decoded from the form + used in the cookies retrieved from the request. + + The optional 'using_strings', if set to 1, treats the 'cookie_dict' as a + mapping of cookie names to values. + """ + + cookies = {} + for name in cookie_dict.keys(): + if using_strings: + value = cookie_dict[name] + else: + cookie = cookie_dict[name] + value = cookie.value + cookie_name = self.decode_cookie_value(name) + cookie_value = self.decode_cookie_value(value) + cookies[cookie_name] = Cookie(cookie_name, cookie_value) + return cookies + def parse_content_preferences(self, accept_preference): """ diff -r 5d9003caabc6 -r aa4871518508 WebStack/JavaServlet.py --- a/WebStack/JavaServlet.py Thu Sep 16 22:18:33 2004 +0000 +++ b/WebStack/JavaServlet.py Thu Sep 16 22:19:45 2004 +0000 @@ -74,8 +74,8 @@ self.cookies_in = {} for cookie in self.request.getCookies() or []: - cookie_name = cookie.getName() - self.cookies_in[cookie_name] = Cookie(cookie_name, cookie.getValue()) + cookie_name = self.decode_cookie_value(cookie.getName()) + self.cookies_in[cookie_name] = Cookie(cookie_name, self.decode_cookie_value(cookie.getValue())) # Cached information. @@ -416,8 +416,7 @@ Stores the given 'cookie' object in the response. """ - new_cookie = javax.servlet.http.Cookie(cookie.name, cookie.value) - self.response.addCookie(new_cookie) + self.set_cookie_value(cookie.name, cookie.value) def set_cookie_value(self, name, value, path=None, expires=None): @@ -429,7 +428,8 @@ time.time function, and indicates the expiry date/time of the cookie. """ - cookie = javax.servlet.http.Cookie(name, value) + cookie = javax.servlet.http.Cookie(self.encode_cookie_value(name), + self.encode_cookie_value(value)) if path is not None: cookie.setPath(path) @@ -448,7 +448,7 @@ # has been sent the cookie or not. # NOTE: Magic discovered in Webware. - cookie = javax.servlet.http.Cookie(cookie_name, "") + cookie = javax.servlet.http.Cookie(self.encode_cookie_value(cookie_name), "") cookie.setPath("/") cookie.setMaxAge(0) self.response.addCookie(cookie) diff -r 5d9003caabc6 -r aa4871518508 WebStack/ModPython.py --- a/WebStack/ModPython.py Thu Sep 16 22:18:33 2004 +0000 +++ b/WebStack/ModPython.py Thu Sep 16 22:19:45 2004 +0000 @@ -5,7 +5,7 @@ """ import Generic -from Helpers.Request import get_body_field, filter_fields +from Helpers.Request import get_body_field, filter_fields, Cookie from Helpers.Response import ConvertingStream from mod_python.util import parse_qs, FieldStorage from mod_python import apache @@ -14,10 +14,11 @@ # The alternative session support requires cookie support of some kind. try: - from mod_python import Cookie + from mod_python.Cookie import get_cookies, add_cookie, Cookie as SimpleCookie + have_cookies = 1 except ImportError: from Cookie import SimpleCookie - Cookie = None + have_cookies = 0 try: from mod_python import Session except ImportError: @@ -42,7 +43,7 @@ # Support non-framework cookies. - if Cookie is None: + if not have_cookies: # Define the incoming cookies. @@ -273,10 +274,11 @@ NOTE: support for enhanced cookies in mod_python. """ - if Cookie: - return Cookie.get_cookies(self.trans) + if have_cookies: + found_cookies = get_cookies(self.trans) else: - return self.cookies_in + found_cookies = self.cookies_in + return self.process_cookies(found_cookies) def get_cookie(self, cookie_name): @@ -287,7 +289,11 @@ cookie exists. """ - return self.get_cookies().get(cookie_name) + cookie = self.get_cookies().get(self.encode_cookie_value(cookie_name)) + if cookie is not None: + return Cookie(cookie_name, self.decode_cookie_value(cookie.value)) + else: + return None # Response-related methods. @@ -360,15 +366,10 @@ Stores the given 'cookie' object in the response. """ - if Cookie: - Cookie.add_cookie(self.trans, cookie) - else: - # NOTE: If multiple cookies of the same name could be specified, this - # NOTE: could need changing. + # NOTE: If multiple cookies of the same name could be specified, this + # NOTE: could need changing. - cookie_out = SimpleCookie() - cookie_out[cookie.name] = cookie.value - self._write_cookie(cookie_out) + self.set_cookie_value(cookie.name, cookie.value) def set_cookie_value(self, name, value, path=None, expires=None): @@ -380,18 +381,18 @@ time.time function, and indicates the expiry date/time of the cookie. """ - # NOTE: We just hope that Cookie converts Unicode arguments to US-ASCII. + name = self.encode_cookie_value(name) - if Cookie: - cookie = Cookie.Cookie(name, value) + if have_cookies: + cookie = SimpleCookie(name, self.encode_cookie_value(value)) if expires is not None: cookie.expires = expires if path is not None: cookie.path = path - Cookie.add_cookie(self.trans, cookie) + add_cookie(self.trans, cookie) else: cookie_out = SimpleCookie() - cookie_out[name] = value + cookie_out[name] = self.encode_cookie_value(value) if path is not None: cookie_out[name]["path"] = path if expires is not None: @@ -409,18 +410,20 @@ # has been sent the cookie or not. # NOTE: Magic discovered in Webware. - if Cookie: - cookie = Cookie.Cookie(cookie_name, "") + name = self.encode_cookie_value(cookie_name) + + if have_cookies: + cookie = SimpleCookie(name, "") cookie.path = "/" cookie.expires = 0 cookie.max_age = 0 - Cookie.add_cookie(self.trans, cookie) + add_cookie(self.trans, cookie) else: cookie_out = SimpleCookie() - cookie_out[cookie_name] = "" - cookie_out[cookie_name]["path"] = "/" - cookie_out[cookie_name]["expires"] = 0 - cookie_out[cookie_name]["max-age"] = 0 + cookie_out[name] = "" + cookie_out[name]["path"] = "/" + cookie_out[name]["expires"] = 0 + cookie_out[name]["max-age"] = 0 self._write_cookie(cookie_out) def _write_cookie(self, cookie): diff -r 5d9003caabc6 -r aa4871518508 WebStack/Twisted.py --- a/WebStack/Twisted.py Thu Sep 16 22:18:33 2004 +0000 +++ b/WebStack/Twisted.py Thu Sep 16 22:19:45 2004 +0000 @@ -264,10 +264,7 @@ NOTE: introduced. """ - cookies = {} - for name, value in self.trans.received_cookies.items(): - cookies[name] = Cookie(name, value) - return cookies + return self.process_cookies(self.trans.received_cookies, using_strings=1) def get_cookie(self, cookie_name): @@ -280,7 +277,11 @@ NOTE: introduced. """ - return Cookie(cookie_name, self.trans.getCookie(cookie_name)) + value = self.trans.getCookie(self.encode_cookie_value(cookie_name)) + if value is not None: + return Cookie(cookie_name, self.decode_cookie_value(value)) + else: + return None # Response-related methods. @@ -355,7 +356,7 @@ Stores the given 'cookie' object in the response. """ - self.trans.addCookie(cookie.name, cookie.value, expires=cookie.expires, path=cookie.path) + self.set_cookie_value(cookie.name, cookie.value, path=cookie.path, expires=cookie.expires) def set_cookie_value(self, name, value, path=None, expires=None): @@ -367,8 +368,8 @@ time.time function, and indicates the expiry date/time of the cookie. """ - self.trans.addCookie(self.format_header_value(name), - self.format_header_value(value), expires=expires, path=path) + self.trans.addCookie(self.encode_cookie_value(name), + self.encode_cookie_value(value), expires=expires, path=path) def delete_cookie(self, cookie_name): @@ -381,7 +382,7 @@ # has been sent the cookie or not. # NOTE: Magic discovered in Webware. - self.trans.addCookie(cookie_name, "", expires=0, path="/", max_age=0) + self.trans.addCookie(self.encode_cookie_value(cookie_name), "", expires=0, path="/", max_age=0) # Session-related methods. diff -r 5d9003caabc6 -r aa4871518508 WebStack/Webware.py --- a/WebStack/Webware.py Thu Sep 16 22:18:33 2004 +0000 +++ b/WebStack/Webware.py Thu Sep 16 22:19:45 2004 +0000 @@ -254,10 +254,7 @@ Returns a dictionary mapping cookie names to cookie objects. """ - cookies = {} - for name, value in self.trans.request().cookies().items(): - cookies[name] = Cookie(name, value) - return cookies + return self.process_cookies(self.trans.request().cookies(), using_strings=1) def get_cookie(self, cookie_name): @@ -269,7 +266,8 @@ """ try: - return Cookie(cookie_name, self.trans.request().cookie(cookie_name)) + value = self.trans.request().cookie(self.encode_cookie_value(cookie_name)) + return Cookie(cookie_name, self.decode_cookie_value(value)) except KeyError: return None @@ -353,7 +351,8 @@ Stores the given 'cookie' object in the response. """ - self.trans.response().addCookie(cookie) + self.set_cookie_value(cookie.name, cookie.value) + #self.trans.response().addCookie(cookie) def set_cookie_value(self, name, value, path=None, expires=None): @@ -365,7 +364,8 @@ time.time function, and indicates the expiry date/time of the cookie. """ - self.trans.response().setCookie(name, value, path, expires) + self.trans.response().setCookie(self.encode_cookie_value(name), + self.encode_cookie_value(value), path, expires) def delete_cookie(self, cookie_name): @@ -374,7 +374,7 @@ 'cookie_name' be deleted/discarded by the client. """ - self.trans.response().delCookie(cookie_name) + self.trans.response().delCookie(self.encode_cookie_value(cookie_name)) # Session-related methods. diff -r 5d9003caabc6 -r aa4871518508 WebStack/Zope.py --- a/WebStack/Zope.py Thu Sep 16 22:18:33 2004 +0000 +++ b/WebStack/Zope.py Thu Sep 16 22:19:45 2004 +0000 @@ -255,10 +255,7 @@ Returns a dictionary mapping cookie names to cookie objects. """ - cookies = {} - for name, value in self.request.cookies.items(): - cookies[name] = Cookie(name, value) - return cookies + return self.process_cookies(self.request.cookies, using_strings=1) def get_cookie(self, cookie_name): @@ -269,7 +266,11 @@ cookie exists. """ - return Cookie(cookie_name, self.request.cookies.get(cookie_name)) + value = self.request.cookies.get(self.encode_cookie_value(cookie_name)) + if value is not None: + return Cookie(cookie_name, value) + else: + return None # Response-related methods. @@ -352,7 +353,7 @@ time.time function, and indicates the expiry date/time of the cookie. """ - self.response.setCookie(name, value) + self.response.setCookie(self.encode_cookie_value(name), self.encode_cookie_value(value)) def delete_cookie(self, cookie_name): @@ -361,7 +362,7 @@ 'cookie_name' be deleted/discarded by the client. """ - self.response.expireCookie(cookie_name) + self.response.expireCookie(self.encode_cookie_value(cookie_name)) # Session-related methods.