--- /dev/null
+"""Load / save to libwww-perl (LWP) format files.\r
+\r
+Actually, the format is slightly extended from that used by LWP's\r
+(libwww-perl's) HTTP::Cookies, to avoid losing some RFC 2965 information\r
+not recorded by LWP.\r
+\r
+It uses the version string "2.0", though really there isn't an LWP Cookies\r
+2.0 format. This indicates that there is extra information in here\r
+(domain_dot and # port_spec) while still being compatible with\r
+libwww-perl, I hope.\r
+\r
+"""\r
+\r
+import time, re\r
+from cookielib import (_warn_unhandled_exception, FileCookieJar, LoadError,\r
+ Cookie, MISSING_FILENAME_TEXT,\r
+ join_header_words, split_header_words,\r
+ iso2time, time2isoz)\r
+\r
+def lwp_cookie_str(cookie):\r
+ """Return string representation of Cookie in an the LWP cookie file format.\r
+\r
+ Actually, the format is extended a bit -- see module docstring.\r
+\r
+ """\r
+ h = [(cookie.name, cookie.value),\r
+ ("path", cookie.path),\r
+ ("domain", cookie.domain)]\r
+ if cookie.port is not None: h.append(("port", cookie.port))\r
+ if cookie.path_specified: h.append(("path_spec", None))\r
+ if cookie.port_specified: h.append(("port_spec", None))\r
+ if cookie.domain_initial_dot: h.append(("domain_dot", None))\r
+ if cookie.secure: h.append(("secure", None))\r
+ if cookie.expires: h.append(("expires",\r
+ time2isoz(float(cookie.expires))))\r
+ if cookie.discard: h.append(("discard", None))\r
+ if cookie.comment: h.append(("comment", cookie.comment))\r
+ if cookie.comment_url: h.append(("commenturl", cookie.comment_url))\r
+\r
+ keys = cookie._rest.keys()\r
+ keys.sort()\r
+ for k in keys:\r
+ h.append((k, str(cookie._rest[k])))\r
+\r
+ h.append(("version", str(cookie.version)))\r
+\r
+ return join_header_words([h])\r
+\r
+class LWPCookieJar(FileCookieJar):\r
+ """\r
+ The LWPCookieJar saves a sequence of"Set-Cookie3" lines.\r
+ "Set-Cookie3" is the format used by the libwww-perl libary, not known\r
+ to be compatible with any browser, but which is easy to read and\r
+ doesn't lose information about RFC 2965 cookies.\r
+\r
+ Additional methods\r
+\r
+ as_lwp_str(ignore_discard=True, ignore_expired=True)\r
+\r
+ """\r
+\r
+ def as_lwp_str(self, ignore_discard=True, ignore_expires=True):\r
+ """Return cookies as a string of "\n"-separated "Set-Cookie3" headers.\r
+\r
+ ignore_discard and ignore_expires: see docstring for FileCookieJar.save\r
+\r
+ """\r
+ now = time.time()\r
+ r = []\r
+ for cookie in self:\r
+ if not ignore_discard and cookie.discard:\r
+ continue\r
+ if not ignore_expires and cookie.is_expired(now):\r
+ continue\r
+ r.append("Set-Cookie3: %s" % lwp_cookie_str(cookie))\r
+ return "\n".join(r+[""])\r
+\r
+ def save(self, filename=None, ignore_discard=False, ignore_expires=False):\r
+ if filename is None:\r
+ if self.filename is not None: filename = self.filename\r
+ else: raise ValueError(MISSING_FILENAME_TEXT)\r
+\r
+ f = open(filename, "w")\r
+ try:\r
+ # There really isn't an LWP Cookies 2.0 format, but this indicates\r
+ # that there is extra information in here (domain_dot and\r
+ # port_spec) while still being compatible with libwww-perl, I hope.\r
+ f.write("#LWP-Cookies-2.0\n")\r
+ f.write(self.as_lwp_str(ignore_discard, ignore_expires))\r
+ finally:\r
+ f.close()\r
+\r
+ def _really_load(self, f, filename, ignore_discard, ignore_expires):\r
+ magic = f.readline()\r
+ if not re.search(self.magic_re, magic):\r
+ msg = ("%r does not look like a Set-Cookie3 (LWP) format "\r
+ "file" % filename)\r
+ raise LoadError(msg)\r
+\r
+ now = time.time()\r
+\r
+ header = "Set-Cookie3:"\r
+ boolean_attrs = ("port_spec", "path_spec", "domain_dot",\r
+ "secure", "discard")\r
+ value_attrs = ("version",\r
+ "port", "path", "domain",\r
+ "expires",\r
+ "comment", "commenturl")\r
+\r
+ try:\r
+ while 1:\r
+ line = f.readline()\r
+ if line == "": break\r
+ if not line.startswith(header):\r
+ continue\r
+ line = line[len(header):].strip()\r
+\r
+ for data in split_header_words([line]):\r
+ name, value = data[0]\r
+ standard = {}\r
+ rest = {}\r
+ for k in boolean_attrs:\r
+ standard[k] = False\r
+ for k, v in data[1:]:\r
+ if k is not None:\r
+ lc = k.lower()\r
+ else:\r
+ lc = None\r
+ # don't lose case distinction for unknown fields\r
+ if (lc in value_attrs) or (lc in boolean_attrs):\r
+ k = lc\r
+ if k in boolean_attrs:\r
+ if v is None: v = True\r
+ standard[k] = v\r
+ elif k in value_attrs:\r
+ standard[k] = v\r
+ else:\r
+ rest[k] = v\r
+\r
+ h = standard.get\r
+ expires = h("expires")\r
+ discard = h("discard")\r
+ if expires is not None:\r
+ expires = iso2time(expires)\r
+ if expires is None:\r
+ discard = True\r
+ domain = h("domain")\r
+ domain_specified = domain.startswith(".")\r
+ c = Cookie(h("version"), name, value,\r
+ h("port"), h("port_spec"),\r
+ domain, domain_specified, h("domain_dot"),\r
+ h("path"), h("path_spec"),\r
+ h("secure"),\r
+ expires,\r
+ discard,\r
+ h("comment"),\r
+ h("commenturl"),\r
+ rest)\r
+ if not ignore_discard and c.discard:\r
+ continue\r
+ if not ignore_expires and c.is_expired(now):\r
+ continue\r
+ self.set_cookie(c)\r
+\r
+ except IOError:\r
+ raise\r
+ except Exception:\r
+ _warn_unhandled_exception()\r
+ raise LoadError("invalid Set-Cookie3 format file %r: %r" %\r
+ (filename, line))\r