1 """Mozilla / Netscape cookie loading / saving.
3 Copyright 2002-2006 John J Lee <jjl@pobox.com>
4 Copyright 1997-1999 Gisle Aas (original libwww-perl code)
6 This code is free software; you can redistribute it and/or modify it
7 under the terms of the BSD or ZPL 2.1 licenses (see the file
8 COPYING.txt included with the distribution).
12 import re
, time
, logging
14 from _clientcookie
import reraise_unmasked_exceptions
, FileCookieJar
, Cookie
, \
15 MISSING_FILENAME_TEXT
, LoadError
16 debug
= logging
.getLogger("ClientCookie").debug
19 class MozillaCookieJar(FileCookieJar
):
22 WARNING: you may want to backup your browser's cookies file if you use
23 this class to save cookies. I *think* it works, but there have been
26 This class differs from CookieJar only in the format it uses to save and
27 load cookies to and from a file. This class uses the Mozilla/Netscape
28 `cookies.txt' format. lynx uses this file format, too.
30 Don't expect cookies saved while the browser is running to be noticed by
31 the browser (in fact, Mozilla on unix will overwrite your saved cookies if
32 you change them on disk while it's running; on Windows, you probably can't
33 save at all while the browser is running).
35 Note that the Mozilla/Netscape format will downgrade RFC2965 cookies to
36 Netscape cookies on saving.
38 In particular, the cookie version and port number information is lost,
39 together with information about whether or not Path, Port and Discard were
40 specified by the Set-Cookie2 (or Set-Cookie) header, and whether or not the
41 domain as set in the HTTP header started with a dot (yes, I'm aware some
42 domains in Netscape files start with a dot and some don't -- trust me, you
43 really don't want to know any more about this).
45 Note that though Mozilla and Netscape use the same format, they use
46 slightly different headers. The class saves cookies using the Netscape
47 header by default (Mozilla can cope with that).
50 magic_re
= "#( Netscape)? HTTP Cookie File"
52 # Netscape HTTP Cookie File
53 # http://www.netscape.com/newsref/std/cookie_spec.html
54 # This is a generated file! Do not edit.
58 def _really_load(self
, f
, filename
, ignore_discard
, ignore_expires
):
62 if not re
.search(self
.magic_re
, magic
):
65 "%s does not look like a Netscape format cookies file" %
73 # last field may be absent, so keep any trailing tab
74 if line
.endswith("\n"): line
= line
[:-1]
76 # skip comments and blank lines XXX what is $ for?
77 if (line
.strip().startswith("#") or
78 line
.strip().startswith("$") or
82 domain
, domain_specified
, path
, secure
, expires
, name
, value
= \
84 secure
= (secure
== "TRUE")
85 domain_specified
= (domain_specified
== "TRUE")
90 initial_dot
= domain
.startswith(".")
91 assert domain_specified
== initial_dot
98 # assume path_specified is false
99 c
= Cookie(0, name
, value
,
101 domain
, domain_specified
, initial_dot
,
109 if not ignore_discard
and c
.discard
:
111 if not ignore_expires
and c
.is_expired(now
):
116 reraise_unmasked_exceptions((IOError,))
117 raise LoadError("invalid Netscape format file %s: %s" %
120 def save(self
, filename
=None, ignore_discard
=False, ignore_expires
=False):
122 if self
.filename
is not None: filename
= self
.filename
123 else: raise ValueError(MISSING_FILENAME_TEXT
)
125 f
= open(filename
, "w")
127 debug("Saving Netscape cookies.txt file")
131 if not ignore_discard
and cookie
.discard
:
132 debug(" Not saving %s: marked for discard", cookie
.name
)
134 if not ignore_expires
and cookie
.is_expired(now
):
135 debug(" Not saving %s: expired", cookie
.name
)
137 if cookie
.secure
: secure
= "TRUE"
138 else: secure
= "FALSE"
139 if cookie
.domain
.startswith("."): initial_dot
= "TRUE"
140 else: initial_dot
= "FALSE"
141 if cookie
.expires
is not None:
142 expires
= str(cookie
.expires
)
145 if cookie
.value
is None:
146 # cookies.txt regards 'Set-Cookie: foo' as a cookie
147 # with no name, whereas cookielib regards it as a
148 # cookie with no value.
155 "\t".join([cookie
.domain
, initial_dot
, cookie
.path
,
156 secure
, expires
, name
, value
])+