ntome
Committed by Romain Bignon
Builds for 1 pipeline failed in 7 minutes 51 seconds

weboob.browser: add a export_session method and a webextension

This method can be used to continue the same session in a GUI browser, using
contrib/webextension-session-importer for Firefox and Chromium.
Weboob session importer
=======================
This is a webextension to export a weboob session URL and cookies and load it in
a real browser.
Build
-----
The extension can be temporarily loaded by loading "manifest.json" in
about:debugging in Firefox and chrome://extensions/ in Chromium.
To build a package, `web-ext build` can be used.
(See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Getting_started_with_web-ext)
Use
---
To export the session with weboob-debug for example:
>>> print(json.dumps(browser.export_session()))
Copy the JSON output (sample):
{"url": "https://example.com/foo", "cookies": [{"name": "foo", "value": "bar"}]}
Then click on the Weboob toolbar button of your browser and paste the JSON.
The browser will set the cookies from weboob and go to the same URL.
../../docs/source/_static/favicon.ico
\ No newline at end of file
{
"manifest_version": 2,
"name": "Weboob session importer",
"version": "0.0.1",
"description": "Import Weboob session URL and cookies",
"homepage_url": "http://weboob.org",
"icons": {
"48": "logo.png"
},
"permissions": [
"<all_urls>",
"cookies",
"tabs"
],
"browser_action": {
"browser_style": true,
"default_icon": "logo.png",
"default_title": "Import Weboob session",
"default_popup": "popup.html"
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
html, body {
width: 300px;
}
textarea {
width: 100%;
background: url(bg.png) center no-repeat;
background-size: 150px;
}
</style>
</head>
<body>
<textarea columns="80" rows="24" id="data">
</textarea>
<br/>
<input id="submit" type="submit" value="Load session" />
<script src="popup.js"></script>
</body>
</html>
var makePromise;
if (navigator.userAgent.indexOf('Firefox') > -1) {
makePromise = function() {
var args = Array.prototype.slice.call(arguments);
var f = args.shift();
var self = args.shift();
return f.apply(self, args);
}
} else { /* In chrome, navigator is an empty object, wtf? */
browser = chrome;
/* chrome APIs don't return Promise objects */
makePromise = function() {
var args = Array.prototype.slice.call(arguments);
var f = args.shift();
var self = args.shift();
return new Promise(function(resolve, reject) {
args.push(function() {
resolve.apply(null, arguments);
});
f.apply(self, args);
});
}
}
function loadCookies() {
var onGetCookies = function(url, cookies) {
var objs = cookies.map(function(cookie) {
return {
name: cookie.name,
value: cookie.value,
domain: cookie.domain,
path: cookie.path,
secure: cookie.secure,
httpOnly: cookie.httpOnly,
expirationDate: cookie.expirationDate,
storeId: cookie.storeId
};
});
var ret = {
url: url,
cookies: objs
};
document.getElementById('data').placeholder = JSON.stringify(ret, null, 2);
};
var onGetActive = function(tabs) {
makePromise(browser.cookies.getAll, browser.cookies, {
url: tabs[0].url
}).then(function(cookies) { onGetCookies(tabs[0].url, cookies); });
};
makePromise(browser.tabs.query, browser.tabs, {
active: true,
currentWindow: true
}).then(onGetActive);
}
function clearCookies(cookieStoreId, url) {
return new Promise(function(resolve, reject) {
var onGetCookie = function(cookies) {
var promises = cookies.map(function(cookie) {
return makePromise(browser.cookies.remove, browser.cookies, {
url: url,
name: cookie.name,
storeId: cookie.storeId
});
});
Promise.all(promises).then(function() { resolve(); });
};
makePromise(browser.cookies.getAll, browser.cookies, {
url: url
/* TODO when FF 52 is out: use cookieStoreId to support private browsing */
}).then(onGetCookie);
});
}
function setCookies(cookieStoreId, url, objs) {
return new Promise(function(resolve, reject) {
var promises = objs.map(function(obj) {
obj.url = url;
/* FF52: use cookieStoreId */
return makePromise(browser.cookies.set, browser.cookies, obj);
});
Promise.all(promises).then(function() { resolve(); });
});
}
function setState() {
var data = JSON.parse(document.getElementById('data').value);
var objs = data.cookies;
var url = data.url;
var onGetActive = function(tabs) {
clearCookies(tabs[0].cookieStoreId, url).then(function() {
setCookies(tabs[0].cookieStoreId, url, objs).then(function() {
makePromise(browser.tabs.update, browser.tabs, tabs[0].id, {
url: url
}).then(function() {
window.close();
});
});
});
};
makePromise(browser.tabs.query, browser.tabs, {
active: true,
currentWindow: true
}).then(onGetActive);
}
window.addEventListener('load', loadCookies);
window.addEventListener('load', function() {
document.getElementById('submit').addEventListener('click', setState);
});
......@@ -479,6 +479,21 @@ class Browser(object):
return
return oldurl
def export_session(self):
def make_cookie(c):
d = {
k: getattr(c, k) for k in ['name', 'value', 'domain', 'path', 'secure']
}
#d['session'] = c.discard
d['httpOnly'] = 'httponly' in [k.lower() for k in c._rest.keys()]
d['expirationDate'] = getattr(c, 'expires', None)
return d
return {
'url': self.url,
'cookies': [make_cookie(c) for c in self.session.cookies],
}
class UrlNotAllowed(Exception):
"""
......