diff options
| author | bwarsaw | 2001-10-01 16:47:40 +0000 |
|---|---|---|
| committer | bwarsaw | 2001-10-01 16:47:40 +0000 |
| commit | 0f97289b826664b69de70cf83d025073cac23d45 (patch) | |
| tree | 2e2807616196f52a508e5f5eee77ab52322e9d46 /misc/Cookie.py | |
| parent | c52eaaecb7bdc378a831e81e3ebff57ed7236379 (diff) | |
| download | mailman-0f97289b826664b69de70cf83d025073cac23d45.tar.gz mailman-0f97289b826664b69de70cf83d025073cac23d45.tar.zst mailman-0f97289b826664b69de70cf83d025073cac23d45.zip | |
Diffstat (limited to 'misc/Cookie.py')
| -rw-r--r-- | misc/Cookie.py | 583 |
1 files changed, 0 insertions, 583 deletions
diff --git a/misc/Cookie.py b/misc/Cookie.py deleted file mode 100644 index 23af3e2f7..000000000 --- a/misc/Cookie.py +++ /dev/null @@ -1,583 +0,0 @@ -#!/usr/bin/env python - -""" -#### -# Copyright (C) 1998 GTE Internetworking -# Author: Timothy O'Malley <timo@bbn.com> -# Hacked: Barry Warsaw <barry@zope.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Library General Public -# License as published by the Free Software Foundation, version 2. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Library General Public License for more details. -# -# You can retrieve a copy of the GNU Library General Public License -# from http://www.gnu.org/. For a copy via US Mail, write to the -# Free Software Foundation, Inc. -# 59 Temple Place - Suite 330, -# Boston, MA 02111-1307 -# USA -# -# GTE Internetworking can be reached electronically via the URL -# http://www.bbn.com or via US Mail at -# -# GTE Internetworking -# 5525 N. MacArthur Boulevard -# Suite 320 -# Irving, Texas 75038 -# USA -#### - -# Cookie.py is an update for the old nscookie.py module. -# Under the old module, it was not possible to set attributes, -# such as "secure" or "Max-Age" on key,value granularity. This -# shortcoming has been addressed in Cookie.py but has come at -# the cost of a slightly changed interface. Cookie.py also -# requires Python-1.5, for the re and cPickle modules. -# -# The original idea to treat Cookies as a dictionary came from -# Dave Mitchel (davem@magnet.com) in 1995, when he released the -# first version of nscookie.py. - -Here's a sample session to show how to use this module. -At the moment, this is the only documentation. - -Importing is easy.. - - >>> import Cookie - -Most of the time you start by creating a cookie. The __init__ -routine can take several arguments, but that isn't covered here. - - >>> C = Cookie.Cookie() - -Now, you can add values to the Cookie just as is if it were a -dictionary. - - >>> C["joe"] = "a cookie" - >>> C - Set-Cookie: joe="a cookie"; - -Notice that the printable representation of a Cookie is the -appropriate format for a Set-Cookie: header. This is the -default behavior. You can change the header by using the -the .output() function - - >>> C.output("Cookie:") - 'Cookie: joe="a cookie";' - - -The .load() method of a Cookie extracts cookies from a string. In -a CGI script, you would use this method to extract the cookies -from the HTTP_COOKIE environment variable. - - >>> C.load("mary=hadalittlelamb;") - >>> C - Set-Cookie: mary=hadalittlelamb; - Set-Cookie: joe="a cookie"; - -Each element of the Cookie also supports all of the RFC 2109 -Cookie attributes. Here's an example which sets the Path -attribute. - - >>> C["joe"]["path"] = "/home/joe" - >>> C - Set-Cookie: mary=hadalittlelamb; - Set-Cookie: joe="a cookie"; Path=/home/joe; - -Before I forget, the .load() method is pretty smart about -identifying a cookie. Escaped quotation marks and nested -semicolons do not confuse it. - - >>> C.load('lobotomy="joe=wolf; lobotomy=\\"nested quote\\"; mark=\\012;";') - >>> C - Set-Cookie: mary=hadalittlelamb; - Set-Cookie: joe="a cookie"; Path=/home/joe; - Set-Cookie: lobotomy="joe=wolf; lobotomy=\"nested quote\"; mark=\012;"; - - -Each dictionary element has a 'value' attribute, which gives you -back the value associated with the key. - - >>> C["joe"].value - 'a cookie' - >>> C["lobotomy"].value - 'joe=wolf; lobotomy="nested quote"; mark=\012;' - -If you set a cookie to a non-string object, that object is -automatically pickled (using cPickle or pickle) in the -Set-Cookie: header. - - >>> C["int"] = 7 - >>> C - Set-Cookie: lobotomy="joe=wolf; lobotomy=\"nested quote\"; mark=\012;"; - Set-Cookie: joe="a cookie"; Path=/home/joe; - Set-Cookie: mary=hadalittlelamb; - Set-Cookie: int="I7\012."; - -If the .load() method finds a pickled object in the string, then -it automatically unpickles it. The 'value' attribute gives you back -the true value, not the encoded representation. - - - >>> C.load('anotherint="I45\\012.";') - - >>> C["anotherint"].value - 45 - >>> C["int"].value - 7 - - >>> C - Set-Cookie: lobotomy="joe=wolf; lobotomy=\"nested quote\"; mark=\012;"; - Set-Cookie: joe="a cookie"; Path=/home/joe; - Set-Cookie: mary=hadalittlelamb; - Set-Cookie: anotherint="I45\012."; - Set-Cookie: int="I7\012."; - -Finally, the encoding/decoding behavior is controllable by -two attributes of the Cookie: - - net_setfunc() Takes in an encoded string and returns a value - user_setfunc() Takes in a value and returns the encoded string - -By default, these functions are defined in the Cookie module, but -you should feel free to override them. - - >>> C.net_setfunc - <function _debabelize at c1558> - >>> C.user_setfunc - <function _babelize at c1530> - -Finis. -""" - -# -# Import our required modules -# -import string, sys -from UserDict import UserDict -try: - from cPickle import dumps, loads -except ImportError: - from pickle import dumps, loads -try: - import re -except ImportError: - raise ImportError, "Cookie.py requires 're' from Python 1.5 or later" - -# -# Define an exception visible to External modules -# -class CookieError(Exception): - pass - - -# These quoting routines conform to the RFC2109 specification, which in -# turn references the character definitions from RFC2068. They provide -# a two-way quoting algorithm. Any non-text character is translated -# into a 4 character sequence: a forward-slash followed by the -# three-digit octal equivalent of the character. Any '\' or '"' is -# quoted with a preceeding '\' slash. -# -# These are taken from RFC2068 and RFC2109. -# _LegalChars is the list of chars which don't require "'s -# _SpecialChars require the cookie to be double-quoted -# _Translator hash-table for fast quoting -# -_LegalChars = string.letters + string.digits + "!#$%&'*+-.^_`|~" -_SpecialChars = string.translate(string._idmap, string._idmap, _LegalChars) -_Translator = { - '\000' : '\\000', '\001' : '\\001', '\002' : '\\002', '\003' : '\\003', - '\004' : '\\004', '\005' : '\\005', '\006' : '\\006', '\007' : '\\007', - '\010' : '\\010', '\011' : '\\011', '\012' : '\\012', '\013' : '\\013', - '\014' : '\\014', '\015' : '\\015', '\016' : '\\016', '\017' : '\\017', - '\020' : '\\020', '\021' : '\\021', '\022' : '\\022', '\023' : '\\023', - '\024' : '\\024', '\025' : '\\025', '\026' : '\\026', '\027' : '\\027', - '\030' : '\\030', '\031' : '\\031', '\032' : '\\032', '\033' : '\\033', - '\034' : '\\034', '\035' : '\\035', '\036' : '\\036', '\037' : '\\037', - ' ' : ' ', '!' : '!', '"' : '\\"', '#' : '#', - '$' : '$', '%' : '%', '&' : '&', "'" : "'", - '(' : '(', ')' : ')', '*' : '*', '+' : '+', - ',' : ',', '-' : '-', '.' : '.', '/' : '/', - '0' : '0', '1' : '1', '2' : '2', '3' : '3', - '4' : '4', '5' : '5', '6' : '6', '7' : '7', - '8' : '8', '9' : '9', ':' : ':', ';' : ';', - '<' : '<', ':' : ':', '>' : '>', '?' : '?', '=':'=', - '@' : '@', 'A' : 'A', 'B' : 'B', 'C' : 'C', - 'D' : 'D', 'E' : 'E', 'F' : 'F', 'G' : 'G', - 'H' : 'H', 'I' : 'I', 'J' : 'J', 'K' : 'K', - 'L' : 'L', 'M' : 'M', 'N' : 'N', 'O' : 'O', - 'P' : 'P', 'Q' : 'Q', 'R' : 'R', 'S' : 'S', - 'T' : 'T', 'U' : 'U', 'V' : 'V', 'W' : 'W', - 'X' : 'X', 'Y' : 'Y', 'Z' : 'Z', '[' : '[', - '\\' : '\\\\', ']' : ']', '^' : '^', '_' : '_', - '`' : '`', 'a' : 'a', 'b' : 'b', 'c' : 'c', - 'd' : 'd', 'e' : 'e', 'f' : 'f', 'g' : 'g', - 'h' : 'h', 'i' : 'i', 'j' : 'j', 'k' : 'k', - 'l' : 'l', 'm' : 'm', 'n' : 'n', 'o' : 'o', - 'p' : 'p', 'q' : 'q', 'r' : 'r', 's' : 's', - 't' : 't', 'u' : 'u', 'v' : 'v', 'w' : 'w', - 'x' : 'x', 'y' : 'y', 'z' : 'z', '{' : '{', - '|' : '|', '}' : '}', '~' : '~', '\177' : '\\177', - '\200' : '\\200', '\201' : '\\201', '\202' : '\\202', '\203' : '\\203', - '\204' : '\\204', '\205' : '\\205', '\206' : '\\206', '\207' : '\\207', - '\210' : '\\210', '\211' : '\\211', '\212' : '\\212', '\213' : '\\213', - '\214' : '\\214', '\215' : '\\215', '\216' : '\\216', '\217' : '\\217', - '\220' : '\\220', '\221' : '\\221', '\222' : '\\222', '\223' : '\\223', - '\224' : '\\224', '\225' : '\\225', '\226' : '\\226', '\227' : '\\227', - '\230' : '\\230', '\231' : '\\231', '\232' : '\\232', '\233' : '\\233', - '\234' : '\\234', '\235' : '\\235', '\236' : '\\236', '\237' : '\\237', - '\240' : '\\240', '\241' : '\\241', '\242' : '\\242', '\243' : '\\243', - '\244' : '\\244', '\245' : '\\245', '\246' : '\\246', '\247' : '\\247', - '\250' : '\\250', '\251' : '\\251', '\252' : '\\252', '\253' : '\\253', - '\254' : '\\254', '\255' : '\\255', '\256' : '\\256', '\257' : '\\257', - '\260' : '\\260', '\261' : '\\261', '\262' : '\\262', '\263' : '\\263', - '\264' : '\\264', '\265' : '\\265', '\266' : '\\266', '\267' : '\\267', - '\270' : '\\270', '\271' : '\\271', '\272' : '\\272', '\273' : '\\273', - '\274' : '\\274', '\275' : '\\275', '\276' : '\\276', '\277' : '\\277', - '\300' : '\\300', '\301' : '\\301', '\302' : '\\302', '\303' : '\\303', - '\304' : '\\304', '\305' : '\\305', '\306' : '\\306', '\307' : '\\307', - '\310' : '\\310', '\311' : '\\311', '\312' : '\\312', '\313' : '\\313', - '\314' : '\\314', '\315' : '\\315', '\316' : '\\316', '\317' : '\\317', - '\320' : '\\320', '\321' : '\\321', '\322' : '\\322', '\323' : '\\323', - '\324' : '\\324', '\325' : '\\325', '\326' : '\\326', '\327' : '\\327', - '\330' : '\\330', '\331' : '\\331', '\332' : '\\332', '\333' : '\\333', - '\334' : '\\334', '\335' : '\\335', '\336' : '\\336', '\337' : '\\337', - '\340' : '\\340', '\341' : '\\341', '\342' : '\\342', '\343' : '\\343', - '\344' : '\\344', '\345' : '\\345', '\346' : '\\346', '\347' : '\\347', - '\350' : '\\350', '\351' : '\\351', '\352' : '\\352', '\353' : '\\353', - '\354' : '\\354', '\355' : '\\355', '\356' : '\\356', '\357' : '\\357', - '\360' : '\\360', '\361' : '\\361', '\362' : '\\362', '\363' : '\\363', - '\364' : '\\364', '\365' : '\\365', '\366' : '\\366', '\367' : '\\367', - '\370' : '\\370', '\371' : '\\371', '\372' : '\\372', '\373' : '\\373', - '\374' : '\\374', '\375' : '\\375', '\376' : '\\376', '\377' : '\\377' - } - -def _translate(c, table=_Translator): - return table[c] - -def _quote(str, join=string.join): - # First check for common (and simple) case. - # - for C in _SpecialChars: - if C in str: - break - else: - return str - - # Ok, down to work. - # It's a shame we can't use _Translator.__getitem__ - # but Python code doesn't have access to that function. - # - return '"' + join( map(_translate, str), "") + '"' -# end _quote - - -_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]") -_QuotePatt = re.compile(r"[\\].") - -def _unquote(str): - # If there aren't any doublequotes, - # then there can't be any special characters. See RFC 2109. - if len(str) < 2: - return str - if str[0] != '"' or str[-1] != '"': - return str - - # We have to assume that we must decode this string. - # Down to work. - - # Remove the "s - str = str[1:-1] - - # Check for special sequences. Examples: - # \012 --> \n - # \" --> " - # - i = 0 - n = len(str) - res = [] - while 0 <= i < n: - Omatch = _OctalPatt.search(str, i) - Qmatch = _QuotePatt.search(str, i) - if not Omatch and not Qmatch: # Neither matched - res.append(str[i:]) - break - # else: - j = k = -1 - if Omatch: j = Omatch.start(0) - if Qmatch: k = Qmatch.start(0) - if Qmatch and ( not Omatch or k < j ): # QuotePatt matched - res.append(str[i:k]) - res.append(str[k+1]) - i = k+2 - else: # OctalPatt matched - res.append(str[i:j]) - res.append( chr( string.atoi(str[j+1:j+4], 8) ) ) - i = j+4 - return string.join(res, "") -# end _unquote - - -# The _babelize() and _debabelize() functions allow arbitrary objects -# to be used as cookie values. Large cookies may add significant -# overhead, because the client retransmits them on each visit. -# -# Note: HTTP imposes a 2k limit on the size of cookie. I don't check -# for this limit, so be careful!!! -# - -def _babelize(val, dumps=dumps): - if type(val) == type(""): - return _quote(val) - else: - return _quote( dumps(val) ) - - -def _debabelize(val, loads=loads): - str = _unquote(val) - try: - return loads(str) - except: - return str - - -# The _getdate() routine is used to set the expiration time in -# the cookie's HTTP header. By default, _getdate() returns the -# current time in the appropriate "expires" format for a -# Set-Cookie header. The one optional argument is an offset from -# now, in seconds. For example, an offset of -3600 means "one hour ago". -# The offset may be a floating point number. -# - -_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] - -_monthname = [None, - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - -def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname): - from time import gmtime, time - now = time() - year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future) - return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \ - (weekdayname[wd], day, monthname[month], year, hh, mm, ss) - - -# -# A class to hold ONE key,value pair. -# In a cookie, each such pair may have several attributes. -# so this class is used to keep the attributes associated -# with the appropriate key,value pair. -# This class also includes a coded_value attribute, which -# is used to hold the network representation of the -# value. This is most useful when Python objects are -# pickled for network transit. -# - -class Morsel(UserDict): - # RFC 2109 lists these attributes as reserved: - # path comment domain - # max-age secure version - # - # For historical reasons, these attributes are also reserved: - # expires - # - # This dictionary provides a mapping from the lowercase - # variant on the left to the appropriate Set-Cookie - # format on the right. - __reserved = { "expires" : "expires", - "path" : "Path", - "comment" : "Comment", - "domain" : "Domain", - "max-age" : "Max-Age", - "secure" : "secure", - "version" : "Version", - } - __reserved_keys = __reserved.keys() - - def __init__(self): - # Set defaults - self.key = self.value = self.coded_value = None - UserDict.__init__(self) - - # Set default attributes - for K in self.__reserved_keys: - UserDict.__setitem__(self, K, "") - # end __init__ - - def __setitem__(self, K, V): - K = string.lower(K) - if not K in self.__reserved_keys: - raise CookieError("Invalid Attribute %s" % K) - UserDict.__setitem__(self, K, V) - # end __setitem__ - - def set(self, key, val, coded_val, strict=1): - if strict and string.lower(key) in self.__reserved_keys: - raise CookieError("Attempt to set a reserved key: %s" % key) - self.key = key - self.value = val - self.coded_value = coded_val - # end set - - def output(self, header = "Set-Cookie:"): - return "%s %s" % ( header, self.OutputString() ) - - __repr__ = output - - def js_output(self): - # Print javascript - return """ - <SCRIPT LANGUAGE="JavaScript"> - <!-- begin hiding - document.cookie = \"%s\" - // end hiding --> - </script> - """ % ( self.OutputString(), ) - # end js_output() - - def OutputString(self): - # Build up our result - # - result = [] - RA = result.append - - # First, the key=value pair - RA("%s=%s;" % (self.key, self.coded_value)) - - # Now add any defined attributes - for K,V in self.items(): - if V == '': continue - if K == "expires" and type(V) == type(1): - RA("%s=%s;" % (self.__reserved[K], _getdate(V))) - elif K == "max-age" and type(V) == type(1): - RA("%s=%d;" % (self.__reserved[K], V)) - elif K == "secure": - RA("%s;" % self.__reserved[K]) - else: - RA("%s=%s;" % (self.__reserved[K], V)) - - # Return the result - return string.join(result, " ") - # end OutputString -# end Morsel class - - - -# -# Pattern for finding cookie -# -# This used to be strict parsing based on the RFC2109 and RFC2068 -# specifications. I have since discovered that MSIE 3.0x doesn't -# follow the character rules outlined in those specs. As a -# result, the parsing rules here are less strict. -# - -_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{]+" -_CookiePattern = re.compile( - r"(?x)" # This is a Verbose pattern - r"(?P<key>" # Start of group 'key' - ""+ _LegalCharsPatt +"" # Any word - r")" # End of group 'key' - r"\s*=\s*" # Equal Sign - r"(?P<val>" # Start of group 'val' - r'"(?:[^\\"]|\\.)*"' # Any doublequoted string - r"|" # or - ""+ _LegalCharsPatt +"" # Any word - r")" # End of group 'val' - r"\s*;?" # Probably ending in a semi-colon - ) - - -# At long last, here is the cookie class. -# Using this class is almost just like using a dictionary. -# See this module's docstring for example usage. -# -class Cookie(UserDict): - # A container class for a set of Morsels - # - - def __init__(self, input=None, - net_setfunc=_debabelize, user_setfunc=_babelize): - self.net_setfunc = net_setfunc # when set from network - self.user_setfunc = user_setfunc # when set by user - UserDict.__init__(self) - if input: self.load(input) - # end __init__ - - def __setitem__(self, key, value): - """Dictionary style assignment.""" - M = self.get(key, Morsel()) - M.set(key, value, apply(self.user_setfunc, (value,))) - UserDict.__setitem__(self, key, M) - # end __setitem__ - - def output(self, header="Set-Cookie:"): - """Return a string suitable for HTTP.""" - result = [] - for K,V in self.items(): - result.append( V.output(header) ) - return string.join(result,"\n") - # end output - - __repr__ = output - - def js_output(self): - """Return a string suitable for JavaScript.""" - result = [] - for K,V in self.items(): - result.append( V.js_output() ) - return string.join(result, "") - # end js_output - - def load(self, rawdata): - """Load cookies from a string (presumably HTTP_COOKIE) or - from a dictionary. Loading cookies from a dictionary 'd' - is equivalent to calling: - map(Cookie.__setitem__, d.keys(), d.values()) - Unfortunately, this does NOT allow merging of two Cookie - dictionaries! - """ - if type(rawdata) == type(""): - self.__ParseString(rawdata) - else: - for K,V in rawdata.items(): - self[K] = V - return - # end get() - - def __ParseString(self, str, patt=_CookiePattern): - i = 0 # Our starting point - n = len(str) # Length of string - M = None # Current morsel - - while 0 <= i < n: - # Start looking for a cookie - match = patt.search(str, i) - if not match: break # No more cookies - K,V = match.group("key"), match.group("val") - i = match.end(0) - - # Parse the key, value in case it's metainfo - if K[0] == "$" and M: - M[string.lower(K[1:])] = V - # We ignore attributes which pertain to the cookie - # mechanism as a whole. See RFC 2109. - # (Does anyone care?) - else: - M = Morsel() - M.set(K, apply(self.net_setfunc, (V,)), V, strict=0) - UserDict.__setitem__(self, K, M) - return - # end __ParseString - -# end Cookie class - -#Local Variables: -#tab-width: 4 -#End: |
