Fix cookie sandboxing in connector
This commit is contained in:
parent
a4bcc4244c
commit
4067a1b820
6 changed files with 325 additions and 189 deletions
285
chrome/content/zotero/xpcom/cookieSandbox.js
Executable file
285
chrome/content/zotero/xpcom/cookieSandbox.js
Executable file
|
@ -0,0 +1,285 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2011 Center for History and New Media
|
||||||
|
George Mason University, Fairfax, Virginia, USA
|
||||||
|
http://zotero.org
|
||||||
|
|
||||||
|
This file is part of Zotero.
|
||||||
|
|
||||||
|
Zotero is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Zotero is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage cookies in a sandboxed fashion
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {browser} browser Hidden browser object
|
||||||
|
* @param {String|nsIURI} uri URI of page to manage cookies for (cookies for domains that are not
|
||||||
|
* subdomains of this URI are ignored)
|
||||||
|
* @param {String} cookieData Cookies with which to initiate the sandbox
|
||||||
|
*/
|
||||||
|
Zotero.CookieSandbox = function(browser, uri, cookieData) {
|
||||||
|
this._webNav = browser.webNavigation;
|
||||||
|
this._browser = browser;
|
||||||
|
this._watchedBrowsers = [browser];
|
||||||
|
this._watchedXHRs = [];
|
||||||
|
this._observerService = Components.classes["@mozilla.org/observer-service;1"].
|
||||||
|
getService(Components.interfaces.nsIObserverService);
|
||||||
|
|
||||||
|
if(uri instanceof Components.interfaces.nsIURI) {
|
||||||
|
this.URI = uri;
|
||||||
|
} else {
|
||||||
|
this.URI = Components.classes["@mozilla.org/network/io-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIIOService)
|
||||||
|
.newURI(uri, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cookies = {};
|
||||||
|
if(cookieData) {
|
||||||
|
var splitCookies = cookieData.split(/; ?/);
|
||||||
|
for each(var cookie in splitCookies) {
|
||||||
|
var key = cookie.substr(0, cookie.indexOf("="));
|
||||||
|
var value = cookie.substr(cookie.indexOf("=")+1);
|
||||||
|
this._cookies[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// register with observer
|
||||||
|
Zotero.CookieSandbox.Observer.register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.CookieSandbox.prototype = {
|
||||||
|
/**
|
||||||
|
* Check whether we track a browser for this document
|
||||||
|
*/
|
||||||
|
"isDocumentTracked":function(doc) {
|
||||||
|
var i = this._watchedBrowsers.length;
|
||||||
|
while(i--) {
|
||||||
|
var browser = this._watchedBrowsers[i];
|
||||||
|
if(doc == browser.contentDocument) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether we track an XHR for this document
|
||||||
|
*/
|
||||||
|
"isXHRTracked":function(xhr) {
|
||||||
|
return this._watchedXHRs.indexOf(xhr) !== -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds cookies to this CookieSandbox based on a cookie header
|
||||||
|
* @param {String} cookieString;
|
||||||
|
*/
|
||||||
|
"addCookiesFromHeader":function(cookieString) {
|
||||||
|
var cookies = cookieString.split("\n");
|
||||||
|
for(var i=0, n=cookies.length; i<n; i++) {
|
||||||
|
var cookieInfo = cookies[i].split(/; ?/);
|
||||||
|
var secure = false;
|
||||||
|
|
||||||
|
for(var j=1, m=cookieInfo.length; j<m; j++) {
|
||||||
|
if(cookieInfo[j].substr(0, cookieInfo[j].indexOf("=")).toLowerCase() === "secure") {
|
||||||
|
secure = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!secure) {
|
||||||
|
var key = cookieInfo[0].substr(0, cookieInfo[0].indexOf("="));
|
||||||
|
var value = cookieInfo[0].substr(cookieInfo[0].indexOf("=")+1);
|
||||||
|
this._cookies[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach CookieSandbox to a specific XMLHttpRequest
|
||||||
|
* @param {XMLHttpRequest} xhr
|
||||||
|
*/
|
||||||
|
"attachToBrowser":function(browser) {
|
||||||
|
this._watchedBrowsers.push(browser);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach CookieSandbox to a specific XMLHttpRequest
|
||||||
|
* @param {XMLHttpRequest} xhr
|
||||||
|
*/
|
||||||
|
"attachToXHR": function(xhr) {
|
||||||
|
this._watchedXHRs.push(xhr);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys this CookieSandbox (intended to be executed when the browser is destroyed)
|
||||||
|
*/
|
||||||
|
"destroy": function() {
|
||||||
|
// unregister with observer
|
||||||
|
Zotero.CookieSandbox.Observer.unregister(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.CookieSandbox.prototype.__defineGetter__("cookieString", function() {
|
||||||
|
return [key+"="+this._cookies[key] for(key in this._cookies)].join("; ");
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nsIObserver implementation for adding, clearing, and slurping cookies
|
||||||
|
*/
|
||||||
|
Zotero.CookieSandbox.Observer = new function() {
|
||||||
|
const observeredTopics = ["http-on-examine-response", "http-on-modify-request", "quit-application"];
|
||||||
|
|
||||||
|
var observerService = Components.classes["@mozilla.org/observer-service;1"].
|
||||||
|
getService(Components.interfaces.nsIObserverService),
|
||||||
|
observing = false,
|
||||||
|
cookieSandboxes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers cookie manager and observer, if necessary
|
||||||
|
*/
|
||||||
|
this.register = function(CookieSandbox) {
|
||||||
|
cookieSandboxes.push(CookieSandbox);
|
||||||
|
|
||||||
|
if(!observing) {
|
||||||
|
Zotero.debug("CookieSandbox: Registering observers");
|
||||||
|
for each(var topic in observeredTopics) observerService.addObserver(this, topic, false);
|
||||||
|
observing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters cookie manager and observer
|
||||||
|
*/
|
||||||
|
this.unregister = function(CookieSandbox) {
|
||||||
|
// remove cookie manager from list
|
||||||
|
cookieSandboxes.splice(cookieSandboxes.indexOf(CookieSandbox), 1);
|
||||||
|
|
||||||
|
// remove observer if this is the last and this is not translation-server
|
||||||
|
if(cookieSandboxes.length === 0 && !Zotero.isServer) {
|
||||||
|
Zotero.debug("CookieSandbox: Unregistering observers");
|
||||||
|
for each(var topic in observeredTopics) observerService.removeObserver(this, topic);
|
||||||
|
observing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements nsIObserver to watch for new cookies and to add sandboxed cookies
|
||||||
|
*/
|
||||||
|
this.observe = function(channel, topic) {
|
||||||
|
if(topic == "quit-application" && cookieSandboxes.length) {
|
||||||
|
Zotero.debug("WARNING: A CookieSandbox for "+cookieSandboxes[0].URI.spec+" was still open on shutdown");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
|
||||||
|
var trackedBy, tested, doc, xhr,
|
||||||
|
channelURI = channel.URI.spec,
|
||||||
|
notificationCallbacks = channel.notificationCallbacks;
|
||||||
|
|
||||||
|
// try the document
|
||||||
|
try {
|
||||||
|
doc = notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow).top.document;
|
||||||
|
} catch(e) {}
|
||||||
|
if(doc) {
|
||||||
|
tested = true;
|
||||||
|
for(var i=0, n=cookieSandboxes.length; i<n; i++) {
|
||||||
|
if(cookieSandboxes[i].isDocumentTracked(doc)) {
|
||||||
|
trackedBy = cookieSandboxes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// try the document for the load group
|
||||||
|
try {
|
||||||
|
doc = channel.loadGroup.notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow).top.document;
|
||||||
|
} catch(e) {}
|
||||||
|
if(doc) {
|
||||||
|
tested = true;
|
||||||
|
for(var i=0, n=cookieSandboxes.length; i<n; i++) {
|
||||||
|
if(cookieSandboxes[i].isDocumentTracked(doc)) {
|
||||||
|
trackedBy = cookieSandboxes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// try getting as an XHR
|
||||||
|
try {
|
||||||
|
xhr = notificationCallbacks.QueryInterface(Components.interfaces.nsIXMLHttpRequest);
|
||||||
|
} catch(e) {}
|
||||||
|
if(xhr) {
|
||||||
|
tested = true;
|
||||||
|
for(var i=0, n=cookieSandboxes.length; i<n; i++) {
|
||||||
|
if(cookieSandboxes[i].isXHRTracked(xhr)) {
|
||||||
|
trackedBy = cookieSandboxes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTracked is now either true, false, or null
|
||||||
|
// trackedBy => we should manage cookies for this request
|
||||||
|
// tested && !trackedBy => we should not manage cookies for this request
|
||||||
|
// !tested && !trackedBy => this request is of a type we couldn't match to this request.
|
||||||
|
// one such type is a link prefetch (nsPrefetchNode) but there might be others as
|
||||||
|
// well. for now, we are paranoid and reject these.
|
||||||
|
|
||||||
|
if(tested) {
|
||||||
|
if(trackedBy) {
|
||||||
|
Zotero.debug("CookieSandbox: Managing cookies for "+channelURI, 5);
|
||||||
|
} else {
|
||||||
|
Zotero.debug("CookieSandbox: Not touching channel for "+channelURI, 5);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Zotero.debug("CookieSandbox: Being paranoid about channel for "+channelURI, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(topic == "http-on-modify-request") {
|
||||||
|
// clear cookies to be sent to other domains
|
||||||
|
if(!trackedBy || channel.URI.host != trackedBy.URI.host) {
|
||||||
|
channel.setRequestHeader("Cookie", "", false);
|
||||||
|
channel.setRequestHeader("Cookie2", "", false);
|
||||||
|
Zotero.debug("CookieSandbox: Cleared cookies to be sent to "+channelURI, 5);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add cookies to be sent to this domain
|
||||||
|
Zotero.debug(trackedBy.cookieString);
|
||||||
|
channel.setRequestHeader("Cookie", trackedBy.cookieString, false);
|
||||||
|
Zotero.debug("CookieSandbox: Added cookies for request to "+channelURI, 5);
|
||||||
|
} else if(topic == "http-on-examine-response") {
|
||||||
|
// clear cookies being received
|
||||||
|
try {
|
||||||
|
var cookieHeader = channel.getResponseHeader("Set-Cookie");
|
||||||
|
} catch(e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
channel.setResponseHeader("Set-Cookie", "", false);
|
||||||
|
channel.setResponseHeader("Set-Cookie2", "", false);
|
||||||
|
|
||||||
|
// don't process further if these cookies are for another set of domains
|
||||||
|
if(!trackedBy || channel.URI.host != trackedBy.URI.host) {
|
||||||
|
Zotero.debug("CookieSandbox: Rejected cookies from "+channelURI, 5);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put new cookies into our sandbox
|
||||||
|
Zotero.debug(cookieHeader);
|
||||||
|
if(cookieHeader) trackedBy.addCookiesFromHeader(cookieHeader);
|
||||||
|
|
||||||
|
Zotero.debug("CookieSandbox: Slurped cookies from "+channelURI, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,9 +12,10 @@ Zotero.HTTP = new function() {
|
||||||
* @param {nsIURI|String} url URL to request
|
* @param {nsIURI|String} url URL to request
|
||||||
* @param {Function} onDone Callback to be executed upon request completion
|
* @param {Function} onDone Callback to be executed upon request completion
|
||||||
* @param {String} responseCharset Character set to force on the response
|
* @param {String} responseCharset Character set to force on the response
|
||||||
|
* @param {Zotero.CookieSandbox} [cookieSandbox] Cookie sandbox object
|
||||||
* @return {Boolean} True if the request was sent, or false if the browser is offline
|
* @return {Boolean} True if the request was sent, or false if the browser is offline
|
||||||
*/
|
*/
|
||||||
this.doGet = function(url, onDone, responseCharset) {
|
this.doGet = function(url, onDone, responseCharset, cookieSandbox) {
|
||||||
if (url instanceof Components.interfaces.nsIURI) {
|
if (url instanceof Components.interfaces.nsIURI) {
|
||||||
// Don't display password in console
|
// Don't display password in console
|
||||||
var disp = url.clone();
|
var disp = url.clone();
|
||||||
|
@ -69,6 +70,7 @@ Zotero.HTTP = new function() {
|
||||||
_stateChange(xmlhttp, onDone, responseCharset);
|
_stateChange(xmlhttp, onDone, responseCharset);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(cookieSandbox) cookieSandbox.attachToXHR(xmlhttp);
|
||||||
xmlhttp.send(null);
|
xmlhttp.send(null);
|
||||||
|
|
||||||
return xmlhttp;
|
return xmlhttp;
|
||||||
|
@ -82,9 +84,10 @@ Zotero.HTTP = new function() {
|
||||||
* @param {Function} onDone Callback to be executed upon request completion
|
* @param {Function} onDone Callback to be executed upon request completion
|
||||||
* @param {String} headers Request HTTP headers
|
* @param {String} headers Request HTTP headers
|
||||||
* @param {String} responseCharset Character set to force on the response
|
* @param {String} responseCharset Character set to force on the response
|
||||||
|
* @param {Zotero.CookieSandbox} [cookieSandbox] Cookie sandbox object
|
||||||
* @return {Boolean} True if the request was sent, or false if the browser is offline
|
* @return {Boolean} True if the request was sent, or false if the browser is offline
|
||||||
*/
|
*/
|
||||||
this.doPost = function(url, body, onDone, headers, responseCharset) {
|
this.doPost = function(url, body, onDone, headers, responseCharset, cookieSandbox) {
|
||||||
if (url instanceof Components.interfaces.nsIURI) {
|
if (url instanceof Components.interfaces.nsIURI) {
|
||||||
// Don't display password in console
|
// Don't display password in console
|
||||||
var disp = url.clone();
|
var disp = url.clone();
|
||||||
|
@ -166,6 +169,7 @@ Zotero.HTTP = new function() {
|
||||||
_stateChange(xmlhttp, onDone, responseCharset);
|
_stateChange(xmlhttp, onDone, responseCharset);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(cookieSandbox) cookieSandbox.attachToXHR(xmlhttp);
|
||||||
xmlhttp.send(body);
|
xmlhttp.send(body);
|
||||||
|
|
||||||
return xmlhttp;
|
return xmlhttp;
|
||||||
|
@ -506,9 +510,10 @@ Zotero.HTTP = new function() {
|
||||||
* @param {Function} exception Callback to be executed if an exception occurs
|
* @param {Function} exception Callback to be executed if an exception occurs
|
||||||
* @param {Boolean} dontDelete Don't delete the hidden browser upon completion; calling function
|
* @param {Boolean} dontDelete Don't delete the hidden browser upon completion; calling function
|
||||||
* must call deleteHiddenBrowser itself.
|
* must call deleteHiddenBrowser itself.
|
||||||
|
* @param {Zotero.CookieSandbox} [cookieSandbox] Cookie sandbox object
|
||||||
* @return {browser} Hidden browser used for loading
|
* @return {browser} Hidden browser used for loading
|
||||||
*/
|
*/
|
||||||
this.processDocuments = function(urls, processor, done, exception, dontDelete) {
|
this.processDocuments = function(urls, processor, done, exception, dontDelete, cookieSandbox) {
|
||||||
/**
|
/**
|
||||||
* Removes event listener for the load event and deletes the hidden browser
|
* Removes event listener for the load event and deletes the hidden browser
|
||||||
*/
|
*/
|
||||||
|
@ -573,6 +578,7 @@ Zotero.HTTP = new function() {
|
||||||
|
|
||||||
var hiddenBrowser = Zotero.Browser.createHiddenBrowser();
|
var hiddenBrowser = Zotero.Browser.createHiddenBrowser();
|
||||||
hiddenBrowser.addEventListener(loadEvent, onLoad, true);
|
hiddenBrowser.addEventListener(loadEvent, onLoad, true);
|
||||||
|
if(cookieSandbox) cookieSandbox.attachToBrowser(hiddenBrowser);
|
||||||
|
|
||||||
doLoad();
|
doLoad();
|
||||||
|
|
||||||
|
|
|
@ -29,169 +29,6 @@ Zotero.Server.Connector = function() {};
|
||||||
Zotero.Server.Connector._waitingForSelection = {};
|
Zotero.Server.Connector._waitingForSelection = {};
|
||||||
Zotero.Server.Connector.Data = {};
|
Zotero.Server.Connector.Data = {};
|
||||||
|
|
||||||
/**
|
|
||||||
* Manage cookies in a sandboxed fashion
|
|
||||||
*
|
|
||||||
* @param {browser} browser Hidden browser object
|
|
||||||
* @param {String} uri URI of page to manage cookies for (cookies for domains that are not
|
|
||||||
* subdomains of this URI are ignored)
|
|
||||||
* @param {String} cookieData Cookies with which to initiate the sandbox
|
|
||||||
*/
|
|
||||||
Zotero.Server.Connector.CookieManager = function(browser, uri, cookieData) {
|
|
||||||
this._webNav = browser.webNavigation;
|
|
||||||
this._browser = browser;
|
|
||||||
this._watchedBrowsers = [browser];
|
|
||||||
this._observerService = Components.classes["@mozilla.org/observer-service;1"].
|
|
||||||
getService(Components.interfaces.nsIObserverService);
|
|
||||||
|
|
||||||
this._uri = Components.classes["@mozilla.org/network/io-service;1"]
|
|
||||||
.getService(Components.interfaces.nsIIOService)
|
|
||||||
.newURI(uri, null, null);
|
|
||||||
|
|
||||||
var splitCookies = cookieData.split(/; ?/);
|
|
||||||
this._cookies = {};
|
|
||||||
for each(var cookie in splitCookies) {
|
|
||||||
var key = cookie.substr(0, cookie.indexOf("="));
|
|
||||||
var value = cookie.substr(cookie.indexOf("=")+1);
|
|
||||||
this._cookies[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[this._observerService.addObserver(this, topic, false) for each(topic in this._observerTopics)];
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.Server.Connector.CookieManager.prototype = {
|
|
||||||
"_observerTopics":["http-on-examine-response", "http-on-modify-request", "quit-application"],
|
|
||||||
"_watchedXHRs":[],
|
|
||||||
|
|
||||||
/**
|
|
||||||
* nsIObserver implementation for adding, clearing, and slurping cookies
|
|
||||||
*/
|
|
||||||
"observe": function(channel, topic) {
|
|
||||||
if(topic == "quit-application") {
|
|
||||||
Zotero.debug("WARNING: A Zotero.Server.CookieManager for "+this._uri.spec+" was still open on shutdown");
|
|
||||||
} else {
|
|
||||||
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
|
|
||||||
var isTracked = null;
|
|
||||||
try {
|
|
||||||
var topDoc = channel.notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow).top.document;
|
|
||||||
for each(var browser in this._watchedBrowsers) {
|
|
||||||
isTracked = topDoc == browser.contentDocument;
|
|
||||||
if(isTracked) break;
|
|
||||||
}
|
|
||||||
} catch(e) {}
|
|
||||||
if(isTracked === null) {
|
|
||||||
try {
|
|
||||||
isTracked = channel.loadGroup.notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow).top.document == this._browser.contentDocument;
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
if(isTracked === null) {
|
|
||||||
try {
|
|
||||||
isTracked = this._watchedXHRs.indexOf(channel.notificationCallbacks.QueryInterface(Components.interfaces.nsIXMLHttpRequest)) !== -1;
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isTracked is now either true, false, or null
|
|
||||||
// true => we should manage cookies for this request
|
|
||||||
// false => we should not manage cookies for this request
|
|
||||||
// null => this request is of a type we couldn't match to this request. one such type
|
|
||||||
// is a link prefetch (nsPrefetchNode) but there might be others as well. for
|
|
||||||
// now, we are paranoid and reject these.
|
|
||||||
|
|
||||||
if(isTracked === false) {
|
|
||||||
Zotero.debug("Zotero.Server.CookieManager: not touching channel for "+channel.URI.spec);
|
|
||||||
return;
|
|
||||||
} else if(isTracked) {
|
|
||||||
Zotero.debug("Zotero.Server.CookieManager: managing cookies for "+channel.URI.spec);
|
|
||||||
} else {
|
|
||||||
Zotero.debug("Zotero.Server.CookieManager: being paranoid about channel for "+channel.URI.spec);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(topic == "http-on-modify-request") {
|
|
||||||
// clear cookies to be sent to other domains
|
|
||||||
if(isTracked === null || channel.URI.host != this._uri.host) {
|
|
||||||
channel.setRequestHeader("Cookie", "", false);
|
|
||||||
channel.setRequestHeader("Cookie2", "", false);
|
|
||||||
Zotero.debug("Zotero.Server.CookieManager: cleared cookies to be sent to "+channel.URI.spec);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add cookies to be sent to this domain
|
|
||||||
var cookies = [key+"="+this._cookies[key]
|
|
||||||
for(key in this._cookies)].join("; ");
|
|
||||||
channel.setRequestHeader("Cookie", cookies, false);
|
|
||||||
Zotero.debug("Zotero.Server.CookieManager: added cookies for request to "+channel.URI.spec);
|
|
||||||
} else if(topic == "http-on-examine-response") {
|
|
||||||
// clear cookies being received
|
|
||||||
try {
|
|
||||||
var cookieHeader = channel.getResponseHeader("Set-Cookie");
|
|
||||||
} catch(e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
channel.setResponseHeader("Set-Cookie", "", false);
|
|
||||||
channel.setResponseHeader("Set-Cookie2", "", false);
|
|
||||||
|
|
||||||
// don't process further if these cookies are for another set of domains
|
|
||||||
if(isTracked === null || channel.URI.host != this._uri.host) {
|
|
||||||
Zotero.debug("Zotero.Server.CookieManager: rejected cookies from "+channel.URI.spec);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// put new cookies into our sandbox
|
|
||||||
if(cookieHeader) {
|
|
||||||
var cookies = cookieHeader.split(/; ?/);
|
|
||||||
var newCookies = {};
|
|
||||||
for each(var cookie in cookies) {
|
|
||||||
var key = cookie.substr(0, cookie.indexOf("="));
|
|
||||||
var value = cookie.substr(cookie.indexOf("=")+1);
|
|
||||||
var lcCookie = key.toLowerCase();
|
|
||||||
|
|
||||||
if(["comment", "domain", "max-age", "path", "version", "expires"].indexOf(lcCookie) != -1) {
|
|
||||||
// ignore cookie parameters; we are only holding cookies for a few minutes
|
|
||||||
// with a single domain, and the path attribute doesn't allow any additional
|
|
||||||
// security.
|
|
||||||
// DEBUG: does ignoring the path attribute break any sites?
|
|
||||||
continue;
|
|
||||||
} else if(lcCookie == "secure") {
|
|
||||||
// don't accept secure cookies
|
|
||||||
newCookies = {};
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
newCookies[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[this._cookies[key] = newCookies[key] for(key in newCookies)];
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.debug("Zotero.Server.CookieManager: slurped cookies from "+channel.URI.spec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach CookieManager to a specific XMLHttpRequest
|
|
||||||
* @param {XMLHttpRequest} xhr
|
|
||||||
*/
|
|
||||||
"attachToBrowser": function(browser) {
|
|
||||||
this._watchedBrowsers.push(browser);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach CookieManager to a specific XMLHttpRequest
|
|
||||||
* @param {XMLHttpRequest} xhr
|
|
||||||
*/
|
|
||||||
"attachToXHR": function(xhr) {
|
|
||||||
this._watchedXHRs.push(xhr);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys this CookieManager (intended to be executed when the browser is destroyed)
|
|
||||||
*/
|
|
||||||
"destroy": function() {
|
|
||||||
[this._observerService.removeObserver(this, topic) for each(topic in this._observerTopics)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists all available translators, including code for translators that should be run on every page
|
* Lists all available translators, including code for translators that should be run on every page
|
||||||
*
|
*
|
||||||
|
@ -268,7 +105,7 @@ Zotero.Server.Connector.Detect.prototype = {
|
||||||
|
|
||||||
var pageShowCalled = false;
|
var pageShowCalled = false;
|
||||||
var me = this;
|
var me = this;
|
||||||
this._translate.setCookieManager(new Zotero.Server.Connector.CookieManager(this._browser,
|
this._translate.setCookieSandbox(new Zotero.CookieSandbox(this._browser,
|
||||||
this._parsedPostData["uri"], this._parsedPostData["cookie"]));
|
this._parsedPostData["uri"], this._parsedPostData["cookie"]));
|
||||||
this._browser.addEventListener("DOMContentLoaded", function() {
|
this._browser.addEventListener("DOMContentLoaded", function() {
|
||||||
try {
|
try {
|
||||||
|
@ -310,7 +147,7 @@ Zotero.Server.Connector.Detect.prototype = {
|
||||||
}
|
}
|
||||||
this._sendResponse(200, "application/json", JSON.stringify(jsons));
|
this._sendResponse(200, "application/json", JSON.stringify(jsons));
|
||||||
|
|
||||||
this._translate.cookieManager.destroy();
|
this._translate.cookieSandbox.destroy();
|
||||||
Zotero.Browser.deleteHiddenBrowser(this._browser);
|
Zotero.Browser.deleteHiddenBrowser(this._browser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -396,6 +233,7 @@ Zotero.Server.Connector.SavePage.prototype = {
|
||||||
"_translatorsAvailable":function(translate, translators) {
|
"_translatorsAvailable":function(translate, translators) {
|
||||||
// make sure translatorsAvailable succeded
|
// make sure translatorsAvailable succeded
|
||||||
if(!translators.length) {
|
if(!translators.length) {
|
||||||
|
me._translate.cookieSandbox.destroy();
|
||||||
Zotero.Browser.deleteHiddenBrowser(this._browser);
|
Zotero.Browser.deleteHiddenBrowser(this._browser);
|
||||||
this._sendResponse(500);
|
this._sendResponse(500);
|
||||||
return;
|
return;
|
||||||
|
@ -420,10 +258,14 @@ Zotero.Server.Connector.SavePage.prototype = {
|
||||||
}
|
}
|
||||||
jsonItems.push(jsonItem);
|
jsonItems.push(jsonItem);
|
||||||
});
|
});
|
||||||
translate.setHandler("done", function(obj, item) {
|
translate.setHandler("done", function(obj, item) {
|
||||||
me._translate.cookieManager.destroy();
|
me._translate.cookieSandbox.destroy();
|
||||||
Zotero.Browser.deleteHiddenBrowser(me._browser);
|
Zotero.Browser.deleteHiddenBrowser(me._browser);
|
||||||
me._sendResponse(201, "application/json", JSON.stringify({"items":jsonItems}));
|
if(jsonItems.length) {
|
||||||
|
me._sendResponse(201, "application/json", JSON.stringify({"items":jsonItems}));
|
||||||
|
} else {
|
||||||
|
me._sendResponse(500);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// set translator and translate
|
// set translator and translate
|
||||||
|
|
|
@ -1315,7 +1315,7 @@ Zotero.Translate.Base.prototype = {
|
||||||
* @class Web translation
|
* @class Web translation
|
||||||
*
|
*
|
||||||
* @property {Document} document The document object to be used for web scraping (set with setDocument)
|
* @property {Document} document The document object to be used for web scraping (set with setDocument)
|
||||||
* @property {Zotero.Connector.CookieManager} cookieManager A CookieManager to manage cookies for
|
* @property {Zotero.CookieSandbox} cookieSandbox A CookieSandbox to manage cookies for
|
||||||
* this Translate instance.
|
* this Translate instance.
|
||||||
*/
|
*/
|
||||||
Zotero.Translate.Web = function() {
|
Zotero.Translate.Web = function() {
|
||||||
|
@ -1336,13 +1336,13 @@ Zotero.Translate.Web.prototype.setDocument = function(doc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a Zotero.Connector.CookieManager to handle cookie management for XHRs initiated from this
|
* Sets a Zotero.CookieSandbox to handle cookie management for XHRs initiated from this
|
||||||
* translate instance
|
* translate instance
|
||||||
*
|
*
|
||||||
* @param {Zotero.Connector.CookieManager} cookieManager
|
* @param {Zotero.CookieSandbox} cookieSandbox
|
||||||
*/
|
*/
|
||||||
Zotero.Translate.Web.prototype.setCookieManager = function(cookieManager) {
|
Zotero.Translate.Web.prototype.setCookieSandbox = function(cookieSandbox) {
|
||||||
this.cookieManager = cookieManager;
|
this.cookieSandbox = cookieSandbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1757,9 +1757,9 @@ Zotero.Translate.Search.prototype._entryFunctionSuffix = "Search";
|
||||||
Zotero.Translate.Search.prototype.Sandbox = Zotero.Translate.Sandbox._inheritFromBase(Zotero.Translate.Sandbox.Search);
|
Zotero.Translate.Search.prototype.Sandbox = Zotero.Translate.Sandbox._inheritFromBase(Zotero.Translate.Sandbox.Search);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @borrows Zotero.Translate.Web#setCookieManager
|
* @borrows Zotero.Translate.Web#setCookieSandbox
|
||||||
*/
|
*/
|
||||||
Zotero.Translate.Search.prototype.setCookieManager = Zotero.Translate.Web.prototype.setCookieManager;
|
Zotero.Translate.Search.prototype.setCookieSandbox = Zotero.Translate.Web.prototype.setCookieSandbox;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the item to be used for searching
|
* Sets the item to be used for searching
|
||||||
|
|
|
@ -1155,10 +1155,10 @@ Zotero.Utilities.Translate.prototype.processDocuments = function(urls, processor
|
||||||
|
|
||||||
var translate = this._translate;
|
var translate = this._translate;
|
||||||
translate.incrementAsyncProcesses();
|
translate.incrementAsyncProcesses();
|
||||||
Zotero.HTTP.processDocuments(urls, processor, function() {
|
var hiddenBrowser = Zotero.HTTP.processDocuments(urls, processor, function() {
|
||||||
if(done) done();
|
if(done) done();
|
||||||
translate.decrementAsyncProcesses();
|
translate.decrementAsyncProcesses();
|
||||||
}, exception);
|
}, exception, false, translate.cookieSandbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1181,6 +1181,8 @@ Zotero.Utilities.Translate.prototype.retrieveDocument = function(url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var hiddenBrowser = Zotero.Browser.createHiddenBrowser();
|
var hiddenBrowser = Zotero.Browser.createHiddenBrowser();
|
||||||
|
if(translate.cookieSandbox) translate.cookieSandbox.attachToBrowser(hiddenBrowser);
|
||||||
|
|
||||||
hiddenBrowser.addEventListener("pageshow", listener, true);
|
hiddenBrowser.addEventListener("pageshow", listener, true);
|
||||||
hiddenBrowser.loadURI(url);
|
hiddenBrowser.loadURI(url);
|
||||||
|
|
||||||
|
@ -1228,16 +1230,16 @@ Zotero.Utilities.Translate.prototype.retrieveSource = function(url, body, header
|
||||||
if(!responseCharset) responseCharset = null;
|
if(!responseCharset) responseCharset = null;
|
||||||
|
|
||||||
var mainThread = Zotero.mainThread;
|
var mainThread = Zotero.mainThread;
|
||||||
var xmlhttp = false;
|
var finished = false;
|
||||||
var listener = function(aXmlhttp) { xmlhttp = aXmlhttp };
|
var listener = function() { finished = true };
|
||||||
|
|
||||||
if(body) {
|
if(body) {
|
||||||
Zotero.HTTP.doPost(url, body, listener, headers, responseCharset);
|
var xmlhttp = Zotero.HTTP.doPost(url, body, listener, headers, responseCharset, translate.cookieSandbox);
|
||||||
} else {
|
} else {
|
||||||
Zotero.HTTP.doGet(url, listener, responseCharset);
|
var xmlhttp = Zotero.HTTP.doGet(url, listener, responseCharset, translate.cookieSandbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
while(!xmlhttp) mainThread.processNextEvent(true);
|
while(!finished) mainThread.processNextEvent(true);
|
||||||
} else {
|
} else {
|
||||||
// Use a synchronous XMLHttpRequest, even though this is inadvisable
|
// Use a synchronous XMLHttpRequest, even though this is inadvisable
|
||||||
var xmlhttp = new XMLHttpRequest();
|
var xmlhttp = new XMLHttpRequest();
|
||||||
|
@ -1274,7 +1276,7 @@ Zotero.Utilities.Translate.prototype.doGet = function(urls, processor, done, res
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
this._translate.incrementAsyncProcesses();
|
this._translate.incrementAsyncProcesses();
|
||||||
Zotero.HTTP.doGet(url, function(xmlhttp) {
|
var xmlhttp = Zotero.HTTP.doGet(url, function(xmlhttp) {
|
||||||
try {
|
try {
|
||||||
if(processor) {
|
if(processor) {
|
||||||
processor(xmlhttp.responseText, xmlhttp, url);
|
processor(xmlhttp.responseText, xmlhttp, url);
|
||||||
|
@ -1291,7 +1293,7 @@ Zotero.Utilities.Translate.prototype.doGet = function(urls, processor, done, res
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
me._translate.complete(false, e);
|
me._translate.complete(false, e);
|
||||||
}
|
}
|
||||||
}, responseCharset);
|
}, responseCharset, this._translate.cookieSandbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1303,14 +1305,14 @@ Zotero.Utilities.Translate.prototype.doPost = function(url, body, onDone, header
|
||||||
|
|
||||||
var translate = this._translate;
|
var translate = this._translate;
|
||||||
this._translate.incrementAsyncProcesses();
|
this._translate.incrementAsyncProcesses();
|
||||||
Zotero.HTTP.doPost(url, body, function(xmlhttp) {
|
var xmlhttp = Zotero.HTTP.doPost(url, body, function(xmlhttp) {
|
||||||
try {
|
try {
|
||||||
onDone(xmlhttp.responseText, xmlhttp);
|
onDone(xmlhttp.responseText, xmlhttp);
|
||||||
translate.decrementAsyncProcesses();
|
translate.decrementAsyncProcesses();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
translate.complete(false, e);
|
translate.complete(false, e);
|
||||||
}
|
}
|
||||||
}, headers, responseCharset);
|
}, headers, responseCharset, translate.cookieSandbox ? translate.cookieSandbox : undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -55,6 +55,7 @@ const xpcomFilesLocal = [
|
||||||
'attachments',
|
'attachments',
|
||||||
'cite',
|
'cite',
|
||||||
'commons',
|
'commons',
|
||||||
|
'cookieSandbox',
|
||||||
'data_access',
|
'data_access',
|
||||||
'data/dataObjects',
|
'data/dataObjects',
|
||||||
'data/cachedTypes',
|
'data/cachedTypes',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue