1 """Mozilla / Netscape cookie loading / saving.""" 2 3 import re, time 4 5 from cookielib import (_warn_unhandled_exception, FileCookieJar, LoadError, 6 Cookie, MISSING_FILENAME_TEXT) 7 8 class MozillaCookieJar(FileCookieJar): 9 """ 10 11 WARNING: you may want to backup your browser's cookies file if you use 12 this class to save cookies. I *think* it works, but there have been 13 bugs in the past! 14 15 This class differs from CookieJar only in the format it uses to save and 16 load cookies to and from a file. This class uses the Mozilla/Netscape 17 `cookies.txt' format. lynx uses this file format, too. 18 19 Don't expect cookies saved while the browser is running to be noticed by 20 the browser (in fact, Mozilla on unix will overwrite your saved cookies if 21 you change them on disk while it's running; on Windows, you probably can't 22 save at all while the browser is running). 23 24 Note that the Mozilla/Netscape format will downgrade RFC2965 cookies to 25 Netscape cookies on saving. 26 27 In particular, the cookie version and port number information is lost, 28 together with information about whether or not Path, Port and Discard were 29 specified by the Set-Cookie2 (or Set-Cookie) header, and whether or not the 30 domain as set in the HTTP header started with a dot (yes, I'm aware some 31 domains in Netscape files start with a dot and some don't -- trust me, you 32 really don't want to know any more about this). 33 34 Note that though Mozilla and Netscape use the same format, they use 35 slightly different headers. The class saves cookies using the Netscape 36 header by default (Mozilla can cope with that). 37 38 """ 39 magic_re = "#( Netscape)? HTTP Cookie File" 40 header = """\ 41 # Netscape HTTP Cookie File 42 # http://www.netscape.com/newsref/std/cookie_spec.html 43 # This is a generated file! Do not edit. 44 45 """ 46 47 def _really_load(self, f, filename, ignore_discard, ignore_expires): 48 now = time.time() 49 50 magic = f.readline() 51 if not re.search(self.magic_re, magic): 52 f.close() 53 raise LoadError( 54 "%r does not look like a Netscape format cookies file" % 55 filename) 56 57 try: 58 while 1: 59 line = f.readline() 60 if line == "": break 61 62 # last field may be absent, so keep any trailing tab 63 if line.endswith("\n"): line = line[:-1] 64 65 # skip comments and blank lines XXX what is $ for? 66 if (line.strip().startswith(("#", "$")) or 67 line.strip() == ""): 68 continue 69 70 domain, domain_specified, path, secure, expires, name, value = \ 71 line.split("\t") 72 secure = (secure == "TRUE") 73 domain_specified = (domain_specified == "TRUE") 74 if name == "": 75 # cookies.txt regards 'Set-Cookie: foo' as a cookie 76 # with no name, whereas cookielib regards it as a 77 # cookie with no value. 78 name = value 79 value = None 80 81 initial_dot = domain.startswith(".") 82 assert domain_specified == initial_dot 83 84 discard = False 85 if expires == "": 86 expires = None 87 discard = True 88 89 # assume path_specified is false 90 c = Cookie(0, name, value, 91 None, False, 92 domain, domain_specified, initial_dot, 93 path, False, 94 secure, 95 expires, 96 discard, 97 None, 98 None, 99 {}) 100 if not ignore_discard and c.discard: 101 continue 102 if not ignore_expires and c.is_expired(now): 103 continue 104 self.set_cookie(c) 105 106 except IOError: 107 raise 108 except Exception: 109 _warn_unhandled_exception() 110 raise LoadError("invalid Netscape format cookies file %r: %r" % 111 (filename, line)) 112 113 def save(self, filename=None, ignore_discard=False, ignore_expires=False): 114 if filename is None: 115 if self.filename is not None: filename = self.filename 116 else: raise ValueError(MISSING_FILENAME_TEXT) 117 118 f = open(filename, "w") 119 try: 120 f.write(self.header) 121 now = time.time() 122 for cookie in self: 123 if not ignore_discard and cookie.discard: 124 continue 125 if not ignore_expires and cookie.is_expired(now): 126 continue 127 if cookie.secure: secure = "TRUE" 128 else: secure = "FALSE" 129 if cookie.domain.startswith("."): initial_dot = "TRUE" 130 else: initial_dot = "FALSE" 131 if cookie.expires is not None: 132 expires = str(cookie.expires) 133 else: 134 expires = "" 135 if cookie.value is None: 136 # cookies.txt regards 'Set-Cookie: foo' as a cookie 137 # with no name, whereas cookielib regards it as a 138 # cookie with no value. 139 name = "" 140 value = cookie.name 141 else: 142 name = cookie.name 143 value = cookie.value 144 f.write( 145 "\t".join([cookie.domain, initial_dot, cookie.path, 146 secure, expires, name, value])+ 147 "\n") 148 finally: 149 f.close() 150