1 """Mozilla / Netscape cookie loading / saving.
3 Copyright 1997-1999 Gisle Aas (libwww-perl)
4 Copyright 2002-2003 John J Lee <jjl@pobox.com> (The Python port)
6 This code is free software; you can redistribute it and/or modify it under
7 the terms of the BSD License (see the file COPYING included with the
12 import sys
, re
, string
, time
15 from _ClientCookie
import CookieJar
, Cookie
, MISSING_FILENAME_TEXT
16 from _Util
import startswith
, endswith
17 from _Debug
import debug
24 try: issubclass(Exception(), (Exception,))
26 real_issubclass
= issubclass
27 from _Util
import compat_issubclass
28 issubclass = compat_issubclass
32 class MozillaCookieJar(CookieJar
):
35 WARNING: you may want to backup your browser's cookies file if you use
36 this class to save cookies. I *think* it works, but there have been
39 This class differs from CookieJar only in the format it uses to save and
40 load cookies to and from a file. This class uses the Netscape/Mozilla
43 Don't expect cookies saved while the browser is running to be noticed by
44 the browser (in fact, Mozilla on unix will overwrite your saved cookies if
45 you change them on disk while it's running; on Windows, you probably can't
46 save at all while the browser is running).
48 Note that the Netscape/Mozilla format will downgrade RFC2965 cookies to
49 Netscape cookies on saving.
51 In particular, the cookie version and port number information is lost,
52 together with information about whether or not Path, Port and Discard were
53 specified by the Set-Cookie2 (or Set-Cookie) header, and whether or not the
54 domain as set in the HTTP header started with a dot (yes, I'm aware some
55 domains in Netscape files start with a dot and some don't -- trust me, you
56 really don't want to know any more about this).
58 Note that though Mozilla and Netscape use the same format, they use
59 slightly different headers. The class saves cookies using the Netscape
60 header by default (Mozilla can cope with that).
63 magic_re
= "#( Netscape)? HTTP Cookie File"
65 # Netscape HTTP Cookie File
66 # http://www.netscape.com/newsref/std/cookie_spec.html
67 # This is a generated file! Do not edit.
71 def _really_load(self
, f
, filename
, ignore_discard
, ignore_expires
):
75 if not re
.search(self
.magic_re
, magic
):
78 "%s does not look like a Netscape format cookies file" %
86 # last field may be absent, so keep any trailing tab
87 if endswith(line
, "\n"): line
= line
[:-1]
89 # skip comments and blank lines XXX what is $ for?
90 if (startswith(string
.strip(line
), "#") or
91 startswith(string
.strip(line
), "$") or
92 string
.strip(line
) == ""):
95 domain
, domain_specified
, path
, secure
, expires
, name
, value
= \
96 string
.split(line
, "\t")
97 secure
= (secure
== "TRUE")
98 domain_specified
= (domain_specified
== "TRUE")
99 if name
== "": name
= None
101 initial_dot
= startswith(domain
, ".")
102 assert domain_specified
== initial_dot
109 # assume path_specified is false
110 c
= Cookie(0, name
, value
,
112 domain
, domain_specified
, initial_dot
,
120 if not ignore_discard
and c
.discard
:
122 if not ignore_expires
and c
.is_expired(now
):
127 unmasked
= (KeyboardInterrupt, SystemExit)
128 if ClientCookie
.CLIENTCOOKIE_DEBUG
:
129 unmasked
= (Exception,)
130 etype
= sys
.exc_info()[0]
131 if issubclass(etype
, IOError) or \
132 issubclass(etype
, unmasked
):
134 raise IOError("invalid Netscape format file %s: %s" %
137 def save(self
, filename
=None, ignore_discard
=False, ignore_expires
=False):
139 if self
.filename
is not None: filename
= self
.filename
140 else: raise ValueError(MISSING_FILENAME_TEXT
)
142 f
= open(filename
, "w")
146 debug("Saving Netscape cookies.txt file")
148 if not ignore_discard
and cookie
.discard
:
149 debug(" Not saving %s: marked for discard" % cookie
.name
)
151 if not ignore_expires
and cookie
.is_expired(now
):
152 debug(" Not saving %s: expired" % cookie
.name
)
154 if cookie
.secure
: secure
= "TRUE"
155 else: secure
= "FALSE"
156 if startswith(cookie
.domain
, "."): initial_dot
= "TRUE"
157 else: initial_dot
= "FALSE"
158 if cookie
.expires
is not None:
159 expires
= str(cookie
.expires
)
162 if cookie
.name
is not None:
167 string
.join([cookie
.domain
, initial_dot
, cookie
.path
,
168 secure
, expires
, name
, cookie
.value
], "\t")+