blob: 2a4fa7b2f68d21f4c996a0ab4eb21e85c57749ff [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
Georg Brandlbbab6712006-05-18 06:18:06 +000014import time, re
Georg Brandle854e762006-05-08 17:48:01 +000015from 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
40 keys = cookie._rest.keys()
41 keys.sort()
42 for k in keys:
43 h.append((k, str(cookie._rest[k])))
44
45 h.append(("version", str(cookie.version)))
46
47 return join_header_words([h])
48
49class LWPCookieJar(FileCookieJar):
50 """
51 The LWPCookieJar saves a sequence of"Set-Cookie3" lines.
52 "Set-Cookie3" is the format used by the libwww-perl libary, not known
53 to be compatible with any browser, but which is easy to read and
54 doesn't lose information about RFC 2965 cookies.
55
56 Additional methods
57
58 as_lwp_str(ignore_discard=True, ignore_expired=True)
59
60 """
61
62 def as_lwp_str(self, ignore_discard=True, ignore_expires=True):
63 """Return cookies as a string of "\n"-separated "Set-Cookie3" headers.
64
65 ignore_discard and ignore_expires: see docstring for FileCookieJar.save
66
67 """
68 now = time.time()
69 r = []
70 for cookie in self:
71 if not ignore_discard and cookie.discard:
72 continue
73 if not ignore_expires and cookie.is_expired(now):
74 continue
75 r.append("Set-Cookie3: %s" % lwp_cookie_str(cookie))
76 return "\n".join(r+[""])
77
78 def save(self, filename=None, ignore_discard=False, ignore_expires=False):
79 if filename is None:
80 if self.filename is not None: filename = self.filename
81 else: raise ValueError(MISSING_FILENAME_TEXT)
82
83 f = open(filename, "w")
84 try:
85 # There really isn't an LWP Cookies 2.0 format, but this indicates
86 # that there is extra information in here (domain_dot and
87 # port_spec) while still being compatible with libwww-perl, I hope.
88 f.write("#LWP-Cookies-2.0\n")
89 f.write(self.as_lwp_str(ignore_discard, ignore_expires))
90 finally:
91 f.close()
92
93 def _really_load(self, f, filename, ignore_discard, ignore_expires):
94 magic = f.readline()
95 if not re.search(self.magic_re, magic):
Georg Brandle854e762006-05-08 17:48:01 +000096 msg = ("%r does not look like a Set-Cookie3 (LWP) format "
97 "file" % filename)
Neal Norwitz3e7de592005-12-23 21:24:35 +000098 raise LoadError(msg)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +000099
100 now = time.time()
101
102 header = "Set-Cookie3:"
103 boolean_attrs = ("port_spec", "path_spec", "domain_dot",
104 "secure", "discard")
105 value_attrs = ("version",
106 "port", "path", "domain",
107 "expires",
108 "comment", "commenturl")
109
110 try:
111 while 1:
112 line = f.readline()
113 if line == "": break
114 if not line.startswith(header):
115 continue
116 line = line[len(header):].strip()
117
118 for data in split_header_words([line]):
119 name, value = data[0]
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000120 standard = {}
121 rest = {}
122 for k in boolean_attrs:
123 standard[k] = False
124 for k, v in data[1:]:
125 if k is not None:
126 lc = k.lower()
127 else:
128 lc = None
129 # don't lose case distinction for unknown fields
130 if (lc in value_attrs) or (lc in boolean_attrs):
131 k = lc
132 if k in boolean_attrs:
133 if v is None: v = True
134 standard[k] = v
135 elif k in value_attrs:
136 standard[k] = v
137 else:
138 rest[k] = v
139
140 h = standard.get
141 expires = h("expires")
142 discard = h("discard")
143 if expires is not None:
144 expires = iso2time(expires)
145 if expires is None:
146 discard = True
147 domain = h("domain")
148 domain_specified = domain.startswith(".")
149 c = Cookie(h("version"), name, value,
150 h("port"), h("port_spec"),
151 domain, domain_specified, h("domain_dot"),
152 h("path"), h("path_spec"),
153 h("secure"),
154 expires,
155 discard,
156 h("comment"),
157 h("commenturl"),
158 rest)
159 if not ignore_discard and c.discard:
160 continue
161 if not ignore_expires and c.is_expired(now):
162 continue
163 self.set_cookie(c)
Georg Brandle854e762006-05-08 17:48:01 +0000164
165 except IOError:
166 raise
167 except Exception:
168 _warn_unhandled_exception()
169 raise LoadError("invalid Set-Cookie3 format file %r: %r" %
170 (filename, line))