Index: Lib/_MozillaCookieJar.py =================================================================== --- Lib/_MozillaCookieJar.py (revision 61352) +++ Lib/_MozillaCookieJar.py (working copy) @@ -1,6 +1,11 @@ """Mozilla / Netscape cookie loading / saving.""" import re, time + +try: + import sqlite3 as sqlite +except ImportError: + sqlite = None from cookielib import (_warn_unhandled_exception, FileCookieJar, LoadError, Cookie, MISSING_FILENAME_TEXT) @@ -43,107 +48,199 @@ # This is a generated file! Do not edit. """ + TYPE_TXT, TYPE_SQLITE3 = (1,2) 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) + if re.search(self.magic_re, magic): + self.data_type = self.TYPE_TXT + try: + while 1: + line = f.readline() + if line == "": break - 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] - # 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 - # 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 = \ + 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 + 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 + initial_dot = domain.startswith(".") + assert domain_specified == initial_dot - discard = False - if expires == "": - expires = None - discard = True + 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) + # 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)) + except IOError: + raise + except Exception: + _warn_unhandled_exception() + raise LoadError("invalid Netscape format cookies file %r: %r" % + (filename, line)) + elif magic.startswith("SQLite format 3") and sqlite: + self.data_type = self.TYPE_SQLITE3 + #cookie spec: + # TABLE moz_cookies + # id INTEGER PRIMARY KEY, + # name TEXT, + # value TEXT, + # host TEXT, + # path TEXT, + # expiry INTEGER, + # lastAccessed INTEGER, + # isSecure INTEGER, + # isHttpOnly INTEGER + try: + con = sqlite.connect(filename) + con.row_factory = sqlite.Row - def save(self, filename=None, ignore_discard=False, ignore_expires=False): + cur = con.cursor() + cur.execute("select * from moz_cookies") + + for row in cur: + # assume path_specified is false + c = Cookie(0, row["name"], row["value"], + None, False, + row["host"], row["host"].startswith("."), + row["host"].startswith("."), + row["path"], False, + bool(row["isSecure"]), + row["expiry"], + False, + None, + None, + { + "lastAccessed": row["lastAccessed"], + "isHttpOnly" : row["isHttpOnly"] + }) + if not ignore_discard and c.discard: + continue + if not ignore_expires and c.is_expired(now): + continue + self.set_cookie(c) + except Exception: + _warn_unhandled_exception() + raise LoadError( + "%r does not look like a FF3 format cookies file" % + filename) + finally: + cur.close() + con.close() + else: + f.close() + raise LoadError( + "%r does not look like a Netscape format cookies file" % + filename) + + + def save(self, filename=None, ignore_discard=False, + ignore_expires=False, data_type=None): + if data_type is None: + try: + data_type = self.data_type + except AttributeError: + data_type = self.TYPE_TXT + if filename is None: if self.filename is not None: filename = self.filename else: raise ValueError(MISSING_FILENAME_TEXT) + + if data_type == self.TYPE_TXT: + 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() + else: + try: + con = sqlite.connect(filename) + con.row_factory = sqlite.Row + + cur = con.cursor() + cur.execute("delete from moz_cookies") + + cookie_iter = _cookie_generator(self, ignore_discard, ignore_expires) + cur.executemany(""" + insert into moz_cookies(name, value, host, path, expiry, + lastAccessed, isSecure, isHttpOnly) + values (?,?,?,?,?,?,?,?)""", cookie_iter) - 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() + finally: + cur.close() + con.close() + +def _cookie_generator(cookies, ignore_discard, ignore_expires): + now = time.time() + for cookie in cookies: + if not ignore_discard and cookie.discard: + continue + if not ignore_expires and cookie.is_expired(now): + continue + yield (cookie.name, cookie.value, cookie.domain, cookie.path, + cookie.expires, cookie._rest.get("lastAccessed", 0), + cookie.secure, cookie._rest.get("isHttpOnly", 0)) + + + +