_MozillaCookieJar.py 5.8 KB

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