2011-07-19 03:52:02 +00:00
|
|
|
/*
|
|
|
|
***** 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
|
2012-02-11 00:10:43 +00:00
|
|
|
* @param {browser} [browser] Hidden browser object
|
2011-07-19 03:52:02 +00:00
|
|
|
* @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._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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-11 00:10:43 +00:00
|
|
|
if(browser) {
|
|
|
|
this.attachToBrowser(browser);
|
|
|
|
}
|
|
|
|
Zotero.CookieSandbox.Observer.register();
|
2011-07-19 03:52:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Zotero.CookieSandbox.prototype = {
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2012-02-11 00:10:43 +00:00
|
|
|
* Attach CookieSandbox to a specific browser
|
|
|
|
* @param {Browser} browser
|
2011-07-19 03:52:02 +00:00
|
|
|
*/
|
|
|
|
"attachToBrowser":function(browser) {
|
2012-02-11 00:10:43 +00:00
|
|
|
Zotero.CookieSandbox.Observer.trackedBrowsers.set(browser, this);
|
2011-07-19 03:52:02 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attach CookieSandbox to a specific XMLHttpRequest
|
2012-02-11 00:10:43 +00:00
|
|
|
* @param {nsIInterfaceRequestor} ir
|
2011-07-19 03:52:02 +00:00
|
|
|
*/
|
2012-02-11 00:10:43 +00:00
|
|
|
"attachToInterfaceRequestor": function(ir) {
|
|
|
|
Zotero.CookieSandbox.Observer.trackedInterfaceRequestors.set(ir.QueryInterface(Components.interfaces.nsIInterfaceRequestor), this);
|
2011-07-19 03:52:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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),
|
2012-02-11 00:10:43 +00:00
|
|
|
observing = false;
|
|
|
|
|
2011-07-19 03:52:02 +00:00
|
|
|
/**
|
|
|
|
* Registers cookie manager and observer, if necessary
|
|
|
|
*/
|
|
|
|
this.register = function(CookieSandbox) {
|
2012-02-14 01:46:24 +00:00
|
|
|
this.trackedBrowsers = new WeakMap();
|
|
|
|
this.trackedInterfaceRequestors = new WeakMap();
|
|
|
|
|
2011-07-19 03:52:02 +00:00
|
|
|
if(!observing) {
|
|
|
|
Zotero.debug("CookieSandbox: Registering observers");
|
|
|
|
for each(var topic in observeredTopics) observerService.addObserver(this, topic, false);
|
|
|
|
observing = true;
|
|
|
|
}
|
2012-02-11 00:10:43 +00:00
|
|
|
};
|
2011-07-19 03:52:02 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements nsIObserver to watch for new cookies and to add sandboxed cookies
|
|
|
|
*/
|
|
|
|
this.observe = function(channel, topic) {
|
|
|
|
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
|
2012-02-11 00:10:43 +00:00
|
|
|
var trackedBy, tested, browser, callbacks,
|
2011-07-19 03:52:02 +00:00
|
|
|
channelURI = channel.URI.spec,
|
|
|
|
notificationCallbacks = channel.notificationCallbacks;
|
|
|
|
|
2012-02-11 00:10:43 +00:00
|
|
|
// try the notification callbacks
|
|
|
|
trackedBy = this.trackedInterfaceRequestors.get(notificationCallbacks);
|
|
|
|
if(trackedBy) {
|
2011-07-19 03:52:02 +00:00
|
|
|
tested = true;
|
|
|
|
} else {
|
2012-02-11 00:10:43 +00:00
|
|
|
// try the browser
|
2011-07-19 03:52:02 +00:00
|
|
|
try {
|
2012-02-11 00:10:43 +00:00
|
|
|
browser = notificationCallbacks.getInterface(Ci.nsIWebNavigation)
|
|
|
|
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
|
2011-07-19 03:52:02 +00:00
|
|
|
} catch(e) {}
|
2012-02-11 00:10:43 +00:00
|
|
|
if(browser) {
|
2011-07-19 03:52:02 +00:00
|
|
|
tested = true;
|
2012-02-11 00:10:43 +00:00
|
|
|
trackedBy = this.trackedBrowsers.get(browser);
|
2011-07-19 03:52:02 +00:00
|
|
|
} else {
|
2012-02-11 00:10:43 +00:00
|
|
|
// try the document for the load group
|
2011-07-19 03:52:02 +00:00
|
|
|
try {
|
2012-02-11 00:10:43 +00:00
|
|
|
browser = channel.loadGroup.notificationCallbacks.getInterface(Ci.nsIWebNavigation)
|
|
|
|
.QueryInterface(Ci.nsIDocShell).chromeEventHandler;
|
2011-07-19 03:52:02 +00:00
|
|
|
} catch(e) {}
|
2012-02-11 00:10:43 +00:00
|
|
|
if(browser) {
|
2011-07-19 03:52:02 +00:00
|
|
|
tested = true;
|
2012-02-11 00:10:43 +00:00
|
|
|
trackedBy = this.trackedBrowsers.get(browser);
|
|
|
|
} else {
|
|
|
|
// try getting as an XHR or nsIWBP
|
|
|
|
try {
|
|
|
|
notificationCallbacks.QueryInterface(Components.interfaces.nsIXMLHttpRequest);
|
|
|
|
tested = true;
|
|
|
|
} catch(e) {}
|
|
|
|
if(!tested) {
|
|
|
|
try {
|
|
|
|
notificationCallbacks.QueryInterface(Components.interfaces.nsIWebBrowserPersist);
|
|
|
|
tested = true;
|
|
|
|
} catch(e) {}
|
2011-07-19 03:52:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
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
|
|
|
|
if(cookieHeader) trackedBy.addCookiesFromHeader(cookieHeader);
|
|
|
|
|
|
|
|
Zotero.debug("CookieSandbox: Slurped cookies from "+channelURI, 5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|