-"""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