Closes #397, Support authenticated PAC setups in Standalone

Trigger a proxy authentication prompt at startup if a PAC file is
installed and one of a few randomly chosen big sites requires a proxy.

This also improves general proxy detection by not making a request
to S3 unless it would actually be proxied.
This commit is contained in:
Dan Stillman 2013-10-17 19:52:41 -04:00
parent db43af33ac
commit 1089856622
3 changed files with 136 additions and 22 deletions

View file

@ -473,29 +473,116 @@ Zotero.HTTP = new function() {
var deferred = Q.defer();
Zotero.proxyAuthComplete = deferred.promise;
var uri = ZOTERO_CONFIG.PROXY_AUTH_URL;
Zotero.debug("HTTP GET " + uri);
var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance();
xmlhttp.open("GET", uri, true);
xmlhttp.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
var useMethodjit = Components.utils.methodjit;
/** @ignore */
xmlhttp.onreadystatechange = function() {
// XXX Remove when we drop support for Fx <24
if(useMethodjit !== undefined) Components.utils.methodjit = useMethodjit;
_stateChange(xmlhttp, function (xmlhttp) {
Zotero.debug("Proxy auth request completed with status "
+ xmlhttp.status + ": " + xmlhttp.responseText);
Q.fcall(function () {
var uris = Zotero.Prefs.get('proxyAuthenticationURLs').split(',');
uris = Zotero.Utilities.arrayShuffle(uris);
uris.unshift(ZOTERO_CONFIG.PROXY_AUTH_URL);
return Q.async(function () {
let max = 3; // how many URIs to try after the general Zotero one
for (let i = 0; i <= max; i++) {
let uri = uris.shift();
if (!uri) {
break;
}
// For non-Zotero URLs, wait for PAC initialization,
// in a rather ugly and inefficient manner
if (i == 1) {
let installed = yield Q.fcall(_pacInstalled)
.then(function (installed) {
if (installed) throw true;
})
.delay(500)
.then(_pacInstalled)
.then(function (installed) {
if (installed) throw true;
})
.delay(1000)
.then(_pacInstalled)
.then(function (installed) {
if (installed) throw true;
})
.delay(2000)
.then(_pacInstalled)
.catch(function () {
return true;
});
if (!installed) {
Zotero.debug("No general proxy or PAC file found -- assuming direct connection");
break;
}
}
let proxyInfo = yield _proxyAsyncResolve(uri);
if (proxyInfo) {
Zotero.debug("Proxy required for " + uri + " -- making HEAD request to trigger auth prompt");
yield Zotero.HTTP.promise("HEAD", uri, {
foreground: true,
dontCache: true
})
.catch(function (e) {
Components.utils.reportError(e);
var msg = "Error connecting to proxy -- proxied requests may not work";
Zotero.log(msg, 'error');
Zotero.debug(msg, 1);
});
break;
}
else {
Zotero.debug("Proxy not required for " + uri);
}
}
deferred.resolve();
});
};
xmlhttp.send(null);
return xmlhttp;
})();
})
.catch(function (e) {
Components.utils.reportError(e);
Zotero.debug(e, 1);
deferred.resolve();
});
}
/**
* Test if a PAC file is installed
*
* There might be a better way to do this that doesn't require stepping
* through the error log and doing a fragile string comparison.
*/
_pacInstalled = function () {
return Zotero.getErrors(true).some(function (val) val.indexOf("PAC file installed") == 0)
}
_proxyAsyncResolve = function (uri) {
Components.utils.import("resource://gre/modules/NetUtil.jsm");
var pps = Components.classes["@mozilla.org/network/protocol-proxy-service;1"]
.getService(Components.interfaces.nsIProtocolProxyService);
var deferred = Q.defer();
pps.asyncResolve(
NetUtil.newURI(uri),
0,
{
onProxyAvailable: function (req, uri, proxyInfo, status) {
//Zotero.debug("onProxyAvailable");
//Zotero.debug(status);
deferred.resolve(proxyInfo);
},
QueryInterface: function (iid) {
const interfaces = [
Components.interfaces.nsIProtocolProxyCallback,
Components.interfaces.nsISupports
];
if (!interfaces.some(function(v) { return iid.equals(v) })) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
return this;
},
}
);
return deferred.promise;
}

View file

@ -575,6 +575,31 @@ Zotero.Utilities = {
return vals;
},
/**
* Return new array with values shuffled
*
* From http://stackoverflow.com/a/6274398
*
* @param {Array} arr
* @return {Array}
*/
"arrayShuffle": function (array) {
var counter = array.length, temp, index;
// While there are elements in the array
while (counter--) {
// Pick a random index
index = (Math.random() * counter) | 0;
// And swap the last element with it
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
},
/**
* Return new array with duplicate values removed

View file

@ -22,6 +22,8 @@ pref("extensions.zotero.debug.time", false);
pref("extensions.zotero.automaticScraperUpdates",true);
pref("extensions.zotero.zoteroDotOrgVersionHeader", true);
pref("extensions.zotero.triggerProxyAuthentication", true);
// Proxy auth URLs should respond successfully to HEAD requests over HTTP and HTTPS (in case of forced HTTPS requests)
pref("extensions.zotero.proxyAuthenticationURLs", 'http://www.acm.org,http://www.ebscohost.com,http://www.elsevier.com,http://www.ieee.org,http://www.jstor.org,http://www.ovid.com,http://www.springer.com,http://www.tandfonline.com');
pref("extensions.zotero.cacheTranslatorData",true);
pref("extensions.zotero.showIn", 1);
pref("extensions.zotero.statusBarIcon", 2);