From f915c03a4c2942ad66a1bc4ce3cb03762b51d379 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Sep 2011 11:27:38 -0600 Subject: [PATCH] JS Browser: Support for migrating cookies to python format --- src/calibre/library/server/utils.py | 7 +++++ src/calibre/web/jsbrowser/browser.py | 44 +++++++++++++++++++++++++- src/calibre/web/jsbrowser/test.py | 47 +++++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/src/calibre/library/server/utils.py b/src/calibre/library/server/utils.py index 53c6cdbd9d..9b8ec98d87 100644 --- a/src/calibre/library/server/utils.py +++ b/src/calibre/library/server/utils.py @@ -94,3 +94,10 @@ def unquote(s): ans = ans.decode('utf-8') return ans +def cookie_time_fmt(time_t): + return time.strftime('%a, %d-%b-%Y %H:%M:%S GMT', time_t) + +def cookie_max_age_to_expires(max_age): + gmt_expiration_time = time.gmtime(time.time() + max_age) + return cookie_time_fmt(gmt_expiration_time) + diff --git a/src/calibre/web/jsbrowser/browser.py b/src/calibre/web/jsbrowser/browser.py index c4341171ea..6ccc5be86c 100644 --- a/src/calibre/web/jsbrowser/browser.py +++ b/src/calibre/web/jsbrowser/browser.py @@ -8,10 +8,11 @@ __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' import os, pprint, time +from cookielib import Cookie from PyQt4.Qt import (QObject, QNetworkAccessManager, QNetworkDiskCache, QNetworkProxy, QNetworkProxyFactory, QEventLoop, QUrl, - QDialog, QVBoxLayout, QSize) + QDialog, QVBoxLayout, QSize, QNetworkCookieJar) from PyQt4.QtWebKit import QWebPage, QWebSettings, QWebView from calibre import USER_AGENT, prints, get_proxies, get_proxy_info @@ -141,6 +142,8 @@ class NetworkAccessManager(QNetworkAccessManager): # {{{ self.pf = ProxyFactory(log) self.setProxyFactory(self.pf) self.finished.connect(self.on_finished) + self.cookie_jar = QNetworkCookieJar() + self.setCookieJar(self.cookie_jar) def on_ssl_errors(self, reply, errors): reply.ignoreSslErrors() @@ -186,6 +189,37 @@ class NetworkAccessManager(QNetworkAccessManager): # {{{ d = ' %r: %r' % (h, reply.rawHeader(h)) debug.append(d) self.log.debug('\n'.join(debug)) + + def py_cookies(self): + for c in self.cookie_jar.allCookies(): + name, value = map(bytes, (c.name(), c.value())) + domain = bytes(c.domain()) + initial_dot = domain_specified = domain.startswith(b'.') + secure = bool(c.isSecure()) + path = unicode(c.path()).strip().encode('utf-8') + expires = c.expirationDate() + is_session_cookie = False + if expires.isValid(): + expires = expires.toTime_t() + else: + expires = None + is_session_cookie = True + path_specified = True + if not path: + path = b'/' + path_specified = False + c = Cookie(0, # version + name, value, + None, # port + False, # port specified + domain, domain_specified, initial_dot, path, + path_specified, + secure, expires, is_session_cookie, + None, # Comment + None, # Comment URL + {} # rest + ) + yield c # }}} class LoadWatcher(QObject): # {{{ @@ -343,3 +377,11 @@ class Browser(QObject, FormsMixin): view = BrowserView(self.page) view.exec_() + def cookies(self): + ''' + Return all the cookies set currently as :class:`Cookie` objects. + Returns expired cookies as well. + ''' + return list(self.nam.py_cookies()) + + diff --git a/src/calibre/web/jsbrowser/test.py b/src/calibre/web/jsbrowser/test.py index 3c9b98fbcf..a2ad450335 100644 --- a/src/calibre/web/jsbrowser/test.py +++ b/src/calibre/web/jsbrowser/test.py @@ -7,11 +7,13 @@ __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import unittest, pprint, threading +import unittest, pprint, threading, time import cherrypy from calibre.web.jsbrowser.browser import Browser +from calibre.library.server.utils import (cookie_max_age_to_expires, + cookie_time_fmt) class Server(object): @@ -84,6 +86,26 @@ class Server(object): cherrypy.response.headers['Content-Type'] = 'text/javascript' return P('content_server/jquery.js', data=True) + @cherrypy.expose + def cookies(self): + try: + cookie = cherrypy.response.cookie + cookie[b'cookiea'] = 'The first cookie' + cookie[b'cookiea']['path'] = '/' + cookie[b'cookiea']['max-age'] = 60 # seconds + cookie[b'cookiea']['version'] = 1 + cookie[b'cookieb'] = 'The second cookie' + cookie[b'cookieb']['path'] = '/' + cookie[b'cookieb']['expires'] = cookie_max_age_to_expires(60) # seconds + cookie[b'cookieb']['version'] = 1 + cookie[b'cookiec'] = 'The third cookie' + cookie[b'cookiec']['path'] = '/' + self.sent_cookies = {n:('"%s"'%c.value, dict(c)) for n, c in + dict(cookie).iteritems()} + return pprint.pformat(self.sent_cookies) + except: + import traceback + traceback.print_exc() class Test(unittest.TestCase): @@ -160,6 +182,29 @@ class Test(unittest.TestCase): self.browser.ajax_submit() self.assertEqual(self.server.form_data['text'], 'Changed') + def test_cookies(self): + 'Test migration of cookies to python objects' + self.assertEqual(self.browser.visit('http://127.0.0.1:%d/cookies'%self.port), + True) + sent_cookies = self.server.sent_cookies + cookies = self.browser.cookies() + cmap = {c.name:c for c in cookies} + for name, vals in sent_cookies.iteritems(): + c = cmap[name] + value, fields = vals + self.assertEqual(value, c.value) + for field in ('secure', 'path'): + cval = getattr(c, field) + if cval is False: + cval = b'' + self.assertEqual(fields[field], cval, + 'Field %s in %s: %r != %r'%(field, name, fields[field], cval)) + cexp = cookie_time_fmt(time.gmtime(c.expires)) + fexp = fields['expires'] + if fexp: + self.assertEqual(fexp, cexp) + + def tests(): return unittest.TestLoader().loadTestsFromTestCase(Test)