mirror of
https://github.com/python/cpython.git
synced 2024-12-11 18:53:56 +08:00
3e7de59bd2
cookielib.LWPCookieJar and .MozillaCookieJar are documented to raise cookielib.LoadError on attempt to load an invalid cookies file, but raise IOError instead. Compromise by having LoadError subclass IOError.
149 lines
5.7 KiB
Python
149 lines
5.7 KiB
Python
"""Mozilla / Netscape cookie loading / saving."""
|
|
|
|
import re, time, logging
|
|
|
|
from cookielib import (reraise_unmasked_exceptions, FileCookieJar, LoadError,
|
|
Cookie, MISSING_FILENAME_TEXT)
|
|
|
|
class MozillaCookieJar(FileCookieJar):
|
|
"""
|
|
|
|
WARNING: you may want to backup your browser's cookies file if you use
|
|
this class to save cookies. I *think* it works, but there have been
|
|
bugs in the past!
|
|
|
|
This class differs from CookieJar only in the format it uses to save and
|
|
load cookies to and from a file. This class uses the Mozilla/Netscape
|
|
`cookies.txt' format. lynx uses this file format, too.
|
|
|
|
Don't expect cookies saved while the browser is running to be noticed by
|
|
the browser (in fact, Mozilla on unix will overwrite your saved cookies if
|
|
you change them on disk while it's running; on Windows, you probably can't
|
|
save at all while the browser is running).
|
|
|
|
Note that the Mozilla/Netscape format will downgrade RFC2965 cookies to
|
|
Netscape cookies on saving.
|
|
|
|
In particular, the cookie version and port number information is lost,
|
|
together with information about whether or not Path, Port and Discard were
|
|
specified by the Set-Cookie2 (or Set-Cookie) header, and whether or not the
|
|
domain as set in the HTTP header started with a dot (yes, I'm aware some
|
|
domains in Netscape files start with a dot and some don't -- trust me, you
|
|
really don't want to know any more about this).
|
|
|
|
Note that though Mozilla and Netscape use the same format, they use
|
|
slightly different headers. The class saves cookies using the Netscape
|
|
header by default (Mozilla can cope with that).
|
|
|
|
"""
|
|
magic_re = "#( Netscape)? HTTP Cookie File"
|
|
header = """\
|
|
# Netscape HTTP Cookie File
|
|
# http://www.netscape.com/newsref/std/cookie_spec.html
|
|
# This is a generated file! Do not edit.
|
|
|
|
"""
|
|
|
|
def _really_load(self, f, filename, ignore_discard, ignore_expires):
|
|
now = time.time()
|
|
|
|
magic = f.readline()
|
|
if not re.search(self.magic_re, magic):
|
|
f.close()
|
|
raise LoadError(
|
|
"%s does not look like a Netscape format cookies file" %
|
|
filename)
|
|
|
|
try:
|
|
while 1:
|
|
line = f.readline()
|
|
if line == "": break
|
|
|
|
# last field may be absent, so keep any trailing tab
|
|
if line.endswith("\n"): line = line[:-1]
|
|
|
|
# skip comments and blank lines XXX what is $ for?
|
|
if (line.strip().startswith("#") or
|
|
line.strip().startswith("$") or
|
|
line.strip() == ""):
|
|
continue
|
|
|
|
domain, domain_specified, path, secure, expires, name, value = \
|
|
line.split("\t")
|
|
secure = (secure == "TRUE")
|
|
domain_specified = (domain_specified == "TRUE")
|
|
if name == "":
|
|
# cookies.txt regards 'Set-Cookie: foo' as a cookie
|
|
# with no name, whereas cookielib regards it as a
|
|
# cookie with no value.
|
|
name = value
|
|
value = None
|
|
|
|
initial_dot = domain.startswith(".")
|
|
assert domain_specified == initial_dot
|
|
|
|
discard = False
|
|
if expires == "":
|
|
expires = None
|
|
discard = True
|
|
|
|
# assume path_specified is false
|
|
c = Cookie(0, name, value,
|
|
None, False,
|
|
domain, domain_specified, initial_dot,
|
|
path, False,
|
|
secure,
|
|
expires,
|
|
discard,
|
|
None,
|
|
None,
|
|
{})
|
|
if not ignore_discard and c.discard:
|
|
continue
|
|
if not ignore_expires and c.is_expired(now):
|
|
continue
|
|
self.set_cookie(c)
|
|
|
|
except:
|
|
reraise_unmasked_exceptions((IOError,))
|
|
raise LoadError("invalid Netscape format file %s: %s" %
|
|
(filename, line))
|
|
|
|
def save(self, filename=None, ignore_discard=False, ignore_expires=False):
|
|
if filename is None:
|
|
if self.filename is not None: filename = self.filename
|
|
else: raise ValueError(MISSING_FILENAME_TEXT)
|
|
|
|
f = open(filename, "w")
|
|
try:
|
|
f.write(self.header)
|
|
now = time.time()
|
|
for cookie in self:
|
|
if not ignore_discard and cookie.discard:
|
|
continue
|
|
if not ignore_expires and cookie.is_expired(now):
|
|
continue
|
|
if cookie.secure: secure = "TRUE"
|
|
else: secure = "FALSE"
|
|
if cookie.domain.startswith("."): initial_dot = "TRUE"
|
|
else: initial_dot = "FALSE"
|
|
if cookie.expires is not None:
|
|
expires = str(cookie.expires)
|
|
else:
|
|
expires = ""
|
|
if cookie.value is None:
|
|
# cookies.txt regards 'Set-Cookie: foo' as a cookie
|
|
# with no name, whereas cookielib regards it as a
|
|
# cookie with no value.
|
|
name = ""
|
|
value = cookie.name
|
|
else:
|
|
name = cookie.name
|
|
value = cookie.value
|
|
f.write(
|
|
"\t".join([cookie.domain, initial_dot, cookie.path,
|
|
secure, expires, name, value])+
|
|
"\n")
|
|
finally:
|
|
f.close()
|