| """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://curl.haxx.se/rfc/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() |