blob: 67209583eb8f7c7752f0a4066e290f92cac8d372 [file] [log] [blame]
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001"""Load / save to libwww-perl (LWP) format files.
2
3Actually, the format is slightly extended from that used by LWP's
4(libwww-perl's) HTTP::Cookies, to avoid losing some RFC 2965 information
5not recorded by LWP.
6
7It uses the version string "2.0", though really there isn't an LWP Cookies
82.0 format. This indicates that there is extra information in here
9(domain_dot and # port_spec) while still being compatible with
10libwww-perl, I hope.
11
12"""
13
Thomas Wouters477c8d52006-05-27 19:21:47 +000014import time, re
15from cookielib import (_warn_unhandled_exception, FileCookieJar, LoadError,
16 Cookie, MISSING_FILENAME_TEXT,
17 join_header_words, split_header_words,
18 iso2time, time2isoz)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +000019
20def lwp_cookie_str(cookie):
21 """Return string representation of Cookie in an the LWP cookie file format.
22
23 Actually, the format is extended a bit -- see module docstring.
24
25 """
26 h = [(cookie.name, cookie.value),
27 ("path", cookie.path),
28 ("domain", cookie.domain)]
29 if cookie.port is not None: h.append(("port", cookie.port))
30 if cookie.path_specified: h.append(("path_spec", None))
31 if cookie.port_specified: h.append(("port_spec", None))
32 if cookie.domain_initial_dot: h.append(("domain_dot", None))
33 if cookie.secure: h.append(("secure", None))
34 if cookie.expires: h.append(("expires",
35 time2isoz(float(cookie.expires))))
36 if cookie.discard: h.append(("discard", None))
37 if cookie.comment: h.append(("comment", cookie.comment))
38 if cookie.comment_url: h.append(("commenturl", cookie.comment_url))
39
Guido van Rossumcc2b0162007-02-11 06:12:03 +000040 keys = sorted(cookie._rest.keys())
Martin v. Löwis2a6ba902004-05-31 18:22:40 +000041 for k in keys:
42 h.append((k, str(cookie._rest[k])))
43
44 h.append(("version", str(cookie.version)))
45
46 return join_header_words([h])
47
48class LWPCookieJar(FileCookieJar):
49 """
50 The LWPCookieJar saves a sequence of"Set-Cookie3" lines.
51 "Set-Cookie3" is the format used by the libwww-perl libary, not known
52 to be compatible with any browser, but which is easy to read and
53 doesn't lose information about RFC 2965 cookies.
54
55 Additional methods
56
57 as_lwp_str(ignore_discard=True, ignore_expired=True)
58
59 """
60
61 def as_lwp_str(self, ignore_discard=True, ignore_expires=True):
62 """Return cookies as a string of "\n"-separated "Set-Cookie3" headers.
63
64 ignore_discard and ignore_expires: see docstring for FileCookieJar.save
65
66 """
67 now = time.time()
68 r = []
69 for cookie in self:
70 if not ignore_discard and cookie.discard:
71 continue
72 if not ignore_expires and cookie.is_expired(now):
73 continue
74 r.append("Set-Cookie3: %s" % lwp_cookie_str(cookie))
75 return "\n".join(r+[""])
76
77 def save(self, filename=None, ignore_discard=False, ignore_expires=False):
78 if filename is None:
79 if self.filename is not None: filename = self.filename
80 else: raise ValueError(MISSING_FILENAME_TEXT)
81
82 f = open(filename, "w")
83 try:
84 # There really isn't an LWP Cookies 2.0 format, but this indicates
85 # that there is extra information in here (domain_dot and
86 # port_spec) while still being compatible with libwww-perl, I hope.
87 f.write("#LWP-Cookies-2.0\n")
88 f.write(self.as_lwp_str(ignore_discard, ignore_expires))
89 finally:
90 f.close()
91
92 def _really_load(self, f, filename, ignore_discard, ignore_expires):
93 magic = f.readline()
94 if not re.search(self.magic_re, magic):
Thomas Wouters477c8d52006-05-27 19:21:47 +000095 msg = ("%r does not look like a Set-Cookie3 (LWP) format "
96 "file" % filename)
Neal Norwitz3e7de592005-12-23 21:24:35 +000097 raise LoadError(msg)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +000098
99 now = time.time()
100
101 header = "Set-Cookie3:"
102 boolean_attrs = ("port_spec", "path_spec", "domain_dot",
103 "secure", "discard")
104 value_attrs = ("version",
105 "port", "path", "domain",
106 "expires",
107 "comment", "commenturl")
108
109 try:
110 while 1:
111 line = f.readline()
112 if line == "": break
113 if not line.startswith(header):
114 continue
115 line = line[len(header):].strip()
116
117 for data in split_header_words([line]):
118 name, value = data[0]
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000119 standard = {}
120 rest = {}
121 for k in boolean_attrs:
122 standard[k] = False
123 for k, v in data[1:]:
124 if k is not None:
125 lc = k.lower()
126 else:
127 lc = None
128 # don't lose case distinction for unknown fields
129 if (lc in value_attrs) or (lc in boolean_attrs):
130 k = lc
131 if k in boolean_attrs:
132 if v is None: v = True
133 standard[k] = v
134 elif k in value_attrs:
135 standard[k] = v
136 else:
137 rest[k] = v
138
139 h = standard.get
140 expires = h("expires")
141 discard = h("discard")
142 if expires is not None:
143 expires = iso2time(expires)
144 if expires is None:
145 discard = True
146 domain = h("domain")
147 domain_specified = domain.startswith(".")
148 c = Cookie(h("version"), name, value,
149 h("port"), h("port_spec"),
150 domain, domain_specified, h("domain_dot"),
151 h("path"), h("path_spec"),
152 h("secure"),
153 expires,
154 discard,
155 h("comment"),
156 h("commenturl"),
157 rest)
158 if not ignore_discard and c.discard:
159 continue
160 if not ignore_expires and c.is_expired(now):
161 continue
162 self.set_cookie(c)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000163
164 except IOError:
165 raise
166 except Exception:
167 _warn_unhandled_exception()
168 raise LoadError("invalid Set-Cookie3 format file %r: %r" %
169 (filename, line))