Home | History | Annotate | Download | only in Lib
      1 """Mozilla / Netscape cookie loading / saving."""
      2 
      3 import re, time
      4 
      5 from cookielib import (_warn_unhandled_exception, FileCookieJar, LoadError,
      6                        Cookie, MISSING_FILENAME_TEXT)
      7 
      8 class MozillaCookieJar(FileCookieJar):
      9     """
     10 
     11     WARNING: you may want to backup your browser's cookies file if you use
     12     this class to save cookies.  I *think* it works, but there have been
     13     bugs in the past!
     14 
     15     This class differs from CookieJar only in the format it uses to save and
     16     load cookies to and from a file.  This class uses the Mozilla/Netscape
     17     `cookies.txt' format.  lynx uses this file format, too.
     18 
     19     Don't expect cookies saved while the browser is running to be noticed by
     20     the browser (in fact, Mozilla on unix will overwrite your saved cookies if
     21     you change them on disk while it's running; on Windows, you probably can't
     22     save at all while the browser is running).
     23 
     24     Note that the Mozilla/Netscape format will downgrade RFC2965 cookies to
     25     Netscape cookies on saving.
     26 
     27     In particular, the cookie version and port number information is lost,
     28     together with information about whether or not Path, Port and Discard were
     29     specified by the Set-Cookie2 (or Set-Cookie) header, and whether or not the
     30     domain as set in the HTTP header started with a dot (yes, I'm aware some
     31     domains in Netscape files start with a dot and some don't -- trust me, you
     32     really don't want to know any more about this).
     33 
     34     Note that though Mozilla and Netscape use the same format, they use
     35     slightly different headers.  The class saves cookies using the Netscape
     36     header by default (Mozilla can cope with that).
     37 
     38     """
     39     magic_re = "#( Netscape)? HTTP Cookie File"
     40     header = """\
     41 # Netscape HTTP Cookie File
     42 # http://curl.haxx.se/rfc/cookie_spec.html
     43 # This is a generated file!  Do not edit.
     44 
     45 """
     46 
     47     def _really_load(self, f, filename, ignore_discard, ignore_expires):
     48         now = time.time()
     49 
     50         magic = f.readline()
     51         if not re.search(self.magic_re, magic):
     52             f.close()
     53             raise LoadError(
     54                 "%r does not look like a Netscape format cookies file" %
     55                 filename)
     56 
     57         try:
     58             while 1:
     59                 line = f.readline()
     60                 if line == "": break
     61 
     62                 # last field may be absent, so keep any trailing tab
     63                 if line.endswith("\n"): line = line[:-1]
     64 
     65                 # skip comments and blank lines XXX what is $ for?
     66                 if (line.strip().startswith(("#", "$")) or
     67                     line.strip() == ""):
     68                     continue
     69 
     70                 domain, domain_specified, path, secure, expires, name, value = \
     71                         line.split("\t")
     72                 secure = (secure == "TRUE")
     73                 domain_specified = (domain_specified == "TRUE")
     74                 if name == "":
     75                     # cookies.txt regards 'Set-Cookie: foo' as a cookie
     76                     # with no name, whereas cookielib regards it as a
     77                     # cookie with no value.
     78                     name = value
     79                     value = None
     80 
     81                 initial_dot = domain.startswith(".")
     82                 assert domain_specified == initial_dot
     83 
     84                 discard = False
     85                 if expires == "":
     86                     expires = None
     87                     discard = True
     88 
     89                 # assume path_specified is false
     90                 c = Cookie(0, name, value,
     91                            None, False,
     92                            domain, domain_specified, initial_dot,
     93                            path, False,
     94                            secure,
     95                            expires,
     96                            discard,
     97                            None,
     98                            None,
     99                            {})
    100                 if not ignore_discard and c.discard:
    101                     continue
    102                 if not ignore_expires and c.is_expired(now):
    103                     continue
    104                 self.set_cookie(c)
    105 
    106         except IOError:
    107             raise
    108         except Exception:
    109             _warn_unhandled_exception()
    110             raise LoadError("invalid Netscape format cookies file %r: %r" %
    111                             (filename, line))
    112 
    113     def save(self, filename=None, ignore_discard=False, ignore_expires=False):
    114         if filename is None:
    115             if self.filename is not None: filename = self.filename
    116             else: raise ValueError(MISSING_FILENAME_TEXT)
    117 
    118         f = open(filename, "w")
    119         try:
    120             f.write(self.header)
    121             now = time.time()
    122             for cookie in self:
    123                 if not ignore_discard and cookie.discard:
    124                     continue
    125                 if not ignore_expires and cookie.is_expired(now):
    126                     continue
    127                 if cookie.secure: secure = "TRUE"
    128                 else: secure = "FALSE"
    129                 if cookie.domain.startswith("."): initial_dot = "TRUE"
    130                 else: initial_dot = "FALSE"
    131                 if cookie.expires is not None:
    132                     expires = str(cookie.expires)
    133                 else:
    134                     expires = ""
    135                 if cookie.value is None:
    136                     # cookies.txt regards 'Set-Cookie: foo' as a cookie
    137                     # with no name, whereas cookielib regards it as a
    138                     # cookie with no value.
    139                     name = ""
    140                     value = cookie.name
    141                 else:
    142                     name = cookie.name
    143                     value = cookie.value
    144                 f.write(
    145                     "\t".join([cookie.domain, initial_dot, cookie.path,
    146                                secure, expires, name, value])+
    147                     "\n")
    148         finally:
    149             f.close()
    150