"""Mozilla / Netscape cookie loading / saving.""" | |
import re, time | |
from cookielib import (_warn_unhandled_exception, 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( | |
"%r 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() == ""): | |
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 IOError: | |
raise | |
except Exception: | |
_warn_unhandled_exception() | |
raise LoadError("invalid Netscape format cookies file %r: %r" % | |
(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() |