1*cda5da8dSAndroid Build Coastguard Workerr"""HTTP cookie handling for web clients. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerThis module has (now fairly distant) origins in Gisle Aas' Perl module 4*cda5da8dSAndroid Build Coastguard WorkerHTTP::Cookies, from the libwww-perl library. 5*cda5da8dSAndroid Build Coastguard Worker 6*cda5da8dSAndroid Build Coastguard WorkerDocstrings, comments and debug strings in this code refer to the 7*cda5da8dSAndroid Build Coastguard Workerattributes of the HTTP cookie system as cookie-attributes, to distinguish 8*cda5da8dSAndroid Build Coastguard Workerthem clearly from Python attributes. 9*cda5da8dSAndroid Build Coastguard Worker 10*cda5da8dSAndroid Build Coastguard WorkerClass diagram (note that BSDDBCookieJar and the MSIE* classes are not 11*cda5da8dSAndroid Build Coastguard Workerdistributed with the Python standard library, but are available from 12*cda5da8dSAndroid Build Coastguard Workerhttp://wwwsearch.sf.net/): 13*cda5da8dSAndroid Build Coastguard Worker 14*cda5da8dSAndroid Build Coastguard Worker CookieJar____ 15*cda5da8dSAndroid Build Coastguard Worker / \ \ 16*cda5da8dSAndroid Build Coastguard Worker FileCookieJar \ \ 17*cda5da8dSAndroid Build Coastguard Worker / | \ \ \ 18*cda5da8dSAndroid Build Coastguard Worker MozillaCookieJar | LWPCookieJar \ \ 19*cda5da8dSAndroid Build Coastguard Worker | | \ 20*cda5da8dSAndroid Build Coastguard Worker | ---MSIEBase | \ 21*cda5da8dSAndroid Build Coastguard Worker | / | | \ 22*cda5da8dSAndroid Build Coastguard Worker | / MSIEDBCookieJar BSDDBCookieJar 23*cda5da8dSAndroid Build Coastguard Worker |/ 24*cda5da8dSAndroid Build Coastguard Worker MSIECookieJar 25*cda5da8dSAndroid Build Coastguard Worker 26*cda5da8dSAndroid Build Coastguard Worker""" 27*cda5da8dSAndroid Build Coastguard Worker 28*cda5da8dSAndroid Build Coastguard Worker__all__ = ['Cookie', 'CookieJar', 'CookiePolicy', 'DefaultCookiePolicy', 29*cda5da8dSAndroid Build Coastguard Worker 'FileCookieJar', 'LWPCookieJar', 'LoadError', 'MozillaCookieJar'] 30*cda5da8dSAndroid Build Coastguard Worker 31*cda5da8dSAndroid Build Coastguard Workerimport os 32*cda5da8dSAndroid Build Coastguard Workerimport copy 33*cda5da8dSAndroid Build Coastguard Workerimport datetime 34*cda5da8dSAndroid Build Coastguard Workerimport re 35*cda5da8dSAndroid Build Coastguard Workerimport time 36*cda5da8dSAndroid Build Coastguard Workerimport urllib.parse, urllib.request 37*cda5da8dSAndroid Build Coastguard Workerimport threading as _threading 38*cda5da8dSAndroid Build Coastguard Workerimport http.client # only for the default HTTP port 39*cda5da8dSAndroid Build Coastguard Workerfrom calendar import timegm 40*cda5da8dSAndroid Build Coastguard Worker 41*cda5da8dSAndroid Build Coastguard Workerdebug = False # set to True to enable debugging via the logging module 42*cda5da8dSAndroid Build Coastguard Workerlogger = None 43*cda5da8dSAndroid Build Coastguard Worker 44*cda5da8dSAndroid Build Coastguard Workerdef _debug(*args): 45*cda5da8dSAndroid Build Coastguard Worker if not debug: 46*cda5da8dSAndroid Build Coastguard Worker return 47*cda5da8dSAndroid Build Coastguard Worker global logger 48*cda5da8dSAndroid Build Coastguard Worker if not logger: 49*cda5da8dSAndroid Build Coastguard Worker import logging 50*cda5da8dSAndroid Build Coastguard Worker logger = logging.getLogger("http.cookiejar") 51*cda5da8dSAndroid Build Coastguard Worker return logger.debug(*args) 52*cda5da8dSAndroid Build Coastguard Worker 53*cda5da8dSAndroid Build Coastguard WorkerHTTPONLY_ATTR = "HTTPOnly" 54*cda5da8dSAndroid Build Coastguard WorkerHTTPONLY_PREFIX = "#HttpOnly_" 55*cda5da8dSAndroid Build Coastguard WorkerDEFAULT_HTTP_PORT = str(http.client.HTTP_PORT) 56*cda5da8dSAndroid Build Coastguard WorkerNETSCAPE_MAGIC_RGX = re.compile("#( Netscape)? HTTP Cookie File") 57*cda5da8dSAndroid Build Coastguard WorkerMISSING_FILENAME_TEXT = ("a filename was not supplied (nor was the CookieJar " 58*cda5da8dSAndroid Build Coastguard Worker "instance initialised with one)") 59*cda5da8dSAndroid Build Coastguard WorkerNETSCAPE_HEADER_TEXT = """\ 60*cda5da8dSAndroid Build Coastguard Worker# Netscape HTTP Cookie File 61*cda5da8dSAndroid Build Coastguard Worker# http://curl.haxx.se/rfc/cookie_spec.html 62*cda5da8dSAndroid Build Coastguard Worker# This is a generated file! Do not edit. 63*cda5da8dSAndroid Build Coastguard Worker 64*cda5da8dSAndroid Build Coastguard Worker""" 65*cda5da8dSAndroid Build Coastguard Worker 66*cda5da8dSAndroid Build Coastguard Workerdef _warn_unhandled_exception(): 67*cda5da8dSAndroid Build Coastguard Worker # There are a few catch-all except: statements in this module, for 68*cda5da8dSAndroid Build Coastguard Worker # catching input that's bad in unexpected ways. Warn if any 69*cda5da8dSAndroid Build Coastguard Worker # exceptions are caught there. 70*cda5da8dSAndroid Build Coastguard Worker import io, warnings, traceback 71*cda5da8dSAndroid Build Coastguard Worker f = io.StringIO() 72*cda5da8dSAndroid Build Coastguard Worker traceback.print_exc(None, f) 73*cda5da8dSAndroid Build Coastguard Worker msg = f.getvalue() 74*cda5da8dSAndroid Build Coastguard Worker warnings.warn("http.cookiejar bug!\n%s" % msg, stacklevel=2) 75*cda5da8dSAndroid Build Coastguard Worker 76*cda5da8dSAndroid Build Coastguard Worker 77*cda5da8dSAndroid Build Coastguard Worker# Date/time conversion 78*cda5da8dSAndroid Build Coastguard Worker# ----------------------------------------------------------------------------- 79*cda5da8dSAndroid Build Coastguard Worker 80*cda5da8dSAndroid Build Coastguard WorkerEPOCH_YEAR = 1970 81*cda5da8dSAndroid Build Coastguard Workerdef _timegm(tt): 82*cda5da8dSAndroid Build Coastguard Worker year, month, mday, hour, min, sec = tt[:6] 83*cda5da8dSAndroid Build Coastguard Worker if ((year >= EPOCH_YEAR) and (1 <= month <= 12) and (1 <= mday <= 31) and 84*cda5da8dSAndroid Build Coastguard Worker (0 <= hour <= 24) and (0 <= min <= 59) and (0 <= sec <= 61)): 85*cda5da8dSAndroid Build Coastguard Worker return timegm(tt) 86*cda5da8dSAndroid Build Coastguard Worker else: 87*cda5da8dSAndroid Build Coastguard Worker return None 88*cda5da8dSAndroid Build Coastguard Worker 89*cda5da8dSAndroid Build Coastguard WorkerDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] 90*cda5da8dSAndroid Build Coastguard WorkerMONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", 91*cda5da8dSAndroid Build Coastguard Worker "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 92*cda5da8dSAndroid Build Coastguard WorkerMONTHS_LOWER = [month.lower() for month in MONTHS] 93*cda5da8dSAndroid Build Coastguard Worker 94*cda5da8dSAndroid Build Coastguard Workerdef time2isoz(t=None): 95*cda5da8dSAndroid Build Coastguard Worker """Return a string representing time in seconds since epoch, t. 96*cda5da8dSAndroid Build Coastguard Worker 97*cda5da8dSAndroid Build Coastguard Worker If the function is called without an argument, it will use the current 98*cda5da8dSAndroid Build Coastguard Worker time. 99*cda5da8dSAndroid Build Coastguard Worker 100*cda5da8dSAndroid Build Coastguard Worker The format of the returned string is like "YYYY-MM-DD hh:mm:ssZ", 101*cda5da8dSAndroid Build Coastguard Worker representing Universal Time (UTC, aka GMT). An example of this format is: 102*cda5da8dSAndroid Build Coastguard Worker 103*cda5da8dSAndroid Build Coastguard Worker 1994-11-24 08:49:37Z 104*cda5da8dSAndroid Build Coastguard Worker 105*cda5da8dSAndroid Build Coastguard Worker """ 106*cda5da8dSAndroid Build Coastguard Worker if t is None: 107*cda5da8dSAndroid Build Coastguard Worker dt = datetime.datetime.utcnow() 108*cda5da8dSAndroid Build Coastguard Worker else: 109*cda5da8dSAndroid Build Coastguard Worker dt = datetime.datetime.utcfromtimestamp(t) 110*cda5da8dSAndroid Build Coastguard Worker return "%04d-%02d-%02d %02d:%02d:%02dZ" % ( 111*cda5da8dSAndroid Build Coastguard Worker dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) 112*cda5da8dSAndroid Build Coastguard Worker 113*cda5da8dSAndroid Build Coastguard Workerdef time2netscape(t=None): 114*cda5da8dSAndroid Build Coastguard Worker """Return a string representing time in seconds since epoch, t. 115*cda5da8dSAndroid Build Coastguard Worker 116*cda5da8dSAndroid Build Coastguard Worker If the function is called without an argument, it will use the current 117*cda5da8dSAndroid Build Coastguard Worker time. 118*cda5da8dSAndroid Build Coastguard Worker 119*cda5da8dSAndroid Build Coastguard Worker The format of the returned string is like this: 120*cda5da8dSAndroid Build Coastguard Worker 121*cda5da8dSAndroid Build Coastguard Worker Wed, DD-Mon-YYYY HH:MM:SS GMT 122*cda5da8dSAndroid Build Coastguard Worker 123*cda5da8dSAndroid Build Coastguard Worker """ 124*cda5da8dSAndroid Build Coastguard Worker if t is None: 125*cda5da8dSAndroid Build Coastguard Worker dt = datetime.datetime.utcnow() 126*cda5da8dSAndroid Build Coastguard Worker else: 127*cda5da8dSAndroid Build Coastguard Worker dt = datetime.datetime.utcfromtimestamp(t) 128*cda5da8dSAndroid Build Coastguard Worker return "%s, %02d-%s-%04d %02d:%02d:%02d GMT" % ( 129*cda5da8dSAndroid Build Coastguard Worker DAYS[dt.weekday()], dt.day, MONTHS[dt.month-1], 130*cda5da8dSAndroid Build Coastguard Worker dt.year, dt.hour, dt.minute, dt.second) 131*cda5da8dSAndroid Build Coastguard Worker 132*cda5da8dSAndroid Build Coastguard Worker 133*cda5da8dSAndroid Build Coastguard WorkerUTC_ZONES = {"GMT": None, "UTC": None, "UT": None, "Z": None} 134*cda5da8dSAndroid Build Coastguard Worker 135*cda5da8dSAndroid Build Coastguard WorkerTIMEZONE_RE = re.compile(r"^([-+])?(\d\d?):?(\d\d)?$", re.ASCII) 136*cda5da8dSAndroid Build Coastguard Workerdef offset_from_tz_string(tz): 137*cda5da8dSAndroid Build Coastguard Worker offset = None 138*cda5da8dSAndroid Build Coastguard Worker if tz in UTC_ZONES: 139*cda5da8dSAndroid Build Coastguard Worker offset = 0 140*cda5da8dSAndroid Build Coastguard Worker else: 141*cda5da8dSAndroid Build Coastguard Worker m = TIMEZONE_RE.search(tz) 142*cda5da8dSAndroid Build Coastguard Worker if m: 143*cda5da8dSAndroid Build Coastguard Worker offset = 3600 * int(m.group(2)) 144*cda5da8dSAndroid Build Coastguard Worker if m.group(3): 145*cda5da8dSAndroid Build Coastguard Worker offset = offset + 60 * int(m.group(3)) 146*cda5da8dSAndroid Build Coastguard Worker if m.group(1) == '-': 147*cda5da8dSAndroid Build Coastguard Worker offset = -offset 148*cda5da8dSAndroid Build Coastguard Worker return offset 149*cda5da8dSAndroid Build Coastguard Worker 150*cda5da8dSAndroid Build Coastguard Workerdef _str2time(day, mon, yr, hr, min, sec, tz): 151*cda5da8dSAndroid Build Coastguard Worker yr = int(yr) 152*cda5da8dSAndroid Build Coastguard Worker if yr > datetime.MAXYEAR: 153*cda5da8dSAndroid Build Coastguard Worker return None 154*cda5da8dSAndroid Build Coastguard Worker 155*cda5da8dSAndroid Build Coastguard Worker # translate month name to number 156*cda5da8dSAndroid Build Coastguard Worker # month numbers start with 1 (January) 157*cda5da8dSAndroid Build Coastguard Worker try: 158*cda5da8dSAndroid Build Coastguard Worker mon = MONTHS_LOWER.index(mon.lower())+1 159*cda5da8dSAndroid Build Coastguard Worker except ValueError: 160*cda5da8dSAndroid Build Coastguard Worker # maybe it's already a number 161*cda5da8dSAndroid Build Coastguard Worker try: 162*cda5da8dSAndroid Build Coastguard Worker imon = int(mon) 163*cda5da8dSAndroid Build Coastguard Worker except ValueError: 164*cda5da8dSAndroid Build Coastguard Worker return None 165*cda5da8dSAndroid Build Coastguard Worker if 1 <= imon <= 12: 166*cda5da8dSAndroid Build Coastguard Worker mon = imon 167*cda5da8dSAndroid Build Coastguard Worker else: 168*cda5da8dSAndroid Build Coastguard Worker return None 169*cda5da8dSAndroid Build Coastguard Worker 170*cda5da8dSAndroid Build Coastguard Worker # make sure clock elements are defined 171*cda5da8dSAndroid Build Coastguard Worker if hr is None: hr = 0 172*cda5da8dSAndroid Build Coastguard Worker if min is None: min = 0 173*cda5da8dSAndroid Build Coastguard Worker if sec is None: sec = 0 174*cda5da8dSAndroid Build Coastguard Worker 175*cda5da8dSAndroid Build Coastguard Worker day = int(day) 176*cda5da8dSAndroid Build Coastguard Worker hr = int(hr) 177*cda5da8dSAndroid Build Coastguard Worker min = int(min) 178*cda5da8dSAndroid Build Coastguard Worker sec = int(sec) 179*cda5da8dSAndroid Build Coastguard Worker 180*cda5da8dSAndroid Build Coastguard Worker if yr < 1000: 181*cda5da8dSAndroid Build Coastguard Worker # find "obvious" year 182*cda5da8dSAndroid Build Coastguard Worker cur_yr = time.localtime(time.time())[0] 183*cda5da8dSAndroid Build Coastguard Worker m = cur_yr % 100 184*cda5da8dSAndroid Build Coastguard Worker tmp = yr 185*cda5da8dSAndroid Build Coastguard Worker yr = yr + cur_yr - m 186*cda5da8dSAndroid Build Coastguard Worker m = m - tmp 187*cda5da8dSAndroid Build Coastguard Worker if abs(m) > 50: 188*cda5da8dSAndroid Build Coastguard Worker if m > 0: yr = yr + 100 189*cda5da8dSAndroid Build Coastguard Worker else: yr = yr - 100 190*cda5da8dSAndroid Build Coastguard Worker 191*cda5da8dSAndroid Build Coastguard Worker # convert UTC time tuple to seconds since epoch (not timezone-adjusted) 192*cda5da8dSAndroid Build Coastguard Worker t = _timegm((yr, mon, day, hr, min, sec, tz)) 193*cda5da8dSAndroid Build Coastguard Worker 194*cda5da8dSAndroid Build Coastguard Worker if t is not None: 195*cda5da8dSAndroid Build Coastguard Worker # adjust time using timezone string, to get absolute time since epoch 196*cda5da8dSAndroid Build Coastguard Worker if tz is None: 197*cda5da8dSAndroid Build Coastguard Worker tz = "UTC" 198*cda5da8dSAndroid Build Coastguard Worker tz = tz.upper() 199*cda5da8dSAndroid Build Coastguard Worker offset = offset_from_tz_string(tz) 200*cda5da8dSAndroid Build Coastguard Worker if offset is None: 201*cda5da8dSAndroid Build Coastguard Worker return None 202*cda5da8dSAndroid Build Coastguard Worker t = t - offset 203*cda5da8dSAndroid Build Coastguard Worker 204*cda5da8dSAndroid Build Coastguard Worker return t 205*cda5da8dSAndroid Build Coastguard Worker 206*cda5da8dSAndroid Build Coastguard WorkerSTRICT_DATE_RE = re.compile( 207*cda5da8dSAndroid Build Coastguard Worker r"^[SMTWF][a-z][a-z], (\d\d) ([JFMASOND][a-z][a-z]) " 208*cda5da8dSAndroid Build Coastguard Worker r"(\d\d\d\d) (\d\d):(\d\d):(\d\d) GMT$", re.ASCII) 209*cda5da8dSAndroid Build Coastguard WorkerWEEKDAY_RE = re.compile( 210*cda5da8dSAndroid Build Coastguard Worker r"^(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)[a-z]*,?\s*", re.I | re.ASCII) 211*cda5da8dSAndroid Build Coastguard WorkerLOOSE_HTTP_DATE_RE = re.compile( 212*cda5da8dSAndroid Build Coastguard Worker r"""^ 213*cda5da8dSAndroid Build Coastguard Worker (\d\d?) # day 214*cda5da8dSAndroid Build Coastguard Worker (?:\s+|[-\/]) 215*cda5da8dSAndroid Build Coastguard Worker (\w+) # month 216*cda5da8dSAndroid Build Coastguard Worker (?:\s+|[-\/]) 217*cda5da8dSAndroid Build Coastguard Worker (\d+) # year 218*cda5da8dSAndroid Build Coastguard Worker (?: 219*cda5da8dSAndroid Build Coastguard Worker (?:\s+|:) # separator before clock 220*cda5da8dSAndroid Build Coastguard Worker (\d\d?):(\d\d) # hour:min 221*cda5da8dSAndroid Build Coastguard Worker (?::(\d\d))? # optional seconds 222*cda5da8dSAndroid Build Coastguard Worker )? # optional clock 223*cda5da8dSAndroid Build Coastguard Worker \s* 224*cda5da8dSAndroid Build Coastguard Worker (?: 225*cda5da8dSAndroid Build Coastguard Worker ([-+]?\d{2,4}|(?![APap][Mm]\b)[A-Za-z]+) # timezone 226*cda5da8dSAndroid Build Coastguard Worker \s* 227*cda5da8dSAndroid Build Coastguard Worker )? 228*cda5da8dSAndroid Build Coastguard Worker (?: 229*cda5da8dSAndroid Build Coastguard Worker \(\w+\) # ASCII representation of timezone in parens. 230*cda5da8dSAndroid Build Coastguard Worker \s* 231*cda5da8dSAndroid Build Coastguard Worker )?$""", re.X | re.ASCII) 232*cda5da8dSAndroid Build Coastguard Workerdef http2time(text): 233*cda5da8dSAndroid Build Coastguard Worker """Returns time in seconds since epoch of time represented by a string. 234*cda5da8dSAndroid Build Coastguard Worker 235*cda5da8dSAndroid Build Coastguard Worker Return value is an integer. 236*cda5da8dSAndroid Build Coastguard Worker 237*cda5da8dSAndroid Build Coastguard Worker None is returned if the format of str is unrecognized, the time is outside 238*cda5da8dSAndroid Build Coastguard Worker the representable range, or the timezone string is not recognized. If the 239*cda5da8dSAndroid Build Coastguard Worker string contains no timezone, UTC is assumed. 240*cda5da8dSAndroid Build Coastguard Worker 241*cda5da8dSAndroid Build Coastguard Worker The timezone in the string may be numerical (like "-0800" or "+0100") or a 242*cda5da8dSAndroid Build Coastguard Worker string timezone (like "UTC", "GMT", "BST" or "EST"). Currently, only the 243*cda5da8dSAndroid Build Coastguard Worker timezone strings equivalent to UTC (zero offset) are known to the function. 244*cda5da8dSAndroid Build Coastguard Worker 245*cda5da8dSAndroid Build Coastguard Worker The function loosely parses the following formats: 246*cda5da8dSAndroid Build Coastguard Worker 247*cda5da8dSAndroid Build Coastguard Worker Wed, 09 Feb 1994 22:23:32 GMT -- HTTP format 248*cda5da8dSAndroid Build Coastguard Worker Tuesday, 08-Feb-94 14:15:29 GMT -- old rfc850 HTTP format 249*cda5da8dSAndroid Build Coastguard Worker Tuesday, 08-Feb-1994 14:15:29 GMT -- broken rfc850 HTTP format 250*cda5da8dSAndroid Build Coastguard Worker 09 Feb 1994 22:23:32 GMT -- HTTP format (no weekday) 251*cda5da8dSAndroid Build Coastguard Worker 08-Feb-94 14:15:29 GMT -- rfc850 format (no weekday) 252*cda5da8dSAndroid Build Coastguard Worker 08-Feb-1994 14:15:29 GMT -- broken rfc850 format (no weekday) 253*cda5da8dSAndroid Build Coastguard Worker 254*cda5da8dSAndroid Build Coastguard Worker The parser ignores leading and trailing whitespace. The time may be 255*cda5da8dSAndroid Build Coastguard Worker absent. 256*cda5da8dSAndroid Build Coastguard Worker 257*cda5da8dSAndroid Build Coastguard Worker If the year is given with only 2 digits, the function will select the 258*cda5da8dSAndroid Build Coastguard Worker century that makes the year closest to the current date. 259*cda5da8dSAndroid Build Coastguard Worker 260*cda5da8dSAndroid Build Coastguard Worker """ 261*cda5da8dSAndroid Build Coastguard Worker # fast exit for strictly conforming string 262*cda5da8dSAndroid Build Coastguard Worker m = STRICT_DATE_RE.search(text) 263*cda5da8dSAndroid Build Coastguard Worker if m: 264*cda5da8dSAndroid Build Coastguard Worker g = m.groups() 265*cda5da8dSAndroid Build Coastguard Worker mon = MONTHS_LOWER.index(g[1].lower()) + 1 266*cda5da8dSAndroid Build Coastguard Worker tt = (int(g[2]), mon, int(g[0]), 267*cda5da8dSAndroid Build Coastguard Worker int(g[3]), int(g[4]), float(g[5])) 268*cda5da8dSAndroid Build Coastguard Worker return _timegm(tt) 269*cda5da8dSAndroid Build Coastguard Worker 270*cda5da8dSAndroid Build Coastguard Worker # No, we need some messy parsing... 271*cda5da8dSAndroid Build Coastguard Worker 272*cda5da8dSAndroid Build Coastguard Worker # clean up 273*cda5da8dSAndroid Build Coastguard Worker text = text.lstrip() 274*cda5da8dSAndroid Build Coastguard Worker text = WEEKDAY_RE.sub("", text, 1) # Useless weekday 275*cda5da8dSAndroid Build Coastguard Worker 276*cda5da8dSAndroid Build Coastguard Worker # tz is time zone specifier string 277*cda5da8dSAndroid Build Coastguard Worker day, mon, yr, hr, min, sec, tz = [None]*7 278*cda5da8dSAndroid Build Coastguard Worker 279*cda5da8dSAndroid Build Coastguard Worker # loose regexp parse 280*cda5da8dSAndroid Build Coastguard Worker m = LOOSE_HTTP_DATE_RE.search(text) 281*cda5da8dSAndroid Build Coastguard Worker if m is not None: 282*cda5da8dSAndroid Build Coastguard Worker day, mon, yr, hr, min, sec, tz = m.groups() 283*cda5da8dSAndroid Build Coastguard Worker else: 284*cda5da8dSAndroid Build Coastguard Worker return None # bad format 285*cda5da8dSAndroid Build Coastguard Worker 286*cda5da8dSAndroid Build Coastguard Worker return _str2time(day, mon, yr, hr, min, sec, tz) 287*cda5da8dSAndroid Build Coastguard Worker 288*cda5da8dSAndroid Build Coastguard WorkerISO_DATE_RE = re.compile( 289*cda5da8dSAndroid Build Coastguard Worker r"""^ 290*cda5da8dSAndroid Build Coastguard Worker (\d{4}) # year 291*cda5da8dSAndroid Build Coastguard Worker [-\/]? 292*cda5da8dSAndroid Build Coastguard Worker (\d\d?) # numerical month 293*cda5da8dSAndroid Build Coastguard Worker [-\/]? 294*cda5da8dSAndroid Build Coastguard Worker (\d\d?) # day 295*cda5da8dSAndroid Build Coastguard Worker (?: 296*cda5da8dSAndroid Build Coastguard Worker (?:\s+|[-:Tt]) # separator before clock 297*cda5da8dSAndroid Build Coastguard Worker (\d\d?):?(\d\d) # hour:min 298*cda5da8dSAndroid Build Coastguard Worker (?::?(\d\d(?:\.\d*)?))? # optional seconds (and fractional) 299*cda5da8dSAndroid Build Coastguard Worker )? # optional clock 300*cda5da8dSAndroid Build Coastguard Worker \s* 301*cda5da8dSAndroid Build Coastguard Worker (?: 302*cda5da8dSAndroid Build Coastguard Worker ([-+]?\d\d?:?(:?\d\d)? 303*cda5da8dSAndroid Build Coastguard Worker |Z|z) # timezone (Z is "zero meridian", i.e. GMT) 304*cda5da8dSAndroid Build Coastguard Worker \s* 305*cda5da8dSAndroid Build Coastguard Worker )?$""", re.X | re. ASCII) 306*cda5da8dSAndroid Build Coastguard Workerdef iso2time(text): 307*cda5da8dSAndroid Build Coastguard Worker """ 308*cda5da8dSAndroid Build Coastguard Worker As for http2time, but parses the ISO 8601 formats: 309*cda5da8dSAndroid Build Coastguard Worker 310*cda5da8dSAndroid Build Coastguard Worker 1994-02-03 14:15:29 -0100 -- ISO 8601 format 311*cda5da8dSAndroid Build Coastguard Worker 1994-02-03 14:15:29 -- zone is optional 312*cda5da8dSAndroid Build Coastguard Worker 1994-02-03 -- only date 313*cda5da8dSAndroid Build Coastguard Worker 1994-02-03T14:15:29 -- Use T as separator 314*cda5da8dSAndroid Build Coastguard Worker 19940203T141529Z -- ISO 8601 compact format 315*cda5da8dSAndroid Build Coastguard Worker 19940203 -- only date 316*cda5da8dSAndroid Build Coastguard Worker 317*cda5da8dSAndroid Build Coastguard Worker """ 318*cda5da8dSAndroid Build Coastguard Worker # clean up 319*cda5da8dSAndroid Build Coastguard Worker text = text.lstrip() 320*cda5da8dSAndroid Build Coastguard Worker 321*cda5da8dSAndroid Build Coastguard Worker # tz is time zone specifier string 322*cda5da8dSAndroid Build Coastguard Worker day, mon, yr, hr, min, sec, tz = [None]*7 323*cda5da8dSAndroid Build Coastguard Worker 324*cda5da8dSAndroid Build Coastguard Worker # loose regexp parse 325*cda5da8dSAndroid Build Coastguard Worker m = ISO_DATE_RE.search(text) 326*cda5da8dSAndroid Build Coastguard Worker if m is not None: 327*cda5da8dSAndroid Build Coastguard Worker # XXX there's an extra bit of the timezone I'm ignoring here: is 328*cda5da8dSAndroid Build Coastguard Worker # this the right thing to do? 329*cda5da8dSAndroid Build Coastguard Worker yr, mon, day, hr, min, sec, tz, _ = m.groups() 330*cda5da8dSAndroid Build Coastguard Worker else: 331*cda5da8dSAndroid Build Coastguard Worker return None # bad format 332*cda5da8dSAndroid Build Coastguard Worker 333*cda5da8dSAndroid Build Coastguard Worker return _str2time(day, mon, yr, hr, min, sec, tz) 334*cda5da8dSAndroid Build Coastguard Worker 335*cda5da8dSAndroid Build Coastguard Worker 336*cda5da8dSAndroid Build Coastguard Worker# Header parsing 337*cda5da8dSAndroid Build Coastguard Worker# ----------------------------------------------------------------------------- 338*cda5da8dSAndroid Build Coastguard Worker 339*cda5da8dSAndroid Build Coastguard Workerdef unmatched(match): 340*cda5da8dSAndroid Build Coastguard Worker """Return unmatched part of re.Match object.""" 341*cda5da8dSAndroid Build Coastguard Worker start, end = match.span(0) 342*cda5da8dSAndroid Build Coastguard Worker return match.string[:start]+match.string[end:] 343*cda5da8dSAndroid Build Coastguard Worker 344*cda5da8dSAndroid Build Coastguard WorkerHEADER_TOKEN_RE = re.compile(r"^\s*([^=\s;,]+)") 345*cda5da8dSAndroid Build Coastguard WorkerHEADER_QUOTED_VALUE_RE = re.compile(r"^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"") 346*cda5da8dSAndroid Build Coastguard WorkerHEADER_VALUE_RE = re.compile(r"^\s*=\s*([^\s;,]*)") 347*cda5da8dSAndroid Build Coastguard WorkerHEADER_ESCAPE_RE = re.compile(r"\\(.)") 348*cda5da8dSAndroid Build Coastguard Workerdef split_header_words(header_values): 349*cda5da8dSAndroid Build Coastguard Worker r"""Parse header values into a list of lists containing key,value pairs. 350*cda5da8dSAndroid Build Coastguard Worker 351*cda5da8dSAndroid Build Coastguard Worker The function knows how to deal with ",", ";" and "=" as well as quoted 352*cda5da8dSAndroid Build Coastguard Worker values after "=". A list of space separated tokens are parsed as if they 353*cda5da8dSAndroid Build Coastguard Worker were separated by ";". 354*cda5da8dSAndroid Build Coastguard Worker 355*cda5da8dSAndroid Build Coastguard Worker If the header_values passed as argument contains multiple values, then they 356*cda5da8dSAndroid Build Coastguard Worker are treated as if they were a single value separated by comma ",". 357*cda5da8dSAndroid Build Coastguard Worker 358*cda5da8dSAndroid Build Coastguard Worker This means that this function is useful for parsing header fields that 359*cda5da8dSAndroid Build Coastguard Worker follow this syntax (BNF as from the HTTP/1.1 specification, but we relax 360*cda5da8dSAndroid Build Coastguard Worker the requirement for tokens). 361*cda5da8dSAndroid Build Coastguard Worker 362*cda5da8dSAndroid Build Coastguard Worker headers = #header 363*cda5da8dSAndroid Build Coastguard Worker header = (token | parameter) *( [";"] (token | parameter)) 364*cda5da8dSAndroid Build Coastguard Worker 365*cda5da8dSAndroid Build Coastguard Worker token = 1*<any CHAR except CTLs or separators> 366*cda5da8dSAndroid Build Coastguard Worker separators = "(" | ")" | "<" | ">" | "@" 367*cda5da8dSAndroid Build Coastguard Worker | "," | ";" | ":" | "\" | <"> 368*cda5da8dSAndroid Build Coastguard Worker | "/" | "[" | "]" | "?" | "=" 369*cda5da8dSAndroid Build Coastguard Worker | "{" | "}" | SP | HT 370*cda5da8dSAndroid Build Coastguard Worker 371*cda5da8dSAndroid Build Coastguard Worker quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) 372*cda5da8dSAndroid Build Coastguard Worker qdtext = <any TEXT except <">> 373*cda5da8dSAndroid Build Coastguard Worker quoted-pair = "\" CHAR 374*cda5da8dSAndroid Build Coastguard Worker 375*cda5da8dSAndroid Build Coastguard Worker parameter = attribute "=" value 376*cda5da8dSAndroid Build Coastguard Worker attribute = token 377*cda5da8dSAndroid Build Coastguard Worker value = token | quoted-string 378*cda5da8dSAndroid Build Coastguard Worker 379*cda5da8dSAndroid Build Coastguard Worker Each header is represented by a list of key/value pairs. The value for a 380*cda5da8dSAndroid Build Coastguard Worker simple token (not part of a parameter) is None. Syntactically incorrect 381*cda5da8dSAndroid Build Coastguard Worker headers will not necessarily be parsed as you would want. 382*cda5da8dSAndroid Build Coastguard Worker 383*cda5da8dSAndroid Build Coastguard Worker This is easier to describe with some examples: 384*cda5da8dSAndroid Build Coastguard Worker 385*cda5da8dSAndroid Build Coastguard Worker >>> split_header_words(['foo="bar"; port="80,81"; discard, bar=baz']) 386*cda5da8dSAndroid Build Coastguard Worker [[('foo', 'bar'), ('port', '80,81'), ('discard', None)], [('bar', 'baz')]] 387*cda5da8dSAndroid Build Coastguard Worker >>> split_header_words(['text/html; charset="iso-8859-1"']) 388*cda5da8dSAndroid Build Coastguard Worker [[('text/html', None), ('charset', 'iso-8859-1')]] 389*cda5da8dSAndroid Build Coastguard Worker >>> split_header_words([r'Basic realm="\"foo\bar\""']) 390*cda5da8dSAndroid Build Coastguard Worker [[('Basic', None), ('realm', '"foobar"')]] 391*cda5da8dSAndroid Build Coastguard Worker 392*cda5da8dSAndroid Build Coastguard Worker """ 393*cda5da8dSAndroid Build Coastguard Worker assert not isinstance(header_values, str) 394*cda5da8dSAndroid Build Coastguard Worker result = [] 395*cda5da8dSAndroid Build Coastguard Worker for text in header_values: 396*cda5da8dSAndroid Build Coastguard Worker orig_text = text 397*cda5da8dSAndroid Build Coastguard Worker pairs = [] 398*cda5da8dSAndroid Build Coastguard Worker while text: 399*cda5da8dSAndroid Build Coastguard Worker m = HEADER_TOKEN_RE.search(text) 400*cda5da8dSAndroid Build Coastguard Worker if m: 401*cda5da8dSAndroid Build Coastguard Worker text = unmatched(m) 402*cda5da8dSAndroid Build Coastguard Worker name = m.group(1) 403*cda5da8dSAndroid Build Coastguard Worker m = HEADER_QUOTED_VALUE_RE.search(text) 404*cda5da8dSAndroid Build Coastguard Worker if m: # quoted value 405*cda5da8dSAndroid Build Coastguard Worker text = unmatched(m) 406*cda5da8dSAndroid Build Coastguard Worker value = m.group(1) 407*cda5da8dSAndroid Build Coastguard Worker value = HEADER_ESCAPE_RE.sub(r"\1", value) 408*cda5da8dSAndroid Build Coastguard Worker else: 409*cda5da8dSAndroid Build Coastguard Worker m = HEADER_VALUE_RE.search(text) 410*cda5da8dSAndroid Build Coastguard Worker if m: # unquoted value 411*cda5da8dSAndroid Build Coastguard Worker text = unmatched(m) 412*cda5da8dSAndroid Build Coastguard Worker value = m.group(1) 413*cda5da8dSAndroid Build Coastguard Worker value = value.rstrip() 414*cda5da8dSAndroid Build Coastguard Worker else: 415*cda5da8dSAndroid Build Coastguard Worker # no value, a lone token 416*cda5da8dSAndroid Build Coastguard Worker value = None 417*cda5da8dSAndroid Build Coastguard Worker pairs.append((name, value)) 418*cda5da8dSAndroid Build Coastguard Worker elif text.lstrip().startswith(","): 419*cda5da8dSAndroid Build Coastguard Worker # concatenated headers, as per RFC 2616 section 4.2 420*cda5da8dSAndroid Build Coastguard Worker text = text.lstrip()[1:] 421*cda5da8dSAndroid Build Coastguard Worker if pairs: result.append(pairs) 422*cda5da8dSAndroid Build Coastguard Worker pairs = [] 423*cda5da8dSAndroid Build Coastguard Worker else: 424*cda5da8dSAndroid Build Coastguard Worker # skip junk 425*cda5da8dSAndroid Build Coastguard Worker non_junk, nr_junk_chars = re.subn(r"^[=\s;]*", "", text) 426*cda5da8dSAndroid Build Coastguard Worker assert nr_junk_chars > 0, ( 427*cda5da8dSAndroid Build Coastguard Worker "split_header_words bug: '%s', '%s', %s" % 428*cda5da8dSAndroid Build Coastguard Worker (orig_text, text, pairs)) 429*cda5da8dSAndroid Build Coastguard Worker text = non_junk 430*cda5da8dSAndroid Build Coastguard Worker if pairs: result.append(pairs) 431*cda5da8dSAndroid Build Coastguard Worker return result 432*cda5da8dSAndroid Build Coastguard Worker 433*cda5da8dSAndroid Build Coastguard WorkerHEADER_JOIN_ESCAPE_RE = re.compile(r"([\"\\])") 434*cda5da8dSAndroid Build Coastguard Workerdef join_header_words(lists): 435*cda5da8dSAndroid Build Coastguard Worker """Do the inverse (almost) of the conversion done by split_header_words. 436*cda5da8dSAndroid Build Coastguard Worker 437*cda5da8dSAndroid Build Coastguard Worker Takes a list of lists of (key, value) pairs and produces a single header 438*cda5da8dSAndroid Build Coastguard Worker value. Attribute values are quoted if needed. 439*cda5da8dSAndroid Build Coastguard Worker 440*cda5da8dSAndroid Build Coastguard Worker >>> join_header_words([[("text/plain", None), ("charset", "iso-8859-1")]]) 441*cda5da8dSAndroid Build Coastguard Worker 'text/plain; charset="iso-8859-1"' 442*cda5da8dSAndroid Build Coastguard Worker >>> join_header_words([[("text/plain", None)], [("charset", "iso-8859-1")]]) 443*cda5da8dSAndroid Build Coastguard Worker 'text/plain, charset="iso-8859-1"' 444*cda5da8dSAndroid Build Coastguard Worker 445*cda5da8dSAndroid Build Coastguard Worker """ 446*cda5da8dSAndroid Build Coastguard Worker headers = [] 447*cda5da8dSAndroid Build Coastguard Worker for pairs in lists: 448*cda5da8dSAndroid Build Coastguard Worker attr = [] 449*cda5da8dSAndroid Build Coastguard Worker for k, v in pairs: 450*cda5da8dSAndroid Build Coastguard Worker if v is not None: 451*cda5da8dSAndroid Build Coastguard Worker if not re.search(r"^\w+$", v): 452*cda5da8dSAndroid Build Coastguard Worker v = HEADER_JOIN_ESCAPE_RE.sub(r"\\\1", v) # escape " and \ 453*cda5da8dSAndroid Build Coastguard Worker v = '"%s"' % v 454*cda5da8dSAndroid Build Coastguard Worker k = "%s=%s" % (k, v) 455*cda5da8dSAndroid Build Coastguard Worker attr.append(k) 456*cda5da8dSAndroid Build Coastguard Worker if attr: headers.append("; ".join(attr)) 457*cda5da8dSAndroid Build Coastguard Worker return ", ".join(headers) 458*cda5da8dSAndroid Build Coastguard Worker 459*cda5da8dSAndroid Build Coastguard Workerdef strip_quotes(text): 460*cda5da8dSAndroid Build Coastguard Worker if text.startswith('"'): 461*cda5da8dSAndroid Build Coastguard Worker text = text[1:] 462*cda5da8dSAndroid Build Coastguard Worker if text.endswith('"'): 463*cda5da8dSAndroid Build Coastguard Worker text = text[:-1] 464*cda5da8dSAndroid Build Coastguard Worker return text 465*cda5da8dSAndroid Build Coastguard Worker 466*cda5da8dSAndroid Build Coastguard Workerdef parse_ns_headers(ns_headers): 467*cda5da8dSAndroid Build Coastguard Worker """Ad-hoc parser for Netscape protocol cookie-attributes. 468*cda5da8dSAndroid Build Coastguard Worker 469*cda5da8dSAndroid Build Coastguard Worker The old Netscape cookie format for Set-Cookie can for instance contain 470*cda5da8dSAndroid Build Coastguard Worker an unquoted "," in the expires field, so we have to use this ad-hoc 471*cda5da8dSAndroid Build Coastguard Worker parser instead of split_header_words. 472*cda5da8dSAndroid Build Coastguard Worker 473*cda5da8dSAndroid Build Coastguard Worker XXX This may not make the best possible effort to parse all the crap 474*cda5da8dSAndroid Build Coastguard Worker that Netscape Cookie headers contain. Ronald Tschalar's HTTPClient 475*cda5da8dSAndroid Build Coastguard Worker parser is probably better, so could do worse than following that if 476*cda5da8dSAndroid Build Coastguard Worker this ever gives any trouble. 477*cda5da8dSAndroid Build Coastguard Worker 478*cda5da8dSAndroid Build Coastguard Worker Currently, this is also used for parsing RFC 2109 cookies. 479*cda5da8dSAndroid Build Coastguard Worker 480*cda5da8dSAndroid Build Coastguard Worker """ 481*cda5da8dSAndroid Build Coastguard Worker known_attrs = ("expires", "domain", "path", "secure", 482*cda5da8dSAndroid Build Coastguard Worker # RFC 2109 attrs (may turn up in Netscape cookies, too) 483*cda5da8dSAndroid Build Coastguard Worker "version", "port", "max-age") 484*cda5da8dSAndroid Build Coastguard Worker 485*cda5da8dSAndroid Build Coastguard Worker result = [] 486*cda5da8dSAndroid Build Coastguard Worker for ns_header in ns_headers: 487*cda5da8dSAndroid Build Coastguard Worker pairs = [] 488*cda5da8dSAndroid Build Coastguard Worker version_set = False 489*cda5da8dSAndroid Build Coastguard Worker 490*cda5da8dSAndroid Build Coastguard Worker # XXX: The following does not strictly adhere to RFCs in that empty 491*cda5da8dSAndroid Build Coastguard Worker # names and values are legal (the former will only appear once and will 492*cda5da8dSAndroid Build Coastguard Worker # be overwritten if multiple occurrences are present). This is 493*cda5da8dSAndroid Build Coastguard Worker # mostly to deal with backwards compatibility. 494*cda5da8dSAndroid Build Coastguard Worker for ii, param in enumerate(ns_header.split(';')): 495*cda5da8dSAndroid Build Coastguard Worker param = param.strip() 496*cda5da8dSAndroid Build Coastguard Worker 497*cda5da8dSAndroid Build Coastguard Worker key, sep, val = param.partition('=') 498*cda5da8dSAndroid Build Coastguard Worker key = key.strip() 499*cda5da8dSAndroid Build Coastguard Worker 500*cda5da8dSAndroid Build Coastguard Worker if not key: 501*cda5da8dSAndroid Build Coastguard Worker if ii == 0: 502*cda5da8dSAndroid Build Coastguard Worker break 503*cda5da8dSAndroid Build Coastguard Worker else: 504*cda5da8dSAndroid Build Coastguard Worker continue 505*cda5da8dSAndroid Build Coastguard Worker 506*cda5da8dSAndroid Build Coastguard Worker # allow for a distinction between present and empty and missing 507*cda5da8dSAndroid Build Coastguard Worker # altogether 508*cda5da8dSAndroid Build Coastguard Worker val = val.strip() if sep else None 509*cda5da8dSAndroid Build Coastguard Worker 510*cda5da8dSAndroid Build Coastguard Worker if ii != 0: 511*cda5da8dSAndroid Build Coastguard Worker lc = key.lower() 512*cda5da8dSAndroid Build Coastguard Worker if lc in known_attrs: 513*cda5da8dSAndroid Build Coastguard Worker key = lc 514*cda5da8dSAndroid Build Coastguard Worker 515*cda5da8dSAndroid Build Coastguard Worker if key == "version": 516*cda5da8dSAndroid Build Coastguard Worker # This is an RFC 2109 cookie. 517*cda5da8dSAndroid Build Coastguard Worker if val is not None: 518*cda5da8dSAndroid Build Coastguard Worker val = strip_quotes(val) 519*cda5da8dSAndroid Build Coastguard Worker version_set = True 520*cda5da8dSAndroid Build Coastguard Worker elif key == "expires": 521*cda5da8dSAndroid Build Coastguard Worker # convert expires date to seconds since epoch 522*cda5da8dSAndroid Build Coastguard Worker if val is not None: 523*cda5da8dSAndroid Build Coastguard Worker val = http2time(strip_quotes(val)) # None if invalid 524*cda5da8dSAndroid Build Coastguard Worker pairs.append((key, val)) 525*cda5da8dSAndroid Build Coastguard Worker 526*cda5da8dSAndroid Build Coastguard Worker if pairs: 527*cda5da8dSAndroid Build Coastguard Worker if not version_set: 528*cda5da8dSAndroid Build Coastguard Worker pairs.append(("version", "0")) 529*cda5da8dSAndroid Build Coastguard Worker result.append(pairs) 530*cda5da8dSAndroid Build Coastguard Worker 531*cda5da8dSAndroid Build Coastguard Worker return result 532*cda5da8dSAndroid Build Coastguard Worker 533*cda5da8dSAndroid Build Coastguard Worker 534*cda5da8dSAndroid Build Coastguard WorkerIPV4_RE = re.compile(r"\.\d+$", re.ASCII) 535*cda5da8dSAndroid Build Coastguard Workerdef is_HDN(text): 536*cda5da8dSAndroid Build Coastguard Worker """Return True if text is a host domain name.""" 537*cda5da8dSAndroid Build Coastguard Worker # XXX 538*cda5da8dSAndroid Build Coastguard Worker # This may well be wrong. Which RFC is HDN defined in, if any (for 539*cda5da8dSAndroid Build Coastguard Worker # the purposes of RFC 2965)? 540*cda5da8dSAndroid Build Coastguard Worker # For the current implementation, what about IPv6? Remember to look 541*cda5da8dSAndroid Build Coastguard Worker # at other uses of IPV4_RE also, if change this. 542*cda5da8dSAndroid Build Coastguard Worker if IPV4_RE.search(text): 543*cda5da8dSAndroid Build Coastguard Worker return False 544*cda5da8dSAndroid Build Coastguard Worker if text == "": 545*cda5da8dSAndroid Build Coastguard Worker return False 546*cda5da8dSAndroid Build Coastguard Worker if text[0] == "." or text[-1] == ".": 547*cda5da8dSAndroid Build Coastguard Worker return False 548*cda5da8dSAndroid Build Coastguard Worker return True 549*cda5da8dSAndroid Build Coastguard Worker 550*cda5da8dSAndroid Build Coastguard Workerdef domain_match(A, B): 551*cda5da8dSAndroid Build Coastguard Worker """Return True if domain A domain-matches domain B, according to RFC 2965. 552*cda5da8dSAndroid Build Coastguard Worker 553*cda5da8dSAndroid Build Coastguard Worker A and B may be host domain names or IP addresses. 554*cda5da8dSAndroid Build Coastguard Worker 555*cda5da8dSAndroid Build Coastguard Worker RFC 2965, section 1: 556*cda5da8dSAndroid Build Coastguard Worker 557*cda5da8dSAndroid Build Coastguard Worker Host names can be specified either as an IP address or a HDN string. 558*cda5da8dSAndroid Build Coastguard Worker Sometimes we compare one host name with another. (Such comparisons SHALL 559*cda5da8dSAndroid Build Coastguard Worker be case-insensitive.) Host A's name domain-matches host B's if 560*cda5da8dSAndroid Build Coastguard Worker 561*cda5da8dSAndroid Build Coastguard Worker * their host name strings string-compare equal; or 562*cda5da8dSAndroid Build Coastguard Worker 563*cda5da8dSAndroid Build Coastguard Worker * A is a HDN string and has the form NB, where N is a non-empty 564*cda5da8dSAndroid Build Coastguard Worker name string, B has the form .B', and B' is a HDN string. (So, 565*cda5da8dSAndroid Build Coastguard Worker x.y.com domain-matches .Y.com but not Y.com.) 566*cda5da8dSAndroid Build Coastguard Worker 567*cda5da8dSAndroid Build Coastguard Worker Note that domain-match is not a commutative operation: a.b.c.com 568*cda5da8dSAndroid Build Coastguard Worker domain-matches .c.com, but not the reverse. 569*cda5da8dSAndroid Build Coastguard Worker 570*cda5da8dSAndroid Build Coastguard Worker """ 571*cda5da8dSAndroid Build Coastguard Worker # Note that, if A or B are IP addresses, the only relevant part of the 572*cda5da8dSAndroid Build Coastguard Worker # definition of the domain-match algorithm is the direct string-compare. 573*cda5da8dSAndroid Build Coastguard Worker A = A.lower() 574*cda5da8dSAndroid Build Coastguard Worker B = B.lower() 575*cda5da8dSAndroid Build Coastguard Worker if A == B: 576*cda5da8dSAndroid Build Coastguard Worker return True 577*cda5da8dSAndroid Build Coastguard Worker if not is_HDN(A): 578*cda5da8dSAndroid Build Coastguard Worker return False 579*cda5da8dSAndroid Build Coastguard Worker i = A.rfind(B) 580*cda5da8dSAndroid Build Coastguard Worker if i == -1 or i == 0: 581*cda5da8dSAndroid Build Coastguard Worker # A does not have form NB, or N is the empty string 582*cda5da8dSAndroid Build Coastguard Worker return False 583*cda5da8dSAndroid Build Coastguard Worker if not B.startswith("."): 584*cda5da8dSAndroid Build Coastguard Worker return False 585*cda5da8dSAndroid Build Coastguard Worker if not is_HDN(B[1:]): 586*cda5da8dSAndroid Build Coastguard Worker return False 587*cda5da8dSAndroid Build Coastguard Worker return True 588*cda5da8dSAndroid Build Coastguard Worker 589*cda5da8dSAndroid Build Coastguard Workerdef liberal_is_HDN(text): 590*cda5da8dSAndroid Build Coastguard Worker """Return True if text is a sort-of-like a host domain name. 591*cda5da8dSAndroid Build Coastguard Worker 592*cda5da8dSAndroid Build Coastguard Worker For accepting/blocking domains. 593*cda5da8dSAndroid Build Coastguard Worker 594*cda5da8dSAndroid Build Coastguard Worker """ 595*cda5da8dSAndroid Build Coastguard Worker if IPV4_RE.search(text): 596*cda5da8dSAndroid Build Coastguard Worker return False 597*cda5da8dSAndroid Build Coastguard Worker return True 598*cda5da8dSAndroid Build Coastguard Worker 599*cda5da8dSAndroid Build Coastguard Workerdef user_domain_match(A, B): 600*cda5da8dSAndroid Build Coastguard Worker """For blocking/accepting domains. 601*cda5da8dSAndroid Build Coastguard Worker 602*cda5da8dSAndroid Build Coastguard Worker A and B may be host domain names or IP addresses. 603*cda5da8dSAndroid Build Coastguard Worker 604*cda5da8dSAndroid Build Coastguard Worker """ 605*cda5da8dSAndroid Build Coastguard Worker A = A.lower() 606*cda5da8dSAndroid Build Coastguard Worker B = B.lower() 607*cda5da8dSAndroid Build Coastguard Worker if not (liberal_is_HDN(A) and liberal_is_HDN(B)): 608*cda5da8dSAndroid Build Coastguard Worker if A == B: 609*cda5da8dSAndroid Build Coastguard Worker # equal IP addresses 610*cda5da8dSAndroid Build Coastguard Worker return True 611*cda5da8dSAndroid Build Coastguard Worker return False 612*cda5da8dSAndroid Build Coastguard Worker initial_dot = B.startswith(".") 613*cda5da8dSAndroid Build Coastguard Worker if initial_dot and A.endswith(B): 614*cda5da8dSAndroid Build Coastguard Worker return True 615*cda5da8dSAndroid Build Coastguard Worker if not initial_dot and A == B: 616*cda5da8dSAndroid Build Coastguard Worker return True 617*cda5da8dSAndroid Build Coastguard Worker return False 618*cda5da8dSAndroid Build Coastguard Worker 619*cda5da8dSAndroid Build Coastguard Workercut_port_re = re.compile(r":\d+$", re.ASCII) 620*cda5da8dSAndroid Build Coastguard Workerdef request_host(request): 621*cda5da8dSAndroid Build Coastguard Worker """Return request-host, as defined by RFC 2965. 622*cda5da8dSAndroid Build Coastguard Worker 623*cda5da8dSAndroid Build Coastguard Worker Variation from RFC: returned value is lowercased, for convenient 624*cda5da8dSAndroid Build Coastguard Worker comparison. 625*cda5da8dSAndroid Build Coastguard Worker 626*cda5da8dSAndroid Build Coastguard Worker """ 627*cda5da8dSAndroid Build Coastguard Worker url = request.get_full_url() 628*cda5da8dSAndroid Build Coastguard Worker host = urllib.parse.urlparse(url)[1] 629*cda5da8dSAndroid Build Coastguard Worker if host == "": 630*cda5da8dSAndroid Build Coastguard Worker host = request.get_header("Host", "") 631*cda5da8dSAndroid Build Coastguard Worker 632*cda5da8dSAndroid Build Coastguard Worker # remove port, if present 633*cda5da8dSAndroid Build Coastguard Worker host = cut_port_re.sub("", host, 1) 634*cda5da8dSAndroid Build Coastguard Worker return host.lower() 635*cda5da8dSAndroid Build Coastguard Worker 636*cda5da8dSAndroid Build Coastguard Workerdef eff_request_host(request): 637*cda5da8dSAndroid Build Coastguard Worker """Return a tuple (request-host, effective request-host name). 638*cda5da8dSAndroid Build Coastguard Worker 639*cda5da8dSAndroid Build Coastguard Worker As defined by RFC 2965, except both are lowercased. 640*cda5da8dSAndroid Build Coastguard Worker 641*cda5da8dSAndroid Build Coastguard Worker """ 642*cda5da8dSAndroid Build Coastguard Worker erhn = req_host = request_host(request) 643*cda5da8dSAndroid Build Coastguard Worker if req_host.find(".") == -1 and not IPV4_RE.search(req_host): 644*cda5da8dSAndroid Build Coastguard Worker erhn = req_host + ".local" 645*cda5da8dSAndroid Build Coastguard Worker return req_host, erhn 646*cda5da8dSAndroid Build Coastguard Worker 647*cda5da8dSAndroid Build Coastguard Workerdef request_path(request): 648*cda5da8dSAndroid Build Coastguard Worker """Path component of request-URI, as defined by RFC 2965.""" 649*cda5da8dSAndroid Build Coastguard Worker url = request.get_full_url() 650*cda5da8dSAndroid Build Coastguard Worker parts = urllib.parse.urlsplit(url) 651*cda5da8dSAndroid Build Coastguard Worker path = escape_path(parts.path) 652*cda5da8dSAndroid Build Coastguard Worker if not path.startswith("/"): 653*cda5da8dSAndroid Build Coastguard Worker # fix bad RFC 2396 absoluteURI 654*cda5da8dSAndroid Build Coastguard Worker path = "/" + path 655*cda5da8dSAndroid Build Coastguard Worker return path 656*cda5da8dSAndroid Build Coastguard Worker 657*cda5da8dSAndroid Build Coastguard Workerdef request_port(request): 658*cda5da8dSAndroid Build Coastguard Worker host = request.host 659*cda5da8dSAndroid Build Coastguard Worker i = host.find(':') 660*cda5da8dSAndroid Build Coastguard Worker if i >= 0: 661*cda5da8dSAndroid Build Coastguard Worker port = host[i+1:] 662*cda5da8dSAndroid Build Coastguard Worker try: 663*cda5da8dSAndroid Build Coastguard Worker int(port) 664*cda5da8dSAndroid Build Coastguard Worker except ValueError: 665*cda5da8dSAndroid Build Coastguard Worker _debug("nonnumeric port: '%s'", port) 666*cda5da8dSAndroid Build Coastguard Worker return None 667*cda5da8dSAndroid Build Coastguard Worker else: 668*cda5da8dSAndroid Build Coastguard Worker port = DEFAULT_HTTP_PORT 669*cda5da8dSAndroid Build Coastguard Worker return port 670*cda5da8dSAndroid Build Coastguard Worker 671*cda5da8dSAndroid Build Coastguard Worker# Characters in addition to A-Z, a-z, 0-9, '_', '.', and '-' that don't 672*cda5da8dSAndroid Build Coastguard Worker# need to be escaped to form a valid HTTP URL (RFCs 2396 and 1738). 673*cda5da8dSAndroid Build Coastguard WorkerHTTP_PATH_SAFE = "%/;:@&=+$,!~*'()" 674*cda5da8dSAndroid Build Coastguard WorkerESCAPED_CHAR_RE = re.compile(r"%([0-9a-fA-F][0-9a-fA-F])") 675*cda5da8dSAndroid Build Coastguard Workerdef uppercase_escaped_char(match): 676*cda5da8dSAndroid Build Coastguard Worker return "%%%s" % match.group(1).upper() 677*cda5da8dSAndroid Build Coastguard Workerdef escape_path(path): 678*cda5da8dSAndroid Build Coastguard Worker """Escape any invalid characters in HTTP URL, and uppercase all escapes.""" 679*cda5da8dSAndroid Build Coastguard Worker # There's no knowing what character encoding was used to create URLs 680*cda5da8dSAndroid Build Coastguard Worker # containing %-escapes, but since we have to pick one to escape invalid 681*cda5da8dSAndroid Build Coastguard Worker # path characters, we pick UTF-8, as recommended in the HTML 4.0 682*cda5da8dSAndroid Build Coastguard Worker # specification: 683*cda5da8dSAndroid Build Coastguard Worker # http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.2.1 684*cda5da8dSAndroid Build Coastguard Worker # And here, kind of: draft-fielding-uri-rfc2396bis-03 685*cda5da8dSAndroid Build Coastguard Worker # (And in draft IRI specification: draft-duerst-iri-05) 686*cda5da8dSAndroid Build Coastguard Worker # (And here, for new URI schemes: RFC 2718) 687*cda5da8dSAndroid Build Coastguard Worker path = urllib.parse.quote(path, HTTP_PATH_SAFE) 688*cda5da8dSAndroid Build Coastguard Worker path = ESCAPED_CHAR_RE.sub(uppercase_escaped_char, path) 689*cda5da8dSAndroid Build Coastguard Worker return path 690*cda5da8dSAndroid Build Coastguard Worker 691*cda5da8dSAndroid Build Coastguard Workerdef reach(h): 692*cda5da8dSAndroid Build Coastguard Worker """Return reach of host h, as defined by RFC 2965, section 1. 693*cda5da8dSAndroid Build Coastguard Worker 694*cda5da8dSAndroid Build Coastguard Worker The reach R of a host name H is defined as follows: 695*cda5da8dSAndroid Build Coastguard Worker 696*cda5da8dSAndroid Build Coastguard Worker * If 697*cda5da8dSAndroid Build Coastguard Worker 698*cda5da8dSAndroid Build Coastguard Worker - H is the host domain name of a host; and, 699*cda5da8dSAndroid Build Coastguard Worker 700*cda5da8dSAndroid Build Coastguard Worker - H has the form A.B; and 701*cda5da8dSAndroid Build Coastguard Worker 702*cda5da8dSAndroid Build Coastguard Worker - A has no embedded (that is, interior) dots; and 703*cda5da8dSAndroid Build Coastguard Worker 704*cda5da8dSAndroid Build Coastguard Worker - B has at least one embedded dot, or B is the string "local". 705*cda5da8dSAndroid Build Coastguard Worker then the reach of H is .B. 706*cda5da8dSAndroid Build Coastguard Worker 707*cda5da8dSAndroid Build Coastguard Worker * Otherwise, the reach of H is H. 708*cda5da8dSAndroid Build Coastguard Worker 709*cda5da8dSAndroid Build Coastguard Worker >>> reach("www.acme.com") 710*cda5da8dSAndroid Build Coastguard Worker '.acme.com' 711*cda5da8dSAndroid Build Coastguard Worker >>> reach("acme.com") 712*cda5da8dSAndroid Build Coastguard Worker 'acme.com' 713*cda5da8dSAndroid Build Coastguard Worker >>> reach("acme.local") 714*cda5da8dSAndroid Build Coastguard Worker '.local' 715*cda5da8dSAndroid Build Coastguard Worker 716*cda5da8dSAndroid Build Coastguard Worker """ 717*cda5da8dSAndroid Build Coastguard Worker i = h.find(".") 718*cda5da8dSAndroid Build Coastguard Worker if i >= 0: 719*cda5da8dSAndroid Build Coastguard Worker #a = h[:i] # this line is only here to show what a is 720*cda5da8dSAndroid Build Coastguard Worker b = h[i+1:] 721*cda5da8dSAndroid Build Coastguard Worker i = b.find(".") 722*cda5da8dSAndroid Build Coastguard Worker if is_HDN(h) and (i >= 0 or b == "local"): 723*cda5da8dSAndroid Build Coastguard Worker return "."+b 724*cda5da8dSAndroid Build Coastguard Worker return h 725*cda5da8dSAndroid Build Coastguard Worker 726*cda5da8dSAndroid Build Coastguard Workerdef is_third_party(request): 727*cda5da8dSAndroid Build Coastguard Worker """ 728*cda5da8dSAndroid Build Coastguard Worker 729*cda5da8dSAndroid Build Coastguard Worker RFC 2965, section 3.3.6: 730*cda5da8dSAndroid Build Coastguard Worker 731*cda5da8dSAndroid Build Coastguard Worker An unverifiable transaction is to a third-party host if its request- 732*cda5da8dSAndroid Build Coastguard Worker host U does not domain-match the reach R of the request-host O in the 733*cda5da8dSAndroid Build Coastguard Worker origin transaction. 734*cda5da8dSAndroid Build Coastguard Worker 735*cda5da8dSAndroid Build Coastguard Worker """ 736*cda5da8dSAndroid Build Coastguard Worker req_host = request_host(request) 737*cda5da8dSAndroid Build Coastguard Worker if not domain_match(req_host, reach(request.origin_req_host)): 738*cda5da8dSAndroid Build Coastguard Worker return True 739*cda5da8dSAndroid Build Coastguard Worker else: 740*cda5da8dSAndroid Build Coastguard Worker return False 741*cda5da8dSAndroid Build Coastguard Worker 742*cda5da8dSAndroid Build Coastguard Worker 743*cda5da8dSAndroid Build Coastguard Workerclass Cookie: 744*cda5da8dSAndroid Build Coastguard Worker """HTTP Cookie. 745*cda5da8dSAndroid Build Coastguard Worker 746*cda5da8dSAndroid Build Coastguard Worker This class represents both Netscape and RFC 2965 cookies. 747*cda5da8dSAndroid Build Coastguard Worker 748*cda5da8dSAndroid Build Coastguard Worker This is deliberately a very simple class. It just holds attributes. It's 749*cda5da8dSAndroid Build Coastguard Worker possible to construct Cookie instances that don't comply with the cookie 750*cda5da8dSAndroid Build Coastguard Worker standards. CookieJar.make_cookies is the factory function for Cookie 751*cda5da8dSAndroid Build Coastguard Worker objects -- it deals with cookie parsing, supplying defaults, and 752*cda5da8dSAndroid Build Coastguard Worker normalising to the representation used in this class. CookiePolicy is 753*cda5da8dSAndroid Build Coastguard Worker responsible for checking them to see whether they should be accepted from 754*cda5da8dSAndroid Build Coastguard Worker and returned to the server. 755*cda5da8dSAndroid Build Coastguard Worker 756*cda5da8dSAndroid Build Coastguard Worker Note that the port may be present in the headers, but unspecified ("Port" 757*cda5da8dSAndroid Build Coastguard Worker rather than"Port=80", for example); if this is the case, port is None. 758*cda5da8dSAndroid Build Coastguard Worker 759*cda5da8dSAndroid Build Coastguard Worker """ 760*cda5da8dSAndroid Build Coastguard Worker 761*cda5da8dSAndroid Build Coastguard Worker def __init__(self, version, name, value, 762*cda5da8dSAndroid Build Coastguard Worker port, port_specified, 763*cda5da8dSAndroid Build Coastguard Worker domain, domain_specified, domain_initial_dot, 764*cda5da8dSAndroid Build Coastguard Worker path, path_specified, 765*cda5da8dSAndroid Build Coastguard Worker secure, 766*cda5da8dSAndroid Build Coastguard Worker expires, 767*cda5da8dSAndroid Build Coastguard Worker discard, 768*cda5da8dSAndroid Build Coastguard Worker comment, 769*cda5da8dSAndroid Build Coastguard Worker comment_url, 770*cda5da8dSAndroid Build Coastguard Worker rest, 771*cda5da8dSAndroid Build Coastguard Worker rfc2109=False, 772*cda5da8dSAndroid Build Coastguard Worker ): 773*cda5da8dSAndroid Build Coastguard Worker 774*cda5da8dSAndroid Build Coastguard Worker if version is not None: version = int(version) 775*cda5da8dSAndroid Build Coastguard Worker if expires is not None: expires = int(float(expires)) 776*cda5da8dSAndroid Build Coastguard Worker if port is None and port_specified is True: 777*cda5da8dSAndroid Build Coastguard Worker raise ValueError("if port is None, port_specified must be false") 778*cda5da8dSAndroid Build Coastguard Worker 779*cda5da8dSAndroid Build Coastguard Worker self.version = version 780*cda5da8dSAndroid Build Coastguard Worker self.name = name 781*cda5da8dSAndroid Build Coastguard Worker self.value = value 782*cda5da8dSAndroid Build Coastguard Worker self.port = port 783*cda5da8dSAndroid Build Coastguard Worker self.port_specified = port_specified 784*cda5da8dSAndroid Build Coastguard Worker # normalise case, as per RFC 2965 section 3.3.3 785*cda5da8dSAndroid Build Coastguard Worker self.domain = domain.lower() 786*cda5da8dSAndroid Build Coastguard Worker self.domain_specified = domain_specified 787*cda5da8dSAndroid Build Coastguard Worker # Sigh. We need to know whether the domain given in the 788*cda5da8dSAndroid Build Coastguard Worker # cookie-attribute had an initial dot, in order to follow RFC 2965 789*cda5da8dSAndroid Build Coastguard Worker # (as clarified in draft errata). Needed for the returned $Domain 790*cda5da8dSAndroid Build Coastguard Worker # value. 791*cda5da8dSAndroid Build Coastguard Worker self.domain_initial_dot = domain_initial_dot 792*cda5da8dSAndroid Build Coastguard Worker self.path = path 793*cda5da8dSAndroid Build Coastguard Worker self.path_specified = path_specified 794*cda5da8dSAndroid Build Coastguard Worker self.secure = secure 795*cda5da8dSAndroid Build Coastguard Worker self.expires = expires 796*cda5da8dSAndroid Build Coastguard Worker self.discard = discard 797*cda5da8dSAndroid Build Coastguard Worker self.comment = comment 798*cda5da8dSAndroid Build Coastguard Worker self.comment_url = comment_url 799*cda5da8dSAndroid Build Coastguard Worker self.rfc2109 = rfc2109 800*cda5da8dSAndroid Build Coastguard Worker 801*cda5da8dSAndroid Build Coastguard Worker self._rest = copy.copy(rest) 802*cda5da8dSAndroid Build Coastguard Worker 803*cda5da8dSAndroid Build Coastguard Worker def has_nonstandard_attr(self, name): 804*cda5da8dSAndroid Build Coastguard Worker return name in self._rest 805*cda5da8dSAndroid Build Coastguard Worker def get_nonstandard_attr(self, name, default=None): 806*cda5da8dSAndroid Build Coastguard Worker return self._rest.get(name, default) 807*cda5da8dSAndroid Build Coastguard Worker def set_nonstandard_attr(self, name, value): 808*cda5da8dSAndroid Build Coastguard Worker self._rest[name] = value 809*cda5da8dSAndroid Build Coastguard Worker 810*cda5da8dSAndroid Build Coastguard Worker def is_expired(self, now=None): 811*cda5da8dSAndroid Build Coastguard Worker if now is None: now = time.time() 812*cda5da8dSAndroid Build Coastguard Worker if (self.expires is not None) and (self.expires <= now): 813*cda5da8dSAndroid Build Coastguard Worker return True 814*cda5da8dSAndroid Build Coastguard Worker return False 815*cda5da8dSAndroid Build Coastguard Worker 816*cda5da8dSAndroid Build Coastguard Worker def __str__(self): 817*cda5da8dSAndroid Build Coastguard Worker if self.port is None: p = "" 818*cda5da8dSAndroid Build Coastguard Worker else: p = ":"+self.port 819*cda5da8dSAndroid Build Coastguard Worker limit = self.domain + p + self.path 820*cda5da8dSAndroid Build Coastguard Worker if self.value is not None: 821*cda5da8dSAndroid Build Coastguard Worker namevalue = "%s=%s" % (self.name, self.value) 822*cda5da8dSAndroid Build Coastguard Worker else: 823*cda5da8dSAndroid Build Coastguard Worker namevalue = self.name 824*cda5da8dSAndroid Build Coastguard Worker return "<Cookie %s for %s>" % (namevalue, limit) 825*cda5da8dSAndroid Build Coastguard Worker 826*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 827*cda5da8dSAndroid Build Coastguard Worker args = [] 828*cda5da8dSAndroid Build Coastguard Worker for name in ("version", "name", "value", 829*cda5da8dSAndroid Build Coastguard Worker "port", "port_specified", 830*cda5da8dSAndroid Build Coastguard Worker "domain", "domain_specified", "domain_initial_dot", 831*cda5da8dSAndroid Build Coastguard Worker "path", "path_specified", 832*cda5da8dSAndroid Build Coastguard Worker "secure", "expires", "discard", "comment", "comment_url", 833*cda5da8dSAndroid Build Coastguard Worker ): 834*cda5da8dSAndroid Build Coastguard Worker attr = getattr(self, name) 835*cda5da8dSAndroid Build Coastguard Worker args.append("%s=%s" % (name, repr(attr))) 836*cda5da8dSAndroid Build Coastguard Worker args.append("rest=%s" % repr(self._rest)) 837*cda5da8dSAndroid Build Coastguard Worker args.append("rfc2109=%s" % repr(self.rfc2109)) 838*cda5da8dSAndroid Build Coastguard Worker return "%s(%s)" % (self.__class__.__name__, ", ".join(args)) 839*cda5da8dSAndroid Build Coastguard Worker 840*cda5da8dSAndroid Build Coastguard Worker 841*cda5da8dSAndroid Build Coastguard Workerclass CookiePolicy: 842*cda5da8dSAndroid Build Coastguard Worker """Defines which cookies get accepted from and returned to server. 843*cda5da8dSAndroid Build Coastguard Worker 844*cda5da8dSAndroid Build Coastguard Worker May also modify cookies, though this is probably a bad idea. 845*cda5da8dSAndroid Build Coastguard Worker 846*cda5da8dSAndroid Build Coastguard Worker The subclass DefaultCookiePolicy defines the standard rules for Netscape 847*cda5da8dSAndroid Build Coastguard Worker and RFC 2965 cookies -- override that if you want a customized policy. 848*cda5da8dSAndroid Build Coastguard Worker 849*cda5da8dSAndroid Build Coastguard Worker """ 850*cda5da8dSAndroid Build Coastguard Worker def set_ok(self, cookie, request): 851*cda5da8dSAndroid Build Coastguard Worker """Return true if (and only if) cookie should be accepted from server. 852*cda5da8dSAndroid Build Coastguard Worker 853*cda5da8dSAndroid Build Coastguard Worker Currently, pre-expired cookies never get this far -- the CookieJar 854*cda5da8dSAndroid Build Coastguard Worker class deletes such cookies itself. 855*cda5da8dSAndroid Build Coastguard Worker 856*cda5da8dSAndroid Build Coastguard Worker """ 857*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError() 858*cda5da8dSAndroid Build Coastguard Worker 859*cda5da8dSAndroid Build Coastguard Worker def return_ok(self, cookie, request): 860*cda5da8dSAndroid Build Coastguard Worker """Return true if (and only if) cookie should be returned to server.""" 861*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError() 862*cda5da8dSAndroid Build Coastguard Worker 863*cda5da8dSAndroid Build Coastguard Worker def domain_return_ok(self, domain, request): 864*cda5da8dSAndroid Build Coastguard Worker """Return false if cookies should not be returned, given cookie domain. 865*cda5da8dSAndroid Build Coastguard Worker """ 866*cda5da8dSAndroid Build Coastguard Worker return True 867*cda5da8dSAndroid Build Coastguard Worker 868*cda5da8dSAndroid Build Coastguard Worker def path_return_ok(self, path, request): 869*cda5da8dSAndroid Build Coastguard Worker """Return false if cookies should not be returned, given cookie path. 870*cda5da8dSAndroid Build Coastguard Worker """ 871*cda5da8dSAndroid Build Coastguard Worker return True 872*cda5da8dSAndroid Build Coastguard Worker 873*cda5da8dSAndroid Build Coastguard Worker 874*cda5da8dSAndroid Build Coastguard Workerclass DefaultCookiePolicy(CookiePolicy): 875*cda5da8dSAndroid Build Coastguard Worker """Implements the standard rules for accepting and returning cookies.""" 876*cda5da8dSAndroid Build Coastguard Worker 877*cda5da8dSAndroid Build Coastguard Worker DomainStrictNoDots = 1 878*cda5da8dSAndroid Build Coastguard Worker DomainStrictNonDomain = 2 879*cda5da8dSAndroid Build Coastguard Worker DomainRFC2965Match = 4 880*cda5da8dSAndroid Build Coastguard Worker 881*cda5da8dSAndroid Build Coastguard Worker DomainLiberal = 0 882*cda5da8dSAndroid Build Coastguard Worker DomainStrict = DomainStrictNoDots|DomainStrictNonDomain 883*cda5da8dSAndroid Build Coastguard Worker 884*cda5da8dSAndroid Build Coastguard Worker def __init__(self, 885*cda5da8dSAndroid Build Coastguard Worker blocked_domains=None, allowed_domains=None, 886*cda5da8dSAndroid Build Coastguard Worker netscape=True, rfc2965=False, 887*cda5da8dSAndroid Build Coastguard Worker rfc2109_as_netscape=None, 888*cda5da8dSAndroid Build Coastguard Worker hide_cookie2=False, 889*cda5da8dSAndroid Build Coastguard Worker strict_domain=False, 890*cda5da8dSAndroid Build Coastguard Worker strict_rfc2965_unverifiable=True, 891*cda5da8dSAndroid Build Coastguard Worker strict_ns_unverifiable=False, 892*cda5da8dSAndroid Build Coastguard Worker strict_ns_domain=DomainLiberal, 893*cda5da8dSAndroid Build Coastguard Worker strict_ns_set_initial_dollar=False, 894*cda5da8dSAndroid Build Coastguard Worker strict_ns_set_path=False, 895*cda5da8dSAndroid Build Coastguard Worker secure_protocols=("https", "wss") 896*cda5da8dSAndroid Build Coastguard Worker ): 897*cda5da8dSAndroid Build Coastguard Worker """Constructor arguments should be passed as keyword arguments only.""" 898*cda5da8dSAndroid Build Coastguard Worker self.netscape = netscape 899*cda5da8dSAndroid Build Coastguard Worker self.rfc2965 = rfc2965 900*cda5da8dSAndroid Build Coastguard Worker self.rfc2109_as_netscape = rfc2109_as_netscape 901*cda5da8dSAndroid Build Coastguard Worker self.hide_cookie2 = hide_cookie2 902*cda5da8dSAndroid Build Coastguard Worker self.strict_domain = strict_domain 903*cda5da8dSAndroid Build Coastguard Worker self.strict_rfc2965_unverifiable = strict_rfc2965_unverifiable 904*cda5da8dSAndroid Build Coastguard Worker self.strict_ns_unverifiable = strict_ns_unverifiable 905*cda5da8dSAndroid Build Coastguard Worker self.strict_ns_domain = strict_ns_domain 906*cda5da8dSAndroid Build Coastguard Worker self.strict_ns_set_initial_dollar = strict_ns_set_initial_dollar 907*cda5da8dSAndroid Build Coastguard Worker self.strict_ns_set_path = strict_ns_set_path 908*cda5da8dSAndroid Build Coastguard Worker self.secure_protocols = secure_protocols 909*cda5da8dSAndroid Build Coastguard Worker 910*cda5da8dSAndroid Build Coastguard Worker if blocked_domains is not None: 911*cda5da8dSAndroid Build Coastguard Worker self._blocked_domains = tuple(blocked_domains) 912*cda5da8dSAndroid Build Coastguard Worker else: 913*cda5da8dSAndroid Build Coastguard Worker self._blocked_domains = () 914*cda5da8dSAndroid Build Coastguard Worker 915*cda5da8dSAndroid Build Coastguard Worker if allowed_domains is not None: 916*cda5da8dSAndroid Build Coastguard Worker allowed_domains = tuple(allowed_domains) 917*cda5da8dSAndroid Build Coastguard Worker self._allowed_domains = allowed_domains 918*cda5da8dSAndroid Build Coastguard Worker 919*cda5da8dSAndroid Build Coastguard Worker def blocked_domains(self): 920*cda5da8dSAndroid Build Coastguard Worker """Return the sequence of blocked domains (as a tuple).""" 921*cda5da8dSAndroid Build Coastguard Worker return self._blocked_domains 922*cda5da8dSAndroid Build Coastguard Worker def set_blocked_domains(self, blocked_domains): 923*cda5da8dSAndroid Build Coastguard Worker """Set the sequence of blocked domains.""" 924*cda5da8dSAndroid Build Coastguard Worker self._blocked_domains = tuple(blocked_domains) 925*cda5da8dSAndroid Build Coastguard Worker 926*cda5da8dSAndroid Build Coastguard Worker def is_blocked(self, domain): 927*cda5da8dSAndroid Build Coastguard Worker for blocked_domain in self._blocked_domains: 928*cda5da8dSAndroid Build Coastguard Worker if user_domain_match(domain, blocked_domain): 929*cda5da8dSAndroid Build Coastguard Worker return True 930*cda5da8dSAndroid Build Coastguard Worker return False 931*cda5da8dSAndroid Build Coastguard Worker 932*cda5da8dSAndroid Build Coastguard Worker def allowed_domains(self): 933*cda5da8dSAndroid Build Coastguard Worker """Return None, or the sequence of allowed domains (as a tuple).""" 934*cda5da8dSAndroid Build Coastguard Worker return self._allowed_domains 935*cda5da8dSAndroid Build Coastguard Worker def set_allowed_domains(self, allowed_domains): 936*cda5da8dSAndroid Build Coastguard Worker """Set the sequence of allowed domains, or None.""" 937*cda5da8dSAndroid Build Coastguard Worker if allowed_domains is not None: 938*cda5da8dSAndroid Build Coastguard Worker allowed_domains = tuple(allowed_domains) 939*cda5da8dSAndroid Build Coastguard Worker self._allowed_domains = allowed_domains 940*cda5da8dSAndroid Build Coastguard Worker 941*cda5da8dSAndroid Build Coastguard Worker def is_not_allowed(self, domain): 942*cda5da8dSAndroid Build Coastguard Worker if self._allowed_domains is None: 943*cda5da8dSAndroid Build Coastguard Worker return False 944*cda5da8dSAndroid Build Coastguard Worker for allowed_domain in self._allowed_domains: 945*cda5da8dSAndroid Build Coastguard Worker if user_domain_match(domain, allowed_domain): 946*cda5da8dSAndroid Build Coastguard Worker return False 947*cda5da8dSAndroid Build Coastguard Worker return True 948*cda5da8dSAndroid Build Coastguard Worker 949*cda5da8dSAndroid Build Coastguard Worker def set_ok(self, cookie, request): 950*cda5da8dSAndroid Build Coastguard Worker """ 951*cda5da8dSAndroid Build Coastguard Worker If you override .set_ok(), be sure to call this method. If it returns 952*cda5da8dSAndroid Build Coastguard Worker false, so should your subclass (assuming your subclass wants to be more 953*cda5da8dSAndroid Build Coastguard Worker strict about which cookies to accept). 954*cda5da8dSAndroid Build Coastguard Worker 955*cda5da8dSAndroid Build Coastguard Worker """ 956*cda5da8dSAndroid Build Coastguard Worker _debug(" - checking cookie %s=%s", cookie.name, cookie.value) 957*cda5da8dSAndroid Build Coastguard Worker 958*cda5da8dSAndroid Build Coastguard Worker assert cookie.name is not None 959*cda5da8dSAndroid Build Coastguard Worker 960*cda5da8dSAndroid Build Coastguard Worker for n in "version", "verifiability", "name", "path", "domain", "port": 961*cda5da8dSAndroid Build Coastguard Worker fn_name = "set_ok_"+n 962*cda5da8dSAndroid Build Coastguard Worker fn = getattr(self, fn_name) 963*cda5da8dSAndroid Build Coastguard Worker if not fn(cookie, request): 964*cda5da8dSAndroid Build Coastguard Worker return False 965*cda5da8dSAndroid Build Coastguard Worker 966*cda5da8dSAndroid Build Coastguard Worker return True 967*cda5da8dSAndroid Build Coastguard Worker 968*cda5da8dSAndroid Build Coastguard Worker def set_ok_version(self, cookie, request): 969*cda5da8dSAndroid Build Coastguard Worker if cookie.version is None: 970*cda5da8dSAndroid Build Coastguard Worker # Version is always set to 0 by parse_ns_headers if it's a Netscape 971*cda5da8dSAndroid Build Coastguard Worker # cookie, so this must be an invalid RFC 2965 cookie. 972*cda5da8dSAndroid Build Coastguard Worker _debug(" Set-Cookie2 without version attribute (%s=%s)", 973*cda5da8dSAndroid Build Coastguard Worker cookie.name, cookie.value) 974*cda5da8dSAndroid Build Coastguard Worker return False 975*cda5da8dSAndroid Build Coastguard Worker if cookie.version > 0 and not self.rfc2965: 976*cda5da8dSAndroid Build Coastguard Worker _debug(" RFC 2965 cookies are switched off") 977*cda5da8dSAndroid Build Coastguard Worker return False 978*cda5da8dSAndroid Build Coastguard Worker elif cookie.version == 0 and not self.netscape: 979*cda5da8dSAndroid Build Coastguard Worker _debug(" Netscape cookies are switched off") 980*cda5da8dSAndroid Build Coastguard Worker return False 981*cda5da8dSAndroid Build Coastguard Worker return True 982*cda5da8dSAndroid Build Coastguard Worker 983*cda5da8dSAndroid Build Coastguard Worker def set_ok_verifiability(self, cookie, request): 984*cda5da8dSAndroid Build Coastguard Worker if request.unverifiable and is_third_party(request): 985*cda5da8dSAndroid Build Coastguard Worker if cookie.version > 0 and self.strict_rfc2965_unverifiable: 986*cda5da8dSAndroid Build Coastguard Worker _debug(" third-party RFC 2965 cookie during " 987*cda5da8dSAndroid Build Coastguard Worker "unverifiable transaction") 988*cda5da8dSAndroid Build Coastguard Worker return False 989*cda5da8dSAndroid Build Coastguard Worker elif cookie.version == 0 and self.strict_ns_unverifiable: 990*cda5da8dSAndroid Build Coastguard Worker _debug(" third-party Netscape cookie during " 991*cda5da8dSAndroid Build Coastguard Worker "unverifiable transaction") 992*cda5da8dSAndroid Build Coastguard Worker return False 993*cda5da8dSAndroid Build Coastguard Worker return True 994*cda5da8dSAndroid Build Coastguard Worker 995*cda5da8dSAndroid Build Coastguard Worker def set_ok_name(self, cookie, request): 996*cda5da8dSAndroid Build Coastguard Worker # Try and stop servers setting V0 cookies designed to hack other 997*cda5da8dSAndroid Build Coastguard Worker # servers that know both V0 and V1 protocols. 998*cda5da8dSAndroid Build Coastguard Worker if (cookie.version == 0 and self.strict_ns_set_initial_dollar and 999*cda5da8dSAndroid Build Coastguard Worker cookie.name.startswith("$")): 1000*cda5da8dSAndroid Build Coastguard Worker _debug(" illegal name (starts with '$'): '%s'", cookie.name) 1001*cda5da8dSAndroid Build Coastguard Worker return False 1002*cda5da8dSAndroid Build Coastguard Worker return True 1003*cda5da8dSAndroid Build Coastguard Worker 1004*cda5da8dSAndroid Build Coastguard Worker def set_ok_path(self, cookie, request): 1005*cda5da8dSAndroid Build Coastguard Worker if cookie.path_specified: 1006*cda5da8dSAndroid Build Coastguard Worker req_path = request_path(request) 1007*cda5da8dSAndroid Build Coastguard Worker if ((cookie.version > 0 or 1008*cda5da8dSAndroid Build Coastguard Worker (cookie.version == 0 and self.strict_ns_set_path)) and 1009*cda5da8dSAndroid Build Coastguard Worker not self.path_return_ok(cookie.path, request)): 1010*cda5da8dSAndroid Build Coastguard Worker _debug(" path attribute %s is not a prefix of request " 1011*cda5da8dSAndroid Build Coastguard Worker "path %s", cookie.path, req_path) 1012*cda5da8dSAndroid Build Coastguard Worker return False 1013*cda5da8dSAndroid Build Coastguard Worker return True 1014*cda5da8dSAndroid Build Coastguard Worker 1015*cda5da8dSAndroid Build Coastguard Worker def set_ok_domain(self, cookie, request): 1016*cda5da8dSAndroid Build Coastguard Worker if self.is_blocked(cookie.domain): 1017*cda5da8dSAndroid Build Coastguard Worker _debug(" domain %s is in user block-list", cookie.domain) 1018*cda5da8dSAndroid Build Coastguard Worker return False 1019*cda5da8dSAndroid Build Coastguard Worker if self.is_not_allowed(cookie.domain): 1020*cda5da8dSAndroid Build Coastguard Worker _debug(" domain %s is not in user allow-list", cookie.domain) 1021*cda5da8dSAndroid Build Coastguard Worker return False 1022*cda5da8dSAndroid Build Coastguard Worker if cookie.domain_specified: 1023*cda5da8dSAndroid Build Coastguard Worker req_host, erhn = eff_request_host(request) 1024*cda5da8dSAndroid Build Coastguard Worker domain = cookie.domain 1025*cda5da8dSAndroid Build Coastguard Worker if self.strict_domain and (domain.count(".") >= 2): 1026*cda5da8dSAndroid Build Coastguard Worker # XXX This should probably be compared with the Konqueror 1027*cda5da8dSAndroid Build Coastguard Worker # (kcookiejar.cpp) and Mozilla implementations, but it's a 1028*cda5da8dSAndroid Build Coastguard Worker # losing battle. 1029*cda5da8dSAndroid Build Coastguard Worker i = domain.rfind(".") 1030*cda5da8dSAndroid Build Coastguard Worker j = domain.rfind(".", 0, i) 1031*cda5da8dSAndroid Build Coastguard Worker if j == 0: # domain like .foo.bar 1032*cda5da8dSAndroid Build Coastguard Worker tld = domain[i+1:] 1033*cda5da8dSAndroid Build Coastguard Worker sld = domain[j+1:i] 1034*cda5da8dSAndroid Build Coastguard Worker if sld.lower() in ("co", "ac", "com", "edu", "org", "net", 1035*cda5da8dSAndroid Build Coastguard Worker "gov", "mil", "int", "aero", "biz", "cat", "coop", 1036*cda5da8dSAndroid Build Coastguard Worker "info", "jobs", "mobi", "museum", "name", "pro", 1037*cda5da8dSAndroid Build Coastguard Worker "travel", "eu") and len(tld) == 2: 1038*cda5da8dSAndroid Build Coastguard Worker # domain like .co.uk 1039*cda5da8dSAndroid Build Coastguard Worker _debug(" country-code second level domain %s", domain) 1040*cda5da8dSAndroid Build Coastguard Worker return False 1041*cda5da8dSAndroid Build Coastguard Worker if domain.startswith("."): 1042*cda5da8dSAndroid Build Coastguard Worker undotted_domain = domain[1:] 1043*cda5da8dSAndroid Build Coastguard Worker else: 1044*cda5da8dSAndroid Build Coastguard Worker undotted_domain = domain 1045*cda5da8dSAndroid Build Coastguard Worker embedded_dots = (undotted_domain.find(".") >= 0) 1046*cda5da8dSAndroid Build Coastguard Worker if not embedded_dots and not erhn.endswith(".local"): 1047*cda5da8dSAndroid Build Coastguard Worker _debug(" non-local domain %s contains no embedded dot", 1048*cda5da8dSAndroid Build Coastguard Worker domain) 1049*cda5da8dSAndroid Build Coastguard Worker return False 1050*cda5da8dSAndroid Build Coastguard Worker if cookie.version == 0: 1051*cda5da8dSAndroid Build Coastguard Worker if (not (erhn.endswith(domain) or 1052*cda5da8dSAndroid Build Coastguard Worker erhn.endswith(f"{undotted_domain}.local")) and 1053*cda5da8dSAndroid Build Coastguard Worker (not erhn.startswith(".") and 1054*cda5da8dSAndroid Build Coastguard Worker not ("."+erhn).endswith(domain))): 1055*cda5da8dSAndroid Build Coastguard Worker _debug(" effective request-host %s (even with added " 1056*cda5da8dSAndroid Build Coastguard Worker "initial dot) does not end with %s", 1057*cda5da8dSAndroid Build Coastguard Worker erhn, domain) 1058*cda5da8dSAndroid Build Coastguard Worker return False 1059*cda5da8dSAndroid Build Coastguard Worker if (cookie.version > 0 or 1060*cda5da8dSAndroid Build Coastguard Worker (self.strict_ns_domain & self.DomainRFC2965Match)): 1061*cda5da8dSAndroid Build Coastguard Worker if not domain_match(erhn, domain): 1062*cda5da8dSAndroid Build Coastguard Worker _debug(" effective request-host %s does not domain-match " 1063*cda5da8dSAndroid Build Coastguard Worker "%s", erhn, domain) 1064*cda5da8dSAndroid Build Coastguard Worker return False 1065*cda5da8dSAndroid Build Coastguard Worker if (cookie.version > 0 or 1066*cda5da8dSAndroid Build Coastguard Worker (self.strict_ns_domain & self.DomainStrictNoDots)): 1067*cda5da8dSAndroid Build Coastguard Worker host_prefix = req_host[:-len(domain)] 1068*cda5da8dSAndroid Build Coastguard Worker if (host_prefix.find(".") >= 0 and 1069*cda5da8dSAndroid Build Coastguard Worker not IPV4_RE.search(req_host)): 1070*cda5da8dSAndroid Build Coastguard Worker _debug(" host prefix %s for domain %s contains a dot", 1071*cda5da8dSAndroid Build Coastguard Worker host_prefix, domain) 1072*cda5da8dSAndroid Build Coastguard Worker return False 1073*cda5da8dSAndroid Build Coastguard Worker return True 1074*cda5da8dSAndroid Build Coastguard Worker 1075*cda5da8dSAndroid Build Coastguard Worker def set_ok_port(self, cookie, request): 1076*cda5da8dSAndroid Build Coastguard Worker if cookie.port_specified: 1077*cda5da8dSAndroid Build Coastguard Worker req_port = request_port(request) 1078*cda5da8dSAndroid Build Coastguard Worker if req_port is None: 1079*cda5da8dSAndroid Build Coastguard Worker req_port = "80" 1080*cda5da8dSAndroid Build Coastguard Worker else: 1081*cda5da8dSAndroid Build Coastguard Worker req_port = str(req_port) 1082*cda5da8dSAndroid Build Coastguard Worker for p in cookie.port.split(","): 1083*cda5da8dSAndroid Build Coastguard Worker try: 1084*cda5da8dSAndroid Build Coastguard Worker int(p) 1085*cda5da8dSAndroid Build Coastguard Worker except ValueError: 1086*cda5da8dSAndroid Build Coastguard Worker _debug(" bad port %s (not numeric)", p) 1087*cda5da8dSAndroid Build Coastguard Worker return False 1088*cda5da8dSAndroid Build Coastguard Worker if p == req_port: 1089*cda5da8dSAndroid Build Coastguard Worker break 1090*cda5da8dSAndroid Build Coastguard Worker else: 1091*cda5da8dSAndroid Build Coastguard Worker _debug(" request port (%s) not found in %s", 1092*cda5da8dSAndroid Build Coastguard Worker req_port, cookie.port) 1093*cda5da8dSAndroid Build Coastguard Worker return False 1094*cda5da8dSAndroid Build Coastguard Worker return True 1095*cda5da8dSAndroid Build Coastguard Worker 1096*cda5da8dSAndroid Build Coastguard Worker def return_ok(self, cookie, request): 1097*cda5da8dSAndroid Build Coastguard Worker """ 1098*cda5da8dSAndroid Build Coastguard Worker If you override .return_ok(), be sure to call this method. If it 1099*cda5da8dSAndroid Build Coastguard Worker returns false, so should your subclass (assuming your subclass wants to 1100*cda5da8dSAndroid Build Coastguard Worker be more strict about which cookies to return). 1101*cda5da8dSAndroid Build Coastguard Worker 1102*cda5da8dSAndroid Build Coastguard Worker """ 1103*cda5da8dSAndroid Build Coastguard Worker # Path has already been checked by .path_return_ok(), and domain 1104*cda5da8dSAndroid Build Coastguard Worker # blocking done by .domain_return_ok(). 1105*cda5da8dSAndroid Build Coastguard Worker _debug(" - checking cookie %s=%s", cookie.name, cookie.value) 1106*cda5da8dSAndroid Build Coastguard Worker 1107*cda5da8dSAndroid Build Coastguard Worker for n in "version", "verifiability", "secure", "expires", "port", "domain": 1108*cda5da8dSAndroid Build Coastguard Worker fn_name = "return_ok_"+n 1109*cda5da8dSAndroid Build Coastguard Worker fn = getattr(self, fn_name) 1110*cda5da8dSAndroid Build Coastguard Worker if not fn(cookie, request): 1111*cda5da8dSAndroid Build Coastguard Worker return False 1112*cda5da8dSAndroid Build Coastguard Worker return True 1113*cda5da8dSAndroid Build Coastguard Worker 1114*cda5da8dSAndroid Build Coastguard Worker def return_ok_version(self, cookie, request): 1115*cda5da8dSAndroid Build Coastguard Worker if cookie.version > 0 and not self.rfc2965: 1116*cda5da8dSAndroid Build Coastguard Worker _debug(" RFC 2965 cookies are switched off") 1117*cda5da8dSAndroid Build Coastguard Worker return False 1118*cda5da8dSAndroid Build Coastguard Worker elif cookie.version == 0 and not self.netscape: 1119*cda5da8dSAndroid Build Coastguard Worker _debug(" Netscape cookies are switched off") 1120*cda5da8dSAndroid Build Coastguard Worker return False 1121*cda5da8dSAndroid Build Coastguard Worker return True 1122*cda5da8dSAndroid Build Coastguard Worker 1123*cda5da8dSAndroid Build Coastguard Worker def return_ok_verifiability(self, cookie, request): 1124*cda5da8dSAndroid Build Coastguard Worker if request.unverifiable and is_third_party(request): 1125*cda5da8dSAndroid Build Coastguard Worker if cookie.version > 0 and self.strict_rfc2965_unverifiable: 1126*cda5da8dSAndroid Build Coastguard Worker _debug(" third-party RFC 2965 cookie during unverifiable " 1127*cda5da8dSAndroid Build Coastguard Worker "transaction") 1128*cda5da8dSAndroid Build Coastguard Worker return False 1129*cda5da8dSAndroid Build Coastguard Worker elif cookie.version == 0 and self.strict_ns_unverifiable: 1130*cda5da8dSAndroid Build Coastguard Worker _debug(" third-party Netscape cookie during unverifiable " 1131*cda5da8dSAndroid Build Coastguard Worker "transaction") 1132*cda5da8dSAndroid Build Coastguard Worker return False 1133*cda5da8dSAndroid Build Coastguard Worker return True 1134*cda5da8dSAndroid Build Coastguard Worker 1135*cda5da8dSAndroid Build Coastguard Worker def return_ok_secure(self, cookie, request): 1136*cda5da8dSAndroid Build Coastguard Worker if cookie.secure and request.type not in self.secure_protocols: 1137*cda5da8dSAndroid Build Coastguard Worker _debug(" secure cookie with non-secure request") 1138*cda5da8dSAndroid Build Coastguard Worker return False 1139*cda5da8dSAndroid Build Coastguard Worker return True 1140*cda5da8dSAndroid Build Coastguard Worker 1141*cda5da8dSAndroid Build Coastguard Worker def return_ok_expires(self, cookie, request): 1142*cda5da8dSAndroid Build Coastguard Worker if cookie.is_expired(self._now): 1143*cda5da8dSAndroid Build Coastguard Worker _debug(" cookie expired") 1144*cda5da8dSAndroid Build Coastguard Worker return False 1145*cda5da8dSAndroid Build Coastguard Worker return True 1146*cda5da8dSAndroid Build Coastguard Worker 1147*cda5da8dSAndroid Build Coastguard Worker def return_ok_port(self, cookie, request): 1148*cda5da8dSAndroid Build Coastguard Worker if cookie.port: 1149*cda5da8dSAndroid Build Coastguard Worker req_port = request_port(request) 1150*cda5da8dSAndroid Build Coastguard Worker if req_port is None: 1151*cda5da8dSAndroid Build Coastguard Worker req_port = "80" 1152*cda5da8dSAndroid Build Coastguard Worker for p in cookie.port.split(","): 1153*cda5da8dSAndroid Build Coastguard Worker if p == req_port: 1154*cda5da8dSAndroid Build Coastguard Worker break 1155*cda5da8dSAndroid Build Coastguard Worker else: 1156*cda5da8dSAndroid Build Coastguard Worker _debug(" request port %s does not match cookie port %s", 1157*cda5da8dSAndroid Build Coastguard Worker req_port, cookie.port) 1158*cda5da8dSAndroid Build Coastguard Worker return False 1159*cda5da8dSAndroid Build Coastguard Worker return True 1160*cda5da8dSAndroid Build Coastguard Worker 1161*cda5da8dSAndroid Build Coastguard Worker def return_ok_domain(self, cookie, request): 1162*cda5da8dSAndroid Build Coastguard Worker req_host, erhn = eff_request_host(request) 1163*cda5da8dSAndroid Build Coastguard Worker domain = cookie.domain 1164*cda5da8dSAndroid Build Coastguard Worker 1165*cda5da8dSAndroid Build Coastguard Worker if domain and not domain.startswith("."): 1166*cda5da8dSAndroid Build Coastguard Worker dotdomain = "." + domain 1167*cda5da8dSAndroid Build Coastguard Worker else: 1168*cda5da8dSAndroid Build Coastguard Worker dotdomain = domain 1169*cda5da8dSAndroid Build Coastguard Worker 1170*cda5da8dSAndroid Build Coastguard Worker # strict check of non-domain cookies: Mozilla does this, MSIE5 doesn't 1171*cda5da8dSAndroid Build Coastguard Worker if (cookie.version == 0 and 1172*cda5da8dSAndroid Build Coastguard Worker (self.strict_ns_domain & self.DomainStrictNonDomain) and 1173*cda5da8dSAndroid Build Coastguard Worker not cookie.domain_specified and domain != erhn): 1174*cda5da8dSAndroid Build Coastguard Worker _debug(" cookie with unspecified domain does not string-compare " 1175*cda5da8dSAndroid Build Coastguard Worker "equal to request domain") 1176*cda5da8dSAndroid Build Coastguard Worker return False 1177*cda5da8dSAndroid Build Coastguard Worker 1178*cda5da8dSAndroid Build Coastguard Worker if cookie.version > 0 and not domain_match(erhn, domain): 1179*cda5da8dSAndroid Build Coastguard Worker _debug(" effective request-host name %s does not domain-match " 1180*cda5da8dSAndroid Build Coastguard Worker "RFC 2965 cookie domain %s", erhn, domain) 1181*cda5da8dSAndroid Build Coastguard Worker return False 1182*cda5da8dSAndroid Build Coastguard Worker if cookie.version == 0 and not ("."+erhn).endswith(dotdomain): 1183*cda5da8dSAndroid Build Coastguard Worker _debug(" request-host %s does not match Netscape cookie domain " 1184*cda5da8dSAndroid Build Coastguard Worker "%s", req_host, domain) 1185*cda5da8dSAndroid Build Coastguard Worker return False 1186*cda5da8dSAndroid Build Coastguard Worker return True 1187*cda5da8dSAndroid Build Coastguard Worker 1188*cda5da8dSAndroid Build Coastguard Worker def domain_return_ok(self, domain, request): 1189*cda5da8dSAndroid Build Coastguard Worker # Liberal check of. This is here as an optimization to avoid 1190*cda5da8dSAndroid Build Coastguard Worker # having to load lots of MSIE cookie files unless necessary. 1191*cda5da8dSAndroid Build Coastguard Worker req_host, erhn = eff_request_host(request) 1192*cda5da8dSAndroid Build Coastguard Worker if not req_host.startswith("."): 1193*cda5da8dSAndroid Build Coastguard Worker req_host = "."+req_host 1194*cda5da8dSAndroid Build Coastguard Worker if not erhn.startswith("."): 1195*cda5da8dSAndroid Build Coastguard Worker erhn = "."+erhn 1196*cda5da8dSAndroid Build Coastguard Worker if domain and not domain.startswith("."): 1197*cda5da8dSAndroid Build Coastguard Worker dotdomain = "." + domain 1198*cda5da8dSAndroid Build Coastguard Worker else: 1199*cda5da8dSAndroid Build Coastguard Worker dotdomain = domain 1200*cda5da8dSAndroid Build Coastguard Worker if not (req_host.endswith(dotdomain) or erhn.endswith(dotdomain)): 1201*cda5da8dSAndroid Build Coastguard Worker #_debug(" request domain %s does not match cookie domain %s", 1202*cda5da8dSAndroid Build Coastguard Worker # req_host, domain) 1203*cda5da8dSAndroid Build Coastguard Worker return False 1204*cda5da8dSAndroid Build Coastguard Worker 1205*cda5da8dSAndroid Build Coastguard Worker if self.is_blocked(domain): 1206*cda5da8dSAndroid Build Coastguard Worker _debug(" domain %s is in user block-list", domain) 1207*cda5da8dSAndroid Build Coastguard Worker return False 1208*cda5da8dSAndroid Build Coastguard Worker if self.is_not_allowed(domain): 1209*cda5da8dSAndroid Build Coastguard Worker _debug(" domain %s is not in user allow-list", domain) 1210*cda5da8dSAndroid Build Coastguard Worker return False 1211*cda5da8dSAndroid Build Coastguard Worker 1212*cda5da8dSAndroid Build Coastguard Worker return True 1213*cda5da8dSAndroid Build Coastguard Worker 1214*cda5da8dSAndroid Build Coastguard Worker def path_return_ok(self, path, request): 1215*cda5da8dSAndroid Build Coastguard Worker _debug("- checking cookie path=%s", path) 1216*cda5da8dSAndroid Build Coastguard Worker req_path = request_path(request) 1217*cda5da8dSAndroid Build Coastguard Worker pathlen = len(path) 1218*cda5da8dSAndroid Build Coastguard Worker if req_path == path: 1219*cda5da8dSAndroid Build Coastguard Worker return True 1220*cda5da8dSAndroid Build Coastguard Worker elif (req_path.startswith(path) and 1221*cda5da8dSAndroid Build Coastguard Worker (path.endswith("/") or req_path[pathlen:pathlen+1] == "/")): 1222*cda5da8dSAndroid Build Coastguard Worker return True 1223*cda5da8dSAndroid Build Coastguard Worker 1224*cda5da8dSAndroid Build Coastguard Worker _debug(" %s does not path-match %s", req_path, path) 1225*cda5da8dSAndroid Build Coastguard Worker return False 1226*cda5da8dSAndroid Build Coastguard Worker 1227*cda5da8dSAndroid Build Coastguard Workerdef deepvalues(mapping): 1228*cda5da8dSAndroid Build Coastguard Worker """Iterates over nested mapping, depth-first""" 1229*cda5da8dSAndroid Build Coastguard Worker for obj in list(mapping.values()): 1230*cda5da8dSAndroid Build Coastguard Worker mapping = False 1231*cda5da8dSAndroid Build Coastguard Worker try: 1232*cda5da8dSAndroid Build Coastguard Worker obj.items 1233*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 1234*cda5da8dSAndroid Build Coastguard Worker pass 1235*cda5da8dSAndroid Build Coastguard Worker else: 1236*cda5da8dSAndroid Build Coastguard Worker mapping = True 1237*cda5da8dSAndroid Build Coastguard Worker yield from deepvalues(obj) 1238*cda5da8dSAndroid Build Coastguard Worker if not mapping: 1239*cda5da8dSAndroid Build Coastguard Worker yield obj 1240*cda5da8dSAndroid Build Coastguard Worker 1241*cda5da8dSAndroid Build Coastguard Worker 1242*cda5da8dSAndroid Build Coastguard Worker# Used as second parameter to dict.get() method, to distinguish absent 1243*cda5da8dSAndroid Build Coastguard Worker# dict key from one with a None value. 1244*cda5da8dSAndroid Build Coastguard Workerclass Absent: pass 1245*cda5da8dSAndroid Build Coastguard Worker 1246*cda5da8dSAndroid Build Coastguard Workerclass CookieJar: 1247*cda5da8dSAndroid Build Coastguard Worker """Collection of HTTP cookies. 1248*cda5da8dSAndroid Build Coastguard Worker 1249*cda5da8dSAndroid Build Coastguard Worker You may not need to know about this class: try 1250*cda5da8dSAndroid Build Coastguard Worker urllib.request.build_opener(HTTPCookieProcessor).open(url). 1251*cda5da8dSAndroid Build Coastguard Worker """ 1252*cda5da8dSAndroid Build Coastguard Worker 1253*cda5da8dSAndroid Build Coastguard Worker non_word_re = re.compile(r"\W") 1254*cda5da8dSAndroid Build Coastguard Worker quote_re = re.compile(r"([\"\\])") 1255*cda5da8dSAndroid Build Coastguard Worker strict_domain_re = re.compile(r"\.?[^.]*") 1256*cda5da8dSAndroid Build Coastguard Worker domain_re = re.compile(r"[^.]*") 1257*cda5da8dSAndroid Build Coastguard Worker dots_re = re.compile(r"^\.+") 1258*cda5da8dSAndroid Build Coastguard Worker 1259*cda5da8dSAndroid Build Coastguard Worker magic_re = re.compile(r"^\#LWP-Cookies-(\d+\.\d+)", re.ASCII) 1260*cda5da8dSAndroid Build Coastguard Worker 1261*cda5da8dSAndroid Build Coastguard Worker def __init__(self, policy=None): 1262*cda5da8dSAndroid Build Coastguard Worker if policy is None: 1263*cda5da8dSAndroid Build Coastguard Worker policy = DefaultCookiePolicy() 1264*cda5da8dSAndroid Build Coastguard Worker self._policy = policy 1265*cda5da8dSAndroid Build Coastguard Worker 1266*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock = _threading.RLock() 1267*cda5da8dSAndroid Build Coastguard Worker self._cookies = {} 1268*cda5da8dSAndroid Build Coastguard Worker 1269*cda5da8dSAndroid Build Coastguard Worker def set_policy(self, policy): 1270*cda5da8dSAndroid Build Coastguard Worker self._policy = policy 1271*cda5da8dSAndroid Build Coastguard Worker 1272*cda5da8dSAndroid Build Coastguard Worker def _cookies_for_domain(self, domain, request): 1273*cda5da8dSAndroid Build Coastguard Worker cookies = [] 1274*cda5da8dSAndroid Build Coastguard Worker if not self._policy.domain_return_ok(domain, request): 1275*cda5da8dSAndroid Build Coastguard Worker return [] 1276*cda5da8dSAndroid Build Coastguard Worker _debug("Checking %s for cookies to return", domain) 1277*cda5da8dSAndroid Build Coastguard Worker cookies_by_path = self._cookies[domain] 1278*cda5da8dSAndroid Build Coastguard Worker for path in cookies_by_path.keys(): 1279*cda5da8dSAndroid Build Coastguard Worker if not self._policy.path_return_ok(path, request): 1280*cda5da8dSAndroid Build Coastguard Worker continue 1281*cda5da8dSAndroid Build Coastguard Worker cookies_by_name = cookies_by_path[path] 1282*cda5da8dSAndroid Build Coastguard Worker for cookie in cookies_by_name.values(): 1283*cda5da8dSAndroid Build Coastguard Worker if not self._policy.return_ok(cookie, request): 1284*cda5da8dSAndroid Build Coastguard Worker _debug(" not returning cookie") 1285*cda5da8dSAndroid Build Coastguard Worker continue 1286*cda5da8dSAndroid Build Coastguard Worker _debug(" it's a match") 1287*cda5da8dSAndroid Build Coastguard Worker cookies.append(cookie) 1288*cda5da8dSAndroid Build Coastguard Worker return cookies 1289*cda5da8dSAndroid Build Coastguard Worker 1290*cda5da8dSAndroid Build Coastguard Worker def _cookies_for_request(self, request): 1291*cda5da8dSAndroid Build Coastguard Worker """Return a list of cookies to be returned to server.""" 1292*cda5da8dSAndroid Build Coastguard Worker cookies = [] 1293*cda5da8dSAndroid Build Coastguard Worker for domain in self._cookies.keys(): 1294*cda5da8dSAndroid Build Coastguard Worker cookies.extend(self._cookies_for_domain(domain, request)) 1295*cda5da8dSAndroid Build Coastguard Worker return cookies 1296*cda5da8dSAndroid Build Coastguard Worker 1297*cda5da8dSAndroid Build Coastguard Worker def _cookie_attrs(self, cookies): 1298*cda5da8dSAndroid Build Coastguard Worker """Return a list of cookie-attributes to be returned to server. 1299*cda5da8dSAndroid Build Coastguard Worker 1300*cda5da8dSAndroid Build Coastguard Worker like ['foo="bar"; $Path="/"', ...] 1301*cda5da8dSAndroid Build Coastguard Worker 1302*cda5da8dSAndroid Build Coastguard Worker The $Version attribute is also added when appropriate (currently only 1303*cda5da8dSAndroid Build Coastguard Worker once per request). 1304*cda5da8dSAndroid Build Coastguard Worker 1305*cda5da8dSAndroid Build Coastguard Worker """ 1306*cda5da8dSAndroid Build Coastguard Worker # add cookies in order of most specific (ie. longest) path first 1307*cda5da8dSAndroid Build Coastguard Worker cookies.sort(key=lambda a: len(a.path), reverse=True) 1308*cda5da8dSAndroid Build Coastguard Worker 1309*cda5da8dSAndroid Build Coastguard Worker version_set = False 1310*cda5da8dSAndroid Build Coastguard Worker 1311*cda5da8dSAndroid Build Coastguard Worker attrs = [] 1312*cda5da8dSAndroid Build Coastguard Worker for cookie in cookies: 1313*cda5da8dSAndroid Build Coastguard Worker # set version of Cookie header 1314*cda5da8dSAndroid Build Coastguard Worker # XXX 1315*cda5da8dSAndroid Build Coastguard Worker # What should it be if multiple matching Set-Cookie headers have 1316*cda5da8dSAndroid Build Coastguard Worker # different versions themselves? 1317*cda5da8dSAndroid Build Coastguard Worker # Answer: there is no answer; was supposed to be settled by 1318*cda5da8dSAndroid Build Coastguard Worker # RFC 2965 errata, but that may never appear... 1319*cda5da8dSAndroid Build Coastguard Worker version = cookie.version 1320*cda5da8dSAndroid Build Coastguard Worker if not version_set: 1321*cda5da8dSAndroid Build Coastguard Worker version_set = True 1322*cda5da8dSAndroid Build Coastguard Worker if version > 0: 1323*cda5da8dSAndroid Build Coastguard Worker attrs.append("$Version=%s" % version) 1324*cda5da8dSAndroid Build Coastguard Worker 1325*cda5da8dSAndroid Build Coastguard Worker # quote cookie value if necessary 1326*cda5da8dSAndroid Build Coastguard Worker # (not for Netscape protocol, which already has any quotes 1327*cda5da8dSAndroid Build Coastguard Worker # intact, due to the poorly-specified Netscape Cookie: syntax) 1328*cda5da8dSAndroid Build Coastguard Worker if ((cookie.value is not None) and 1329*cda5da8dSAndroid Build Coastguard Worker self.non_word_re.search(cookie.value) and version > 0): 1330*cda5da8dSAndroid Build Coastguard Worker value = self.quote_re.sub(r"\\\1", cookie.value) 1331*cda5da8dSAndroid Build Coastguard Worker else: 1332*cda5da8dSAndroid Build Coastguard Worker value = cookie.value 1333*cda5da8dSAndroid Build Coastguard Worker 1334*cda5da8dSAndroid Build Coastguard Worker # add cookie-attributes to be returned in Cookie header 1335*cda5da8dSAndroid Build Coastguard Worker if cookie.value is None: 1336*cda5da8dSAndroid Build Coastguard Worker attrs.append(cookie.name) 1337*cda5da8dSAndroid Build Coastguard Worker else: 1338*cda5da8dSAndroid Build Coastguard Worker attrs.append("%s=%s" % (cookie.name, value)) 1339*cda5da8dSAndroid Build Coastguard Worker if version > 0: 1340*cda5da8dSAndroid Build Coastguard Worker if cookie.path_specified: 1341*cda5da8dSAndroid Build Coastguard Worker attrs.append('$Path="%s"' % cookie.path) 1342*cda5da8dSAndroid Build Coastguard Worker if cookie.domain.startswith("."): 1343*cda5da8dSAndroid Build Coastguard Worker domain = cookie.domain 1344*cda5da8dSAndroid Build Coastguard Worker if (not cookie.domain_initial_dot and 1345*cda5da8dSAndroid Build Coastguard Worker domain.startswith(".")): 1346*cda5da8dSAndroid Build Coastguard Worker domain = domain[1:] 1347*cda5da8dSAndroid Build Coastguard Worker attrs.append('$Domain="%s"' % domain) 1348*cda5da8dSAndroid Build Coastguard Worker if cookie.port is not None: 1349*cda5da8dSAndroid Build Coastguard Worker p = "$Port" 1350*cda5da8dSAndroid Build Coastguard Worker if cookie.port_specified: 1351*cda5da8dSAndroid Build Coastguard Worker p = p + ('="%s"' % cookie.port) 1352*cda5da8dSAndroid Build Coastguard Worker attrs.append(p) 1353*cda5da8dSAndroid Build Coastguard Worker 1354*cda5da8dSAndroid Build Coastguard Worker return attrs 1355*cda5da8dSAndroid Build Coastguard Worker 1356*cda5da8dSAndroid Build Coastguard Worker def add_cookie_header(self, request): 1357*cda5da8dSAndroid Build Coastguard Worker """Add correct Cookie: header to request (urllib.request.Request object). 1358*cda5da8dSAndroid Build Coastguard Worker 1359*cda5da8dSAndroid Build Coastguard Worker The Cookie2 header is also added unless policy.hide_cookie2 is true. 1360*cda5da8dSAndroid Build Coastguard Worker 1361*cda5da8dSAndroid Build Coastguard Worker """ 1362*cda5da8dSAndroid Build Coastguard Worker _debug("add_cookie_header") 1363*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.acquire() 1364*cda5da8dSAndroid Build Coastguard Worker try: 1365*cda5da8dSAndroid Build Coastguard Worker 1366*cda5da8dSAndroid Build Coastguard Worker self._policy._now = self._now = int(time.time()) 1367*cda5da8dSAndroid Build Coastguard Worker 1368*cda5da8dSAndroid Build Coastguard Worker cookies = self._cookies_for_request(request) 1369*cda5da8dSAndroid Build Coastguard Worker 1370*cda5da8dSAndroid Build Coastguard Worker attrs = self._cookie_attrs(cookies) 1371*cda5da8dSAndroid Build Coastguard Worker if attrs: 1372*cda5da8dSAndroid Build Coastguard Worker if not request.has_header("Cookie"): 1373*cda5da8dSAndroid Build Coastguard Worker request.add_unredirected_header( 1374*cda5da8dSAndroid Build Coastguard Worker "Cookie", "; ".join(attrs)) 1375*cda5da8dSAndroid Build Coastguard Worker 1376*cda5da8dSAndroid Build Coastguard Worker # if necessary, advertise that we know RFC 2965 1377*cda5da8dSAndroid Build Coastguard Worker if (self._policy.rfc2965 and not self._policy.hide_cookie2 and 1378*cda5da8dSAndroid Build Coastguard Worker not request.has_header("Cookie2")): 1379*cda5da8dSAndroid Build Coastguard Worker for cookie in cookies: 1380*cda5da8dSAndroid Build Coastguard Worker if cookie.version != 1: 1381*cda5da8dSAndroid Build Coastguard Worker request.add_unredirected_header("Cookie2", '$Version="1"') 1382*cda5da8dSAndroid Build Coastguard Worker break 1383*cda5da8dSAndroid Build Coastguard Worker 1384*cda5da8dSAndroid Build Coastguard Worker finally: 1385*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.release() 1386*cda5da8dSAndroid Build Coastguard Worker 1387*cda5da8dSAndroid Build Coastguard Worker self.clear_expired_cookies() 1388*cda5da8dSAndroid Build Coastguard Worker 1389*cda5da8dSAndroid Build Coastguard Worker def _normalized_cookie_tuples(self, attrs_set): 1390*cda5da8dSAndroid Build Coastguard Worker """Return list of tuples containing normalised cookie information. 1391*cda5da8dSAndroid Build Coastguard Worker 1392*cda5da8dSAndroid Build Coastguard Worker attrs_set is the list of lists of key,value pairs extracted from 1393*cda5da8dSAndroid Build Coastguard Worker the Set-Cookie or Set-Cookie2 headers. 1394*cda5da8dSAndroid Build Coastguard Worker 1395*cda5da8dSAndroid Build Coastguard Worker Tuples are name, value, standard, rest, where name and value are the 1396*cda5da8dSAndroid Build Coastguard Worker cookie name and value, standard is a dictionary containing the standard 1397*cda5da8dSAndroid Build Coastguard Worker cookie-attributes (discard, secure, version, expires or max-age, 1398*cda5da8dSAndroid Build Coastguard Worker domain, path and port) and rest is a dictionary containing the rest of 1399*cda5da8dSAndroid Build Coastguard Worker the cookie-attributes. 1400*cda5da8dSAndroid Build Coastguard Worker 1401*cda5da8dSAndroid Build Coastguard Worker """ 1402*cda5da8dSAndroid Build Coastguard Worker cookie_tuples = [] 1403*cda5da8dSAndroid Build Coastguard Worker 1404*cda5da8dSAndroid Build Coastguard Worker boolean_attrs = "discard", "secure" 1405*cda5da8dSAndroid Build Coastguard Worker value_attrs = ("version", 1406*cda5da8dSAndroid Build Coastguard Worker "expires", "max-age", 1407*cda5da8dSAndroid Build Coastguard Worker "domain", "path", "port", 1408*cda5da8dSAndroid Build Coastguard Worker "comment", "commenturl") 1409*cda5da8dSAndroid Build Coastguard Worker 1410*cda5da8dSAndroid Build Coastguard Worker for cookie_attrs in attrs_set: 1411*cda5da8dSAndroid Build Coastguard Worker name, value = cookie_attrs[0] 1412*cda5da8dSAndroid Build Coastguard Worker 1413*cda5da8dSAndroid Build Coastguard Worker # Build dictionary of standard cookie-attributes (standard) and 1414*cda5da8dSAndroid Build Coastguard Worker # dictionary of other cookie-attributes (rest). 1415*cda5da8dSAndroid Build Coastguard Worker 1416*cda5da8dSAndroid Build Coastguard Worker # Note: expiry time is normalised to seconds since epoch. V0 1417*cda5da8dSAndroid Build Coastguard Worker # cookies should have the Expires cookie-attribute, and V1 cookies 1418*cda5da8dSAndroid Build Coastguard Worker # should have Max-Age, but since V1 includes RFC 2109 cookies (and 1419*cda5da8dSAndroid Build Coastguard Worker # since V0 cookies may be a mish-mash of Netscape and RFC 2109), we 1420*cda5da8dSAndroid Build Coastguard Worker # accept either (but prefer Max-Age). 1421*cda5da8dSAndroid Build Coastguard Worker max_age_set = False 1422*cda5da8dSAndroid Build Coastguard Worker 1423*cda5da8dSAndroid Build Coastguard Worker bad_cookie = False 1424*cda5da8dSAndroid Build Coastguard Worker 1425*cda5da8dSAndroid Build Coastguard Worker standard = {} 1426*cda5da8dSAndroid Build Coastguard Worker rest = {} 1427*cda5da8dSAndroid Build Coastguard Worker for k, v in cookie_attrs[1:]: 1428*cda5da8dSAndroid Build Coastguard Worker lc = k.lower() 1429*cda5da8dSAndroid Build Coastguard Worker # don't lose case distinction for unknown fields 1430*cda5da8dSAndroid Build Coastguard Worker if lc in value_attrs or lc in boolean_attrs: 1431*cda5da8dSAndroid Build Coastguard Worker k = lc 1432*cda5da8dSAndroid Build Coastguard Worker if k in boolean_attrs and v is None: 1433*cda5da8dSAndroid Build Coastguard Worker # boolean cookie-attribute is present, but has no value 1434*cda5da8dSAndroid Build Coastguard Worker # (like "discard", rather than "port=80") 1435*cda5da8dSAndroid Build Coastguard Worker v = True 1436*cda5da8dSAndroid Build Coastguard Worker if k in standard: 1437*cda5da8dSAndroid Build Coastguard Worker # only first value is significant 1438*cda5da8dSAndroid Build Coastguard Worker continue 1439*cda5da8dSAndroid Build Coastguard Worker if k == "domain": 1440*cda5da8dSAndroid Build Coastguard Worker if v is None: 1441*cda5da8dSAndroid Build Coastguard Worker _debug(" missing value for domain attribute") 1442*cda5da8dSAndroid Build Coastguard Worker bad_cookie = True 1443*cda5da8dSAndroid Build Coastguard Worker break 1444*cda5da8dSAndroid Build Coastguard Worker # RFC 2965 section 3.3.3 1445*cda5da8dSAndroid Build Coastguard Worker v = v.lower() 1446*cda5da8dSAndroid Build Coastguard Worker if k == "expires": 1447*cda5da8dSAndroid Build Coastguard Worker if max_age_set: 1448*cda5da8dSAndroid Build Coastguard Worker # Prefer max-age to expires (like Mozilla) 1449*cda5da8dSAndroid Build Coastguard Worker continue 1450*cda5da8dSAndroid Build Coastguard Worker if v is None: 1451*cda5da8dSAndroid Build Coastguard Worker _debug(" missing or invalid value for expires " 1452*cda5da8dSAndroid Build Coastguard Worker "attribute: treating as session cookie") 1453*cda5da8dSAndroid Build Coastguard Worker continue 1454*cda5da8dSAndroid Build Coastguard Worker if k == "max-age": 1455*cda5da8dSAndroid Build Coastguard Worker max_age_set = True 1456*cda5da8dSAndroid Build Coastguard Worker try: 1457*cda5da8dSAndroid Build Coastguard Worker v = int(v) 1458*cda5da8dSAndroid Build Coastguard Worker except ValueError: 1459*cda5da8dSAndroid Build Coastguard Worker _debug(" missing or invalid (non-numeric) value for " 1460*cda5da8dSAndroid Build Coastguard Worker "max-age attribute") 1461*cda5da8dSAndroid Build Coastguard Worker bad_cookie = True 1462*cda5da8dSAndroid Build Coastguard Worker break 1463*cda5da8dSAndroid Build Coastguard Worker # convert RFC 2965 Max-Age to seconds since epoch 1464*cda5da8dSAndroid Build Coastguard Worker # XXX Strictly you're supposed to follow RFC 2616 1465*cda5da8dSAndroid Build Coastguard Worker # age-calculation rules. Remember that zero Max-Age 1466*cda5da8dSAndroid Build Coastguard Worker # is a request to discard (old and new) cookie, though. 1467*cda5da8dSAndroid Build Coastguard Worker k = "expires" 1468*cda5da8dSAndroid Build Coastguard Worker v = self._now + v 1469*cda5da8dSAndroid Build Coastguard Worker if (k in value_attrs) or (k in boolean_attrs): 1470*cda5da8dSAndroid Build Coastguard Worker if (v is None and 1471*cda5da8dSAndroid Build Coastguard Worker k not in ("port", "comment", "commenturl")): 1472*cda5da8dSAndroid Build Coastguard Worker _debug(" missing value for %s attribute" % k) 1473*cda5da8dSAndroid Build Coastguard Worker bad_cookie = True 1474*cda5da8dSAndroid Build Coastguard Worker break 1475*cda5da8dSAndroid Build Coastguard Worker standard[k] = v 1476*cda5da8dSAndroid Build Coastguard Worker else: 1477*cda5da8dSAndroid Build Coastguard Worker rest[k] = v 1478*cda5da8dSAndroid Build Coastguard Worker 1479*cda5da8dSAndroid Build Coastguard Worker if bad_cookie: 1480*cda5da8dSAndroid Build Coastguard Worker continue 1481*cda5da8dSAndroid Build Coastguard Worker 1482*cda5da8dSAndroid Build Coastguard Worker cookie_tuples.append((name, value, standard, rest)) 1483*cda5da8dSAndroid Build Coastguard Worker 1484*cda5da8dSAndroid Build Coastguard Worker return cookie_tuples 1485*cda5da8dSAndroid Build Coastguard Worker 1486*cda5da8dSAndroid Build Coastguard Worker def _cookie_from_cookie_tuple(self, tup, request): 1487*cda5da8dSAndroid Build Coastguard Worker # standard is dict of standard cookie-attributes, rest is dict of the 1488*cda5da8dSAndroid Build Coastguard Worker # rest of them 1489*cda5da8dSAndroid Build Coastguard Worker name, value, standard, rest = tup 1490*cda5da8dSAndroid Build Coastguard Worker 1491*cda5da8dSAndroid Build Coastguard Worker domain = standard.get("domain", Absent) 1492*cda5da8dSAndroid Build Coastguard Worker path = standard.get("path", Absent) 1493*cda5da8dSAndroid Build Coastguard Worker port = standard.get("port", Absent) 1494*cda5da8dSAndroid Build Coastguard Worker expires = standard.get("expires", Absent) 1495*cda5da8dSAndroid Build Coastguard Worker 1496*cda5da8dSAndroid Build Coastguard Worker # set the easy defaults 1497*cda5da8dSAndroid Build Coastguard Worker version = standard.get("version", None) 1498*cda5da8dSAndroid Build Coastguard Worker if version is not None: 1499*cda5da8dSAndroid Build Coastguard Worker try: 1500*cda5da8dSAndroid Build Coastguard Worker version = int(version) 1501*cda5da8dSAndroid Build Coastguard Worker except ValueError: 1502*cda5da8dSAndroid Build Coastguard Worker return None # invalid version, ignore cookie 1503*cda5da8dSAndroid Build Coastguard Worker secure = standard.get("secure", False) 1504*cda5da8dSAndroid Build Coastguard Worker # (discard is also set if expires is Absent) 1505*cda5da8dSAndroid Build Coastguard Worker discard = standard.get("discard", False) 1506*cda5da8dSAndroid Build Coastguard Worker comment = standard.get("comment", None) 1507*cda5da8dSAndroid Build Coastguard Worker comment_url = standard.get("commenturl", None) 1508*cda5da8dSAndroid Build Coastguard Worker 1509*cda5da8dSAndroid Build Coastguard Worker # set default path 1510*cda5da8dSAndroid Build Coastguard Worker if path is not Absent and path != "": 1511*cda5da8dSAndroid Build Coastguard Worker path_specified = True 1512*cda5da8dSAndroid Build Coastguard Worker path = escape_path(path) 1513*cda5da8dSAndroid Build Coastguard Worker else: 1514*cda5da8dSAndroid Build Coastguard Worker path_specified = False 1515*cda5da8dSAndroid Build Coastguard Worker path = request_path(request) 1516*cda5da8dSAndroid Build Coastguard Worker i = path.rfind("/") 1517*cda5da8dSAndroid Build Coastguard Worker if i != -1: 1518*cda5da8dSAndroid Build Coastguard Worker if version == 0: 1519*cda5da8dSAndroid Build Coastguard Worker # Netscape spec parts company from reality here 1520*cda5da8dSAndroid Build Coastguard Worker path = path[:i] 1521*cda5da8dSAndroid Build Coastguard Worker else: 1522*cda5da8dSAndroid Build Coastguard Worker path = path[:i+1] 1523*cda5da8dSAndroid Build Coastguard Worker if len(path) == 0: path = "/" 1524*cda5da8dSAndroid Build Coastguard Worker 1525*cda5da8dSAndroid Build Coastguard Worker # set default domain 1526*cda5da8dSAndroid Build Coastguard Worker domain_specified = domain is not Absent 1527*cda5da8dSAndroid Build Coastguard Worker # but first we have to remember whether it starts with a dot 1528*cda5da8dSAndroid Build Coastguard Worker domain_initial_dot = False 1529*cda5da8dSAndroid Build Coastguard Worker if domain_specified: 1530*cda5da8dSAndroid Build Coastguard Worker domain_initial_dot = bool(domain.startswith(".")) 1531*cda5da8dSAndroid Build Coastguard Worker if domain is Absent: 1532*cda5da8dSAndroid Build Coastguard Worker req_host, erhn = eff_request_host(request) 1533*cda5da8dSAndroid Build Coastguard Worker domain = erhn 1534*cda5da8dSAndroid Build Coastguard Worker elif not domain.startswith("."): 1535*cda5da8dSAndroid Build Coastguard Worker domain = "."+domain 1536*cda5da8dSAndroid Build Coastguard Worker 1537*cda5da8dSAndroid Build Coastguard Worker # set default port 1538*cda5da8dSAndroid Build Coastguard Worker port_specified = False 1539*cda5da8dSAndroid Build Coastguard Worker if port is not Absent: 1540*cda5da8dSAndroid Build Coastguard Worker if port is None: 1541*cda5da8dSAndroid Build Coastguard Worker # Port attr present, but has no value: default to request port. 1542*cda5da8dSAndroid Build Coastguard Worker # Cookie should then only be sent back on that port. 1543*cda5da8dSAndroid Build Coastguard Worker port = request_port(request) 1544*cda5da8dSAndroid Build Coastguard Worker else: 1545*cda5da8dSAndroid Build Coastguard Worker port_specified = True 1546*cda5da8dSAndroid Build Coastguard Worker port = re.sub(r"\s+", "", port) 1547*cda5da8dSAndroid Build Coastguard Worker else: 1548*cda5da8dSAndroid Build Coastguard Worker # No port attr present. Cookie can be sent back on any port. 1549*cda5da8dSAndroid Build Coastguard Worker port = None 1550*cda5da8dSAndroid Build Coastguard Worker 1551*cda5da8dSAndroid Build Coastguard Worker # set default expires and discard 1552*cda5da8dSAndroid Build Coastguard Worker if expires is Absent: 1553*cda5da8dSAndroid Build Coastguard Worker expires = None 1554*cda5da8dSAndroid Build Coastguard Worker discard = True 1555*cda5da8dSAndroid Build Coastguard Worker elif expires <= self._now: 1556*cda5da8dSAndroid Build Coastguard Worker # Expiry date in past is request to delete cookie. This can't be 1557*cda5da8dSAndroid Build Coastguard Worker # in DefaultCookiePolicy, because can't delete cookies there. 1558*cda5da8dSAndroid Build Coastguard Worker try: 1559*cda5da8dSAndroid Build Coastguard Worker self.clear(domain, path, name) 1560*cda5da8dSAndroid Build Coastguard Worker except KeyError: 1561*cda5da8dSAndroid Build Coastguard Worker pass 1562*cda5da8dSAndroid Build Coastguard Worker _debug("Expiring cookie, domain='%s', path='%s', name='%s'", 1563*cda5da8dSAndroid Build Coastguard Worker domain, path, name) 1564*cda5da8dSAndroid Build Coastguard Worker return None 1565*cda5da8dSAndroid Build Coastguard Worker 1566*cda5da8dSAndroid Build Coastguard Worker return Cookie(version, 1567*cda5da8dSAndroid Build Coastguard Worker name, value, 1568*cda5da8dSAndroid Build Coastguard Worker port, port_specified, 1569*cda5da8dSAndroid Build Coastguard Worker domain, domain_specified, domain_initial_dot, 1570*cda5da8dSAndroid Build Coastguard Worker path, path_specified, 1571*cda5da8dSAndroid Build Coastguard Worker secure, 1572*cda5da8dSAndroid Build Coastguard Worker expires, 1573*cda5da8dSAndroid Build Coastguard Worker discard, 1574*cda5da8dSAndroid Build Coastguard Worker comment, 1575*cda5da8dSAndroid Build Coastguard Worker comment_url, 1576*cda5da8dSAndroid Build Coastguard Worker rest) 1577*cda5da8dSAndroid Build Coastguard Worker 1578*cda5da8dSAndroid Build Coastguard Worker def _cookies_from_attrs_set(self, attrs_set, request): 1579*cda5da8dSAndroid Build Coastguard Worker cookie_tuples = self._normalized_cookie_tuples(attrs_set) 1580*cda5da8dSAndroid Build Coastguard Worker 1581*cda5da8dSAndroid Build Coastguard Worker cookies = [] 1582*cda5da8dSAndroid Build Coastguard Worker for tup in cookie_tuples: 1583*cda5da8dSAndroid Build Coastguard Worker cookie = self._cookie_from_cookie_tuple(tup, request) 1584*cda5da8dSAndroid Build Coastguard Worker if cookie: cookies.append(cookie) 1585*cda5da8dSAndroid Build Coastguard Worker return cookies 1586*cda5da8dSAndroid Build Coastguard Worker 1587*cda5da8dSAndroid Build Coastguard Worker def _process_rfc2109_cookies(self, cookies): 1588*cda5da8dSAndroid Build Coastguard Worker rfc2109_as_ns = getattr(self._policy, 'rfc2109_as_netscape', None) 1589*cda5da8dSAndroid Build Coastguard Worker if rfc2109_as_ns is None: 1590*cda5da8dSAndroid Build Coastguard Worker rfc2109_as_ns = not self._policy.rfc2965 1591*cda5da8dSAndroid Build Coastguard Worker for cookie in cookies: 1592*cda5da8dSAndroid Build Coastguard Worker if cookie.version == 1: 1593*cda5da8dSAndroid Build Coastguard Worker cookie.rfc2109 = True 1594*cda5da8dSAndroid Build Coastguard Worker if rfc2109_as_ns: 1595*cda5da8dSAndroid Build Coastguard Worker # treat 2109 cookies as Netscape cookies rather than 1596*cda5da8dSAndroid Build Coastguard Worker # as RFC2965 cookies 1597*cda5da8dSAndroid Build Coastguard Worker cookie.version = 0 1598*cda5da8dSAndroid Build Coastguard Worker 1599*cda5da8dSAndroid Build Coastguard Worker def make_cookies(self, response, request): 1600*cda5da8dSAndroid Build Coastguard Worker """Return sequence of Cookie objects extracted from response object.""" 1601*cda5da8dSAndroid Build Coastguard Worker # get cookie-attributes for RFC 2965 and Netscape protocols 1602*cda5da8dSAndroid Build Coastguard Worker headers = response.info() 1603*cda5da8dSAndroid Build Coastguard Worker rfc2965_hdrs = headers.get_all("Set-Cookie2", []) 1604*cda5da8dSAndroid Build Coastguard Worker ns_hdrs = headers.get_all("Set-Cookie", []) 1605*cda5da8dSAndroid Build Coastguard Worker self._policy._now = self._now = int(time.time()) 1606*cda5da8dSAndroid Build Coastguard Worker 1607*cda5da8dSAndroid Build Coastguard Worker rfc2965 = self._policy.rfc2965 1608*cda5da8dSAndroid Build Coastguard Worker netscape = self._policy.netscape 1609*cda5da8dSAndroid Build Coastguard Worker 1610*cda5da8dSAndroid Build Coastguard Worker if ((not rfc2965_hdrs and not ns_hdrs) or 1611*cda5da8dSAndroid Build Coastguard Worker (not ns_hdrs and not rfc2965) or 1612*cda5da8dSAndroid Build Coastguard Worker (not rfc2965_hdrs and not netscape) or 1613*cda5da8dSAndroid Build Coastguard Worker (not netscape and not rfc2965)): 1614*cda5da8dSAndroid Build Coastguard Worker return [] # no relevant cookie headers: quick exit 1615*cda5da8dSAndroid Build Coastguard Worker 1616*cda5da8dSAndroid Build Coastguard Worker try: 1617*cda5da8dSAndroid Build Coastguard Worker cookies = self._cookies_from_attrs_set( 1618*cda5da8dSAndroid Build Coastguard Worker split_header_words(rfc2965_hdrs), request) 1619*cda5da8dSAndroid Build Coastguard Worker except Exception: 1620*cda5da8dSAndroid Build Coastguard Worker _warn_unhandled_exception() 1621*cda5da8dSAndroid Build Coastguard Worker cookies = [] 1622*cda5da8dSAndroid Build Coastguard Worker 1623*cda5da8dSAndroid Build Coastguard Worker if ns_hdrs and netscape: 1624*cda5da8dSAndroid Build Coastguard Worker try: 1625*cda5da8dSAndroid Build Coastguard Worker # RFC 2109 and Netscape cookies 1626*cda5da8dSAndroid Build Coastguard Worker ns_cookies = self._cookies_from_attrs_set( 1627*cda5da8dSAndroid Build Coastguard Worker parse_ns_headers(ns_hdrs), request) 1628*cda5da8dSAndroid Build Coastguard Worker except Exception: 1629*cda5da8dSAndroid Build Coastguard Worker _warn_unhandled_exception() 1630*cda5da8dSAndroid Build Coastguard Worker ns_cookies = [] 1631*cda5da8dSAndroid Build Coastguard Worker self._process_rfc2109_cookies(ns_cookies) 1632*cda5da8dSAndroid Build Coastguard Worker 1633*cda5da8dSAndroid Build Coastguard Worker # Look for Netscape cookies (from Set-Cookie headers) that match 1634*cda5da8dSAndroid Build Coastguard Worker # corresponding RFC 2965 cookies (from Set-Cookie2 headers). 1635*cda5da8dSAndroid Build Coastguard Worker # For each match, keep the RFC 2965 cookie and ignore the Netscape 1636*cda5da8dSAndroid Build Coastguard Worker # cookie (RFC 2965 section 9.1). Actually, RFC 2109 cookies are 1637*cda5da8dSAndroid Build Coastguard Worker # bundled in with the Netscape cookies for this purpose, which is 1638*cda5da8dSAndroid Build Coastguard Worker # reasonable behaviour. 1639*cda5da8dSAndroid Build Coastguard Worker if rfc2965: 1640*cda5da8dSAndroid Build Coastguard Worker lookup = {} 1641*cda5da8dSAndroid Build Coastguard Worker for cookie in cookies: 1642*cda5da8dSAndroid Build Coastguard Worker lookup[(cookie.domain, cookie.path, cookie.name)] = None 1643*cda5da8dSAndroid Build Coastguard Worker 1644*cda5da8dSAndroid Build Coastguard Worker def no_matching_rfc2965(ns_cookie, lookup=lookup): 1645*cda5da8dSAndroid Build Coastguard Worker key = ns_cookie.domain, ns_cookie.path, ns_cookie.name 1646*cda5da8dSAndroid Build Coastguard Worker return key not in lookup 1647*cda5da8dSAndroid Build Coastguard Worker ns_cookies = filter(no_matching_rfc2965, ns_cookies) 1648*cda5da8dSAndroid Build Coastguard Worker 1649*cda5da8dSAndroid Build Coastguard Worker if ns_cookies: 1650*cda5da8dSAndroid Build Coastguard Worker cookies.extend(ns_cookies) 1651*cda5da8dSAndroid Build Coastguard Worker 1652*cda5da8dSAndroid Build Coastguard Worker return cookies 1653*cda5da8dSAndroid Build Coastguard Worker 1654*cda5da8dSAndroid Build Coastguard Worker def set_cookie_if_ok(self, cookie, request): 1655*cda5da8dSAndroid Build Coastguard Worker """Set a cookie if policy says it's OK to do so.""" 1656*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.acquire() 1657*cda5da8dSAndroid Build Coastguard Worker try: 1658*cda5da8dSAndroid Build Coastguard Worker self._policy._now = self._now = int(time.time()) 1659*cda5da8dSAndroid Build Coastguard Worker 1660*cda5da8dSAndroid Build Coastguard Worker if self._policy.set_ok(cookie, request): 1661*cda5da8dSAndroid Build Coastguard Worker self.set_cookie(cookie) 1662*cda5da8dSAndroid Build Coastguard Worker 1663*cda5da8dSAndroid Build Coastguard Worker 1664*cda5da8dSAndroid Build Coastguard Worker finally: 1665*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.release() 1666*cda5da8dSAndroid Build Coastguard Worker 1667*cda5da8dSAndroid Build Coastguard Worker def set_cookie(self, cookie): 1668*cda5da8dSAndroid Build Coastguard Worker """Set a cookie, without checking whether or not it should be set.""" 1669*cda5da8dSAndroid Build Coastguard Worker c = self._cookies 1670*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.acquire() 1671*cda5da8dSAndroid Build Coastguard Worker try: 1672*cda5da8dSAndroid Build Coastguard Worker if cookie.domain not in c: c[cookie.domain] = {} 1673*cda5da8dSAndroid Build Coastguard Worker c2 = c[cookie.domain] 1674*cda5da8dSAndroid Build Coastguard Worker if cookie.path not in c2: c2[cookie.path] = {} 1675*cda5da8dSAndroid Build Coastguard Worker c3 = c2[cookie.path] 1676*cda5da8dSAndroid Build Coastguard Worker c3[cookie.name] = cookie 1677*cda5da8dSAndroid Build Coastguard Worker finally: 1678*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.release() 1679*cda5da8dSAndroid Build Coastguard Worker 1680*cda5da8dSAndroid Build Coastguard Worker def extract_cookies(self, response, request): 1681*cda5da8dSAndroid Build Coastguard Worker """Extract cookies from response, where allowable given the request.""" 1682*cda5da8dSAndroid Build Coastguard Worker _debug("extract_cookies: %s", response.info()) 1683*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.acquire() 1684*cda5da8dSAndroid Build Coastguard Worker try: 1685*cda5da8dSAndroid Build Coastguard Worker for cookie in self.make_cookies(response, request): 1686*cda5da8dSAndroid Build Coastguard Worker if self._policy.set_ok(cookie, request): 1687*cda5da8dSAndroid Build Coastguard Worker _debug(" setting cookie: %s", cookie) 1688*cda5da8dSAndroid Build Coastguard Worker self.set_cookie(cookie) 1689*cda5da8dSAndroid Build Coastguard Worker finally: 1690*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.release() 1691*cda5da8dSAndroid Build Coastguard Worker 1692*cda5da8dSAndroid Build Coastguard Worker def clear(self, domain=None, path=None, name=None): 1693*cda5da8dSAndroid Build Coastguard Worker """Clear some cookies. 1694*cda5da8dSAndroid Build Coastguard Worker 1695*cda5da8dSAndroid Build Coastguard Worker Invoking this method without arguments will clear all cookies. If 1696*cda5da8dSAndroid Build Coastguard Worker given a single argument, only cookies belonging to that domain will be 1697*cda5da8dSAndroid Build Coastguard Worker removed. If given two arguments, cookies belonging to the specified 1698*cda5da8dSAndroid Build Coastguard Worker path within that domain are removed. If given three arguments, then 1699*cda5da8dSAndroid Build Coastguard Worker the cookie with the specified name, path and domain is removed. 1700*cda5da8dSAndroid Build Coastguard Worker 1701*cda5da8dSAndroid Build Coastguard Worker Raises KeyError if no matching cookie exists. 1702*cda5da8dSAndroid Build Coastguard Worker 1703*cda5da8dSAndroid Build Coastguard Worker """ 1704*cda5da8dSAndroid Build Coastguard Worker if name is not None: 1705*cda5da8dSAndroid Build Coastguard Worker if (domain is None) or (path is None): 1706*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1707*cda5da8dSAndroid Build Coastguard Worker "domain and path must be given to remove a cookie by name") 1708*cda5da8dSAndroid Build Coastguard Worker del self._cookies[domain][path][name] 1709*cda5da8dSAndroid Build Coastguard Worker elif path is not None: 1710*cda5da8dSAndroid Build Coastguard Worker if domain is None: 1711*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1712*cda5da8dSAndroid Build Coastguard Worker "domain must be given to remove cookies by path") 1713*cda5da8dSAndroid Build Coastguard Worker del self._cookies[domain][path] 1714*cda5da8dSAndroid Build Coastguard Worker elif domain is not None: 1715*cda5da8dSAndroid Build Coastguard Worker del self._cookies[domain] 1716*cda5da8dSAndroid Build Coastguard Worker else: 1717*cda5da8dSAndroid Build Coastguard Worker self._cookies = {} 1718*cda5da8dSAndroid Build Coastguard Worker 1719*cda5da8dSAndroid Build Coastguard Worker def clear_session_cookies(self): 1720*cda5da8dSAndroid Build Coastguard Worker """Discard all session cookies. 1721*cda5da8dSAndroid Build Coastguard Worker 1722*cda5da8dSAndroid Build Coastguard Worker Note that the .save() method won't save session cookies anyway, unless 1723*cda5da8dSAndroid Build Coastguard Worker you ask otherwise by passing a true ignore_discard argument. 1724*cda5da8dSAndroid Build Coastguard Worker 1725*cda5da8dSAndroid Build Coastguard Worker """ 1726*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.acquire() 1727*cda5da8dSAndroid Build Coastguard Worker try: 1728*cda5da8dSAndroid Build Coastguard Worker for cookie in self: 1729*cda5da8dSAndroid Build Coastguard Worker if cookie.discard: 1730*cda5da8dSAndroid Build Coastguard Worker self.clear(cookie.domain, cookie.path, cookie.name) 1731*cda5da8dSAndroid Build Coastguard Worker finally: 1732*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.release() 1733*cda5da8dSAndroid Build Coastguard Worker 1734*cda5da8dSAndroid Build Coastguard Worker def clear_expired_cookies(self): 1735*cda5da8dSAndroid Build Coastguard Worker """Discard all expired cookies. 1736*cda5da8dSAndroid Build Coastguard Worker 1737*cda5da8dSAndroid Build Coastguard Worker You probably don't need to call this method: expired cookies are never 1738*cda5da8dSAndroid Build Coastguard Worker sent back to the server (provided you're using DefaultCookiePolicy), 1739*cda5da8dSAndroid Build Coastguard Worker this method is called by CookieJar itself every so often, and the 1740*cda5da8dSAndroid Build Coastguard Worker .save() method won't save expired cookies anyway (unless you ask 1741*cda5da8dSAndroid Build Coastguard Worker otherwise by passing a true ignore_expires argument). 1742*cda5da8dSAndroid Build Coastguard Worker 1743*cda5da8dSAndroid Build Coastguard Worker """ 1744*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.acquire() 1745*cda5da8dSAndroid Build Coastguard Worker try: 1746*cda5da8dSAndroid Build Coastguard Worker now = time.time() 1747*cda5da8dSAndroid Build Coastguard Worker for cookie in self: 1748*cda5da8dSAndroid Build Coastguard Worker if cookie.is_expired(now): 1749*cda5da8dSAndroid Build Coastguard Worker self.clear(cookie.domain, cookie.path, cookie.name) 1750*cda5da8dSAndroid Build Coastguard Worker finally: 1751*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.release() 1752*cda5da8dSAndroid Build Coastguard Worker 1753*cda5da8dSAndroid Build Coastguard Worker def __iter__(self): 1754*cda5da8dSAndroid Build Coastguard Worker return deepvalues(self._cookies) 1755*cda5da8dSAndroid Build Coastguard Worker 1756*cda5da8dSAndroid Build Coastguard Worker def __len__(self): 1757*cda5da8dSAndroid Build Coastguard Worker """Return number of contained cookies.""" 1758*cda5da8dSAndroid Build Coastguard Worker i = 0 1759*cda5da8dSAndroid Build Coastguard Worker for cookie in self: i = i + 1 1760*cda5da8dSAndroid Build Coastguard Worker return i 1761*cda5da8dSAndroid Build Coastguard Worker 1762*cda5da8dSAndroid Build Coastguard Worker def __repr__(self): 1763*cda5da8dSAndroid Build Coastguard Worker r = [] 1764*cda5da8dSAndroid Build Coastguard Worker for cookie in self: r.append(repr(cookie)) 1765*cda5da8dSAndroid Build Coastguard Worker return "<%s[%s]>" % (self.__class__.__name__, ", ".join(r)) 1766*cda5da8dSAndroid Build Coastguard Worker 1767*cda5da8dSAndroid Build Coastguard Worker def __str__(self): 1768*cda5da8dSAndroid Build Coastguard Worker r = [] 1769*cda5da8dSAndroid Build Coastguard Worker for cookie in self: r.append(str(cookie)) 1770*cda5da8dSAndroid Build Coastguard Worker return "<%s[%s]>" % (self.__class__.__name__, ", ".join(r)) 1771*cda5da8dSAndroid Build Coastguard Worker 1772*cda5da8dSAndroid Build Coastguard Worker 1773*cda5da8dSAndroid Build Coastguard Worker# derives from OSError for backwards-compatibility with Python 2.4.0 1774*cda5da8dSAndroid Build Coastguard Workerclass LoadError(OSError): pass 1775*cda5da8dSAndroid Build Coastguard Worker 1776*cda5da8dSAndroid Build Coastguard Workerclass FileCookieJar(CookieJar): 1777*cda5da8dSAndroid Build Coastguard Worker """CookieJar that can be loaded from and saved to a file.""" 1778*cda5da8dSAndroid Build Coastguard Worker 1779*cda5da8dSAndroid Build Coastguard Worker def __init__(self, filename=None, delayload=False, policy=None): 1780*cda5da8dSAndroid Build Coastguard Worker """ 1781*cda5da8dSAndroid Build Coastguard Worker Cookies are NOT loaded from the named file until either the .load() or 1782*cda5da8dSAndroid Build Coastguard Worker .revert() method is called. 1783*cda5da8dSAndroid Build Coastguard Worker 1784*cda5da8dSAndroid Build Coastguard Worker """ 1785*cda5da8dSAndroid Build Coastguard Worker CookieJar.__init__(self, policy) 1786*cda5da8dSAndroid Build Coastguard Worker if filename is not None: 1787*cda5da8dSAndroid Build Coastguard Worker filename = os.fspath(filename) 1788*cda5da8dSAndroid Build Coastguard Worker self.filename = filename 1789*cda5da8dSAndroid Build Coastguard Worker self.delayload = bool(delayload) 1790*cda5da8dSAndroid Build Coastguard Worker 1791*cda5da8dSAndroid Build Coastguard Worker def save(self, filename=None, ignore_discard=False, ignore_expires=False): 1792*cda5da8dSAndroid Build Coastguard Worker """Save cookies to a file.""" 1793*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError() 1794*cda5da8dSAndroid Build Coastguard Worker 1795*cda5da8dSAndroid Build Coastguard Worker def load(self, filename=None, ignore_discard=False, ignore_expires=False): 1796*cda5da8dSAndroid Build Coastguard Worker """Load cookies from a file.""" 1797*cda5da8dSAndroid Build Coastguard Worker if filename is None: 1798*cda5da8dSAndroid Build Coastguard Worker if self.filename is not None: filename = self.filename 1799*cda5da8dSAndroid Build Coastguard Worker else: raise ValueError(MISSING_FILENAME_TEXT) 1800*cda5da8dSAndroid Build Coastguard Worker 1801*cda5da8dSAndroid Build Coastguard Worker with open(filename) as f: 1802*cda5da8dSAndroid Build Coastguard Worker self._really_load(f, filename, ignore_discard, ignore_expires) 1803*cda5da8dSAndroid Build Coastguard Worker 1804*cda5da8dSAndroid Build Coastguard Worker def revert(self, filename=None, 1805*cda5da8dSAndroid Build Coastguard Worker ignore_discard=False, ignore_expires=False): 1806*cda5da8dSAndroid Build Coastguard Worker """Clear all cookies and reload cookies from a saved file. 1807*cda5da8dSAndroid Build Coastguard Worker 1808*cda5da8dSAndroid Build Coastguard Worker Raises LoadError (or OSError) if reversion is not successful; the 1809*cda5da8dSAndroid Build Coastguard Worker object's state will not be altered if this happens. 1810*cda5da8dSAndroid Build Coastguard Worker 1811*cda5da8dSAndroid Build Coastguard Worker """ 1812*cda5da8dSAndroid Build Coastguard Worker if filename is None: 1813*cda5da8dSAndroid Build Coastguard Worker if self.filename is not None: filename = self.filename 1814*cda5da8dSAndroid Build Coastguard Worker else: raise ValueError(MISSING_FILENAME_TEXT) 1815*cda5da8dSAndroid Build Coastguard Worker 1816*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.acquire() 1817*cda5da8dSAndroid Build Coastguard Worker try: 1818*cda5da8dSAndroid Build Coastguard Worker 1819*cda5da8dSAndroid Build Coastguard Worker old_state = copy.deepcopy(self._cookies) 1820*cda5da8dSAndroid Build Coastguard Worker self._cookies = {} 1821*cda5da8dSAndroid Build Coastguard Worker try: 1822*cda5da8dSAndroid Build Coastguard Worker self.load(filename, ignore_discard, ignore_expires) 1823*cda5da8dSAndroid Build Coastguard Worker except OSError: 1824*cda5da8dSAndroid Build Coastguard Worker self._cookies = old_state 1825*cda5da8dSAndroid Build Coastguard Worker raise 1826*cda5da8dSAndroid Build Coastguard Worker 1827*cda5da8dSAndroid Build Coastguard Worker finally: 1828*cda5da8dSAndroid Build Coastguard Worker self._cookies_lock.release() 1829*cda5da8dSAndroid Build Coastguard Worker 1830*cda5da8dSAndroid Build Coastguard Worker 1831*cda5da8dSAndroid Build Coastguard Workerdef lwp_cookie_str(cookie): 1832*cda5da8dSAndroid Build Coastguard Worker """Return string representation of Cookie in the LWP cookie file format. 1833*cda5da8dSAndroid Build Coastguard Worker 1834*cda5da8dSAndroid Build Coastguard Worker Actually, the format is extended a bit -- see module docstring. 1835*cda5da8dSAndroid Build Coastguard Worker 1836*cda5da8dSAndroid Build Coastguard Worker """ 1837*cda5da8dSAndroid Build Coastguard Worker h = [(cookie.name, cookie.value), 1838*cda5da8dSAndroid Build Coastguard Worker ("path", cookie.path), 1839*cda5da8dSAndroid Build Coastguard Worker ("domain", cookie.domain)] 1840*cda5da8dSAndroid Build Coastguard Worker if cookie.port is not None: h.append(("port", cookie.port)) 1841*cda5da8dSAndroid Build Coastguard Worker if cookie.path_specified: h.append(("path_spec", None)) 1842*cda5da8dSAndroid Build Coastguard Worker if cookie.port_specified: h.append(("port_spec", None)) 1843*cda5da8dSAndroid Build Coastguard Worker if cookie.domain_initial_dot: h.append(("domain_dot", None)) 1844*cda5da8dSAndroid Build Coastguard Worker if cookie.secure: h.append(("secure", None)) 1845*cda5da8dSAndroid Build Coastguard Worker if cookie.expires: h.append(("expires", 1846*cda5da8dSAndroid Build Coastguard Worker time2isoz(float(cookie.expires)))) 1847*cda5da8dSAndroid Build Coastguard Worker if cookie.discard: h.append(("discard", None)) 1848*cda5da8dSAndroid Build Coastguard Worker if cookie.comment: h.append(("comment", cookie.comment)) 1849*cda5da8dSAndroid Build Coastguard Worker if cookie.comment_url: h.append(("commenturl", cookie.comment_url)) 1850*cda5da8dSAndroid Build Coastguard Worker 1851*cda5da8dSAndroid Build Coastguard Worker keys = sorted(cookie._rest.keys()) 1852*cda5da8dSAndroid Build Coastguard Worker for k in keys: 1853*cda5da8dSAndroid Build Coastguard Worker h.append((k, str(cookie._rest[k]))) 1854*cda5da8dSAndroid Build Coastguard Worker 1855*cda5da8dSAndroid Build Coastguard Worker h.append(("version", str(cookie.version))) 1856*cda5da8dSAndroid Build Coastguard Worker 1857*cda5da8dSAndroid Build Coastguard Worker return join_header_words([h]) 1858*cda5da8dSAndroid Build Coastguard Worker 1859*cda5da8dSAndroid Build Coastguard Workerclass LWPCookieJar(FileCookieJar): 1860*cda5da8dSAndroid Build Coastguard Worker """ 1861*cda5da8dSAndroid Build Coastguard Worker The LWPCookieJar saves a sequence of "Set-Cookie3" lines. 1862*cda5da8dSAndroid Build Coastguard Worker "Set-Cookie3" is the format used by the libwww-perl library, not known 1863*cda5da8dSAndroid Build Coastguard Worker to be compatible with any browser, but which is easy to read and 1864*cda5da8dSAndroid Build Coastguard Worker doesn't lose information about RFC 2965 cookies. 1865*cda5da8dSAndroid Build Coastguard Worker 1866*cda5da8dSAndroid Build Coastguard Worker Additional methods 1867*cda5da8dSAndroid Build Coastguard Worker 1868*cda5da8dSAndroid Build Coastguard Worker as_lwp_str(ignore_discard=True, ignore_expired=True) 1869*cda5da8dSAndroid Build Coastguard Worker 1870*cda5da8dSAndroid Build Coastguard Worker """ 1871*cda5da8dSAndroid Build Coastguard Worker 1872*cda5da8dSAndroid Build Coastguard Worker def as_lwp_str(self, ignore_discard=True, ignore_expires=True): 1873*cda5da8dSAndroid Build Coastguard Worker """Return cookies as a string of "\\n"-separated "Set-Cookie3" headers. 1874*cda5da8dSAndroid Build Coastguard Worker 1875*cda5da8dSAndroid Build Coastguard Worker ignore_discard and ignore_expires: see docstring for FileCookieJar.save 1876*cda5da8dSAndroid Build Coastguard Worker 1877*cda5da8dSAndroid Build Coastguard Worker """ 1878*cda5da8dSAndroid Build Coastguard Worker now = time.time() 1879*cda5da8dSAndroid Build Coastguard Worker r = [] 1880*cda5da8dSAndroid Build Coastguard Worker for cookie in self: 1881*cda5da8dSAndroid Build Coastguard Worker if not ignore_discard and cookie.discard: 1882*cda5da8dSAndroid Build Coastguard Worker continue 1883*cda5da8dSAndroid Build Coastguard Worker if not ignore_expires and cookie.is_expired(now): 1884*cda5da8dSAndroid Build Coastguard Worker continue 1885*cda5da8dSAndroid Build Coastguard Worker r.append("Set-Cookie3: %s" % lwp_cookie_str(cookie)) 1886*cda5da8dSAndroid Build Coastguard Worker return "\n".join(r+[""]) 1887*cda5da8dSAndroid Build Coastguard Worker 1888*cda5da8dSAndroid Build Coastguard Worker def save(self, filename=None, ignore_discard=False, ignore_expires=False): 1889*cda5da8dSAndroid Build Coastguard Worker if filename is None: 1890*cda5da8dSAndroid Build Coastguard Worker if self.filename is not None: filename = self.filename 1891*cda5da8dSAndroid Build Coastguard Worker else: raise ValueError(MISSING_FILENAME_TEXT) 1892*cda5da8dSAndroid Build Coastguard Worker 1893*cda5da8dSAndroid Build Coastguard Worker with os.fdopen( 1894*cda5da8dSAndroid Build Coastguard Worker os.open(filename, os.O_CREAT | os.O_WRONLY | os.O_TRUNC, 0o600), 1895*cda5da8dSAndroid Build Coastguard Worker 'w', 1896*cda5da8dSAndroid Build Coastguard Worker ) as f: 1897*cda5da8dSAndroid Build Coastguard Worker # There really isn't an LWP Cookies 2.0 format, but this indicates 1898*cda5da8dSAndroid Build Coastguard Worker # that there is extra information in here (domain_dot and 1899*cda5da8dSAndroid Build Coastguard Worker # port_spec) while still being compatible with libwww-perl, I hope. 1900*cda5da8dSAndroid Build Coastguard Worker f.write("#LWP-Cookies-2.0\n") 1901*cda5da8dSAndroid Build Coastguard Worker f.write(self.as_lwp_str(ignore_discard, ignore_expires)) 1902*cda5da8dSAndroid Build Coastguard Worker 1903*cda5da8dSAndroid Build Coastguard Worker def _really_load(self, f, filename, ignore_discard, ignore_expires): 1904*cda5da8dSAndroid Build Coastguard Worker magic = f.readline() 1905*cda5da8dSAndroid Build Coastguard Worker if not self.magic_re.search(magic): 1906*cda5da8dSAndroid Build Coastguard Worker msg = ("%r does not look like a Set-Cookie3 (LWP) format " 1907*cda5da8dSAndroid Build Coastguard Worker "file" % filename) 1908*cda5da8dSAndroid Build Coastguard Worker raise LoadError(msg) 1909*cda5da8dSAndroid Build Coastguard Worker 1910*cda5da8dSAndroid Build Coastguard Worker now = time.time() 1911*cda5da8dSAndroid Build Coastguard Worker 1912*cda5da8dSAndroid Build Coastguard Worker header = "Set-Cookie3:" 1913*cda5da8dSAndroid Build Coastguard Worker boolean_attrs = ("port_spec", "path_spec", "domain_dot", 1914*cda5da8dSAndroid Build Coastguard Worker "secure", "discard") 1915*cda5da8dSAndroid Build Coastguard Worker value_attrs = ("version", 1916*cda5da8dSAndroid Build Coastguard Worker "port", "path", "domain", 1917*cda5da8dSAndroid Build Coastguard Worker "expires", 1918*cda5da8dSAndroid Build Coastguard Worker "comment", "commenturl") 1919*cda5da8dSAndroid Build Coastguard Worker 1920*cda5da8dSAndroid Build Coastguard Worker try: 1921*cda5da8dSAndroid Build Coastguard Worker while 1: 1922*cda5da8dSAndroid Build Coastguard Worker line = f.readline() 1923*cda5da8dSAndroid Build Coastguard Worker if line == "": break 1924*cda5da8dSAndroid Build Coastguard Worker if not line.startswith(header): 1925*cda5da8dSAndroid Build Coastguard Worker continue 1926*cda5da8dSAndroid Build Coastguard Worker line = line[len(header):].strip() 1927*cda5da8dSAndroid Build Coastguard Worker 1928*cda5da8dSAndroid Build Coastguard Worker for data in split_header_words([line]): 1929*cda5da8dSAndroid Build Coastguard Worker name, value = data[0] 1930*cda5da8dSAndroid Build Coastguard Worker standard = {} 1931*cda5da8dSAndroid Build Coastguard Worker rest = {} 1932*cda5da8dSAndroid Build Coastguard Worker for k in boolean_attrs: 1933*cda5da8dSAndroid Build Coastguard Worker standard[k] = False 1934*cda5da8dSAndroid Build Coastguard Worker for k, v in data[1:]: 1935*cda5da8dSAndroid Build Coastguard Worker if k is not None: 1936*cda5da8dSAndroid Build Coastguard Worker lc = k.lower() 1937*cda5da8dSAndroid Build Coastguard Worker else: 1938*cda5da8dSAndroid Build Coastguard Worker lc = None 1939*cda5da8dSAndroid Build Coastguard Worker # don't lose case distinction for unknown fields 1940*cda5da8dSAndroid Build Coastguard Worker if (lc in value_attrs) or (lc in boolean_attrs): 1941*cda5da8dSAndroid Build Coastguard Worker k = lc 1942*cda5da8dSAndroid Build Coastguard Worker if k in boolean_attrs: 1943*cda5da8dSAndroid Build Coastguard Worker if v is None: v = True 1944*cda5da8dSAndroid Build Coastguard Worker standard[k] = v 1945*cda5da8dSAndroid Build Coastguard Worker elif k in value_attrs: 1946*cda5da8dSAndroid Build Coastguard Worker standard[k] = v 1947*cda5da8dSAndroid Build Coastguard Worker else: 1948*cda5da8dSAndroid Build Coastguard Worker rest[k] = v 1949*cda5da8dSAndroid Build Coastguard Worker 1950*cda5da8dSAndroid Build Coastguard Worker h = standard.get 1951*cda5da8dSAndroid Build Coastguard Worker expires = h("expires") 1952*cda5da8dSAndroid Build Coastguard Worker discard = h("discard") 1953*cda5da8dSAndroid Build Coastguard Worker if expires is not None: 1954*cda5da8dSAndroid Build Coastguard Worker expires = iso2time(expires) 1955*cda5da8dSAndroid Build Coastguard Worker if expires is None: 1956*cda5da8dSAndroid Build Coastguard Worker discard = True 1957*cda5da8dSAndroid Build Coastguard Worker domain = h("domain") 1958*cda5da8dSAndroid Build Coastguard Worker domain_specified = domain.startswith(".") 1959*cda5da8dSAndroid Build Coastguard Worker c = Cookie(h("version"), name, value, 1960*cda5da8dSAndroid Build Coastguard Worker h("port"), h("port_spec"), 1961*cda5da8dSAndroid Build Coastguard Worker domain, domain_specified, h("domain_dot"), 1962*cda5da8dSAndroid Build Coastguard Worker h("path"), h("path_spec"), 1963*cda5da8dSAndroid Build Coastguard Worker h("secure"), 1964*cda5da8dSAndroid Build Coastguard Worker expires, 1965*cda5da8dSAndroid Build Coastguard Worker discard, 1966*cda5da8dSAndroid Build Coastguard Worker h("comment"), 1967*cda5da8dSAndroid Build Coastguard Worker h("commenturl"), 1968*cda5da8dSAndroid Build Coastguard Worker rest) 1969*cda5da8dSAndroid Build Coastguard Worker if not ignore_discard and c.discard: 1970*cda5da8dSAndroid Build Coastguard Worker continue 1971*cda5da8dSAndroid Build Coastguard Worker if not ignore_expires and c.is_expired(now): 1972*cda5da8dSAndroid Build Coastguard Worker continue 1973*cda5da8dSAndroid Build Coastguard Worker self.set_cookie(c) 1974*cda5da8dSAndroid Build Coastguard Worker except OSError: 1975*cda5da8dSAndroid Build Coastguard Worker raise 1976*cda5da8dSAndroid Build Coastguard Worker except Exception: 1977*cda5da8dSAndroid Build Coastguard Worker _warn_unhandled_exception() 1978*cda5da8dSAndroid Build Coastguard Worker raise LoadError("invalid Set-Cookie3 format file %r: %r" % 1979*cda5da8dSAndroid Build Coastguard Worker (filename, line)) 1980*cda5da8dSAndroid Build Coastguard Worker 1981*cda5da8dSAndroid Build Coastguard Worker 1982*cda5da8dSAndroid Build Coastguard Workerclass MozillaCookieJar(FileCookieJar): 1983*cda5da8dSAndroid Build Coastguard Worker """ 1984*cda5da8dSAndroid Build Coastguard Worker 1985*cda5da8dSAndroid Build Coastguard Worker WARNING: you may want to backup your browser's cookies file if you use 1986*cda5da8dSAndroid Build Coastguard Worker this class to save cookies. I *think* it works, but there have been 1987*cda5da8dSAndroid Build Coastguard Worker bugs in the past! 1988*cda5da8dSAndroid Build Coastguard Worker 1989*cda5da8dSAndroid Build Coastguard Worker This class differs from CookieJar only in the format it uses to save and 1990*cda5da8dSAndroid Build Coastguard Worker load cookies to and from a file. This class uses the Mozilla/Netscape 1991*cda5da8dSAndroid Build Coastguard Worker `cookies.txt' format. curl and lynx use this file format, too. 1992*cda5da8dSAndroid Build Coastguard Worker 1993*cda5da8dSAndroid Build Coastguard Worker Don't expect cookies saved while the browser is running to be noticed by 1994*cda5da8dSAndroid Build Coastguard Worker the browser (in fact, Mozilla on unix will overwrite your saved cookies if 1995*cda5da8dSAndroid Build Coastguard Worker you change them on disk while it's running; on Windows, you probably can't 1996*cda5da8dSAndroid Build Coastguard Worker save at all while the browser is running). 1997*cda5da8dSAndroid Build Coastguard Worker 1998*cda5da8dSAndroid Build Coastguard Worker Note that the Mozilla/Netscape format will downgrade RFC2965 cookies to 1999*cda5da8dSAndroid Build Coastguard Worker Netscape cookies on saving. 2000*cda5da8dSAndroid Build Coastguard Worker 2001*cda5da8dSAndroid Build Coastguard Worker In particular, the cookie version and port number information is lost, 2002*cda5da8dSAndroid Build Coastguard Worker together with information about whether or not Path, Port and Discard were 2003*cda5da8dSAndroid Build Coastguard Worker specified by the Set-Cookie2 (or Set-Cookie) header, and whether or not the 2004*cda5da8dSAndroid Build Coastguard Worker domain as set in the HTTP header started with a dot (yes, I'm aware some 2005*cda5da8dSAndroid Build Coastguard Worker domains in Netscape files start with a dot and some don't -- trust me, you 2006*cda5da8dSAndroid Build Coastguard Worker really don't want to know any more about this). 2007*cda5da8dSAndroid Build Coastguard Worker 2008*cda5da8dSAndroid Build Coastguard Worker Note that though Mozilla and Netscape use the same format, they use 2009*cda5da8dSAndroid Build Coastguard Worker slightly different headers. The class saves cookies using the Netscape 2010*cda5da8dSAndroid Build Coastguard Worker header by default (Mozilla can cope with that). 2011*cda5da8dSAndroid Build Coastguard Worker 2012*cda5da8dSAndroid Build Coastguard Worker """ 2013*cda5da8dSAndroid Build Coastguard Worker 2014*cda5da8dSAndroid Build Coastguard Worker def _really_load(self, f, filename, ignore_discard, ignore_expires): 2015*cda5da8dSAndroid Build Coastguard Worker now = time.time() 2016*cda5da8dSAndroid Build Coastguard Worker 2017*cda5da8dSAndroid Build Coastguard Worker if not NETSCAPE_MAGIC_RGX.match(f.readline()): 2018*cda5da8dSAndroid Build Coastguard Worker raise LoadError( 2019*cda5da8dSAndroid Build Coastguard Worker "%r does not look like a Netscape format cookies file" % 2020*cda5da8dSAndroid Build Coastguard Worker filename) 2021*cda5da8dSAndroid Build Coastguard Worker 2022*cda5da8dSAndroid Build Coastguard Worker try: 2023*cda5da8dSAndroid Build Coastguard Worker while 1: 2024*cda5da8dSAndroid Build Coastguard Worker line = f.readline() 2025*cda5da8dSAndroid Build Coastguard Worker rest = {} 2026*cda5da8dSAndroid Build Coastguard Worker 2027*cda5da8dSAndroid Build Coastguard Worker if line == "": break 2028*cda5da8dSAndroid Build Coastguard Worker 2029*cda5da8dSAndroid Build Coastguard Worker # httponly is a cookie flag as defined in rfc6265 2030*cda5da8dSAndroid Build Coastguard Worker # when encoded in a netscape cookie file, 2031*cda5da8dSAndroid Build Coastguard Worker # the line is prepended with "#HttpOnly_" 2032*cda5da8dSAndroid Build Coastguard Worker if line.startswith(HTTPONLY_PREFIX): 2033*cda5da8dSAndroid Build Coastguard Worker rest[HTTPONLY_ATTR] = "" 2034*cda5da8dSAndroid Build Coastguard Worker line = line[len(HTTPONLY_PREFIX):] 2035*cda5da8dSAndroid Build Coastguard Worker 2036*cda5da8dSAndroid Build Coastguard Worker # last field may be absent, so keep any trailing tab 2037*cda5da8dSAndroid Build Coastguard Worker if line.endswith("\n"): line = line[:-1] 2038*cda5da8dSAndroid Build Coastguard Worker 2039*cda5da8dSAndroid Build Coastguard Worker # skip comments and blank lines XXX what is $ for? 2040*cda5da8dSAndroid Build Coastguard Worker if (line.strip().startswith(("#", "$")) or 2041*cda5da8dSAndroid Build Coastguard Worker line.strip() == ""): 2042*cda5da8dSAndroid Build Coastguard Worker continue 2043*cda5da8dSAndroid Build Coastguard Worker 2044*cda5da8dSAndroid Build Coastguard Worker domain, domain_specified, path, secure, expires, name, value = \ 2045*cda5da8dSAndroid Build Coastguard Worker line.split("\t") 2046*cda5da8dSAndroid Build Coastguard Worker secure = (secure == "TRUE") 2047*cda5da8dSAndroid Build Coastguard Worker domain_specified = (domain_specified == "TRUE") 2048*cda5da8dSAndroid Build Coastguard Worker if name == "": 2049*cda5da8dSAndroid Build Coastguard Worker # cookies.txt regards 'Set-Cookie: foo' as a cookie 2050*cda5da8dSAndroid Build Coastguard Worker # with no name, whereas http.cookiejar regards it as a 2051*cda5da8dSAndroid Build Coastguard Worker # cookie with no value. 2052*cda5da8dSAndroid Build Coastguard Worker name = value 2053*cda5da8dSAndroid Build Coastguard Worker value = None 2054*cda5da8dSAndroid Build Coastguard Worker 2055*cda5da8dSAndroid Build Coastguard Worker initial_dot = domain.startswith(".") 2056*cda5da8dSAndroid Build Coastguard Worker assert domain_specified == initial_dot 2057*cda5da8dSAndroid Build Coastguard Worker 2058*cda5da8dSAndroid Build Coastguard Worker discard = False 2059*cda5da8dSAndroid Build Coastguard Worker if expires == "": 2060*cda5da8dSAndroid Build Coastguard Worker expires = None 2061*cda5da8dSAndroid Build Coastguard Worker discard = True 2062*cda5da8dSAndroid Build Coastguard Worker 2063*cda5da8dSAndroid Build Coastguard Worker # assume path_specified is false 2064*cda5da8dSAndroid Build Coastguard Worker c = Cookie(0, name, value, 2065*cda5da8dSAndroid Build Coastguard Worker None, False, 2066*cda5da8dSAndroid Build Coastguard Worker domain, domain_specified, initial_dot, 2067*cda5da8dSAndroid Build Coastguard Worker path, False, 2068*cda5da8dSAndroid Build Coastguard Worker secure, 2069*cda5da8dSAndroid Build Coastguard Worker expires, 2070*cda5da8dSAndroid Build Coastguard Worker discard, 2071*cda5da8dSAndroid Build Coastguard Worker None, 2072*cda5da8dSAndroid Build Coastguard Worker None, 2073*cda5da8dSAndroid Build Coastguard Worker rest) 2074*cda5da8dSAndroid Build Coastguard Worker if not ignore_discard and c.discard: 2075*cda5da8dSAndroid Build Coastguard Worker continue 2076*cda5da8dSAndroid Build Coastguard Worker if not ignore_expires and c.is_expired(now): 2077*cda5da8dSAndroid Build Coastguard Worker continue 2078*cda5da8dSAndroid Build Coastguard Worker self.set_cookie(c) 2079*cda5da8dSAndroid Build Coastguard Worker 2080*cda5da8dSAndroid Build Coastguard Worker except OSError: 2081*cda5da8dSAndroid Build Coastguard Worker raise 2082*cda5da8dSAndroid Build Coastguard Worker except Exception: 2083*cda5da8dSAndroid Build Coastguard Worker _warn_unhandled_exception() 2084*cda5da8dSAndroid Build Coastguard Worker raise LoadError("invalid Netscape format cookies file %r: %r" % 2085*cda5da8dSAndroid Build Coastguard Worker (filename, line)) 2086*cda5da8dSAndroid Build Coastguard Worker 2087*cda5da8dSAndroid Build Coastguard Worker def save(self, filename=None, ignore_discard=False, ignore_expires=False): 2088*cda5da8dSAndroid Build Coastguard Worker if filename is None: 2089*cda5da8dSAndroid Build Coastguard Worker if self.filename is not None: filename = self.filename 2090*cda5da8dSAndroid Build Coastguard Worker else: raise ValueError(MISSING_FILENAME_TEXT) 2091*cda5da8dSAndroid Build Coastguard Worker 2092*cda5da8dSAndroid Build Coastguard Worker with os.fdopen( 2093*cda5da8dSAndroid Build Coastguard Worker os.open(filename, os.O_CREAT | os.O_WRONLY | os.O_TRUNC, 0o600), 2094*cda5da8dSAndroid Build Coastguard Worker 'w', 2095*cda5da8dSAndroid Build Coastguard Worker ) as f: 2096*cda5da8dSAndroid Build Coastguard Worker f.write(NETSCAPE_HEADER_TEXT) 2097*cda5da8dSAndroid Build Coastguard Worker now = time.time() 2098*cda5da8dSAndroid Build Coastguard Worker for cookie in self: 2099*cda5da8dSAndroid Build Coastguard Worker domain = cookie.domain 2100*cda5da8dSAndroid Build Coastguard Worker if not ignore_discard and cookie.discard: 2101*cda5da8dSAndroid Build Coastguard Worker continue 2102*cda5da8dSAndroid Build Coastguard Worker if not ignore_expires and cookie.is_expired(now): 2103*cda5da8dSAndroid Build Coastguard Worker continue 2104*cda5da8dSAndroid Build Coastguard Worker if cookie.secure: secure = "TRUE" 2105*cda5da8dSAndroid Build Coastguard Worker else: secure = "FALSE" 2106*cda5da8dSAndroid Build Coastguard Worker if domain.startswith("."): initial_dot = "TRUE" 2107*cda5da8dSAndroid Build Coastguard Worker else: initial_dot = "FALSE" 2108*cda5da8dSAndroid Build Coastguard Worker if cookie.expires is not None: 2109*cda5da8dSAndroid Build Coastguard Worker expires = str(cookie.expires) 2110*cda5da8dSAndroid Build Coastguard Worker else: 2111*cda5da8dSAndroid Build Coastguard Worker expires = "" 2112*cda5da8dSAndroid Build Coastguard Worker if cookie.value is None: 2113*cda5da8dSAndroid Build Coastguard Worker # cookies.txt regards 'Set-Cookie: foo' as a cookie 2114*cda5da8dSAndroid Build Coastguard Worker # with no name, whereas http.cookiejar regards it as a 2115*cda5da8dSAndroid Build Coastguard Worker # cookie with no value. 2116*cda5da8dSAndroid Build Coastguard Worker name = "" 2117*cda5da8dSAndroid Build Coastguard Worker value = cookie.name 2118*cda5da8dSAndroid Build Coastguard Worker else: 2119*cda5da8dSAndroid Build Coastguard Worker name = cookie.name 2120*cda5da8dSAndroid Build Coastguard Worker value = cookie.value 2121*cda5da8dSAndroid Build Coastguard Worker if cookie.has_nonstandard_attr(HTTPONLY_ATTR): 2122*cda5da8dSAndroid Build Coastguard Worker domain = HTTPONLY_PREFIX + domain 2123*cda5da8dSAndroid Build Coastguard Worker f.write( 2124*cda5da8dSAndroid Build Coastguard Worker "\t".join([domain, initial_dot, cookie.path, 2125*cda5da8dSAndroid Build Coastguard Worker secure, expires, name, value])+ 2126*cda5da8dSAndroid Build Coastguard Worker "\n") 2127