closes #743, Support non-EZproxy proxies

closes #831, transparent EZProxy support
adds a proxy pane to the preferences
asks before saving proxies to the DB (to avoid the potential phishing risk #831 would otherwise pose)
This commit is contained in:
Simon Kornblith 2008-07-28 11:11:12 +00:00
parent 30e4ae859d
commit 09e8249db8
16 changed files with 893 additions and 195 deletions

View file

@ -103,7 +103,7 @@ var Zotero_Browser = new function() {
Zotero_Browser.browserData = new Object();
Zotero_Browser._scrapePopupShowing = false;
Zotero.Ingester.ProxyMonitor.init();
Zotero.Proxies.init();
Zotero.Ingester.MIMEHandler.init();
Zotero.Cite.MIMEHandler.init();
Zotero.Translate.init();

View file

@ -22,6 +22,7 @@
var openURLServerField;
var openURLVersionMenu;
var proxies;
function init()
{
@ -32,6 +33,7 @@ function init()
}
refreshStylesList();
refreshProxyList();
populateQuickCopyList();
updateQuickCopyInstructions();
initSearchPane();
@ -930,8 +932,6 @@ function refreshStylesList(cslID) {
var styleData = Zotero.DB.query(sql);
if (!styleData) return;
Zotero.debug("ASKED FOR "+cslID);
var selectIndex = false;
for (var i=0; i<styleData.length; i++) {
var treeitem = document.createElement('treeitem');
@ -949,7 +949,6 @@ function refreshStylesList(cslID) {
if(styleData[i].cslID.length < Zotero.ENConverter.uriPrefix.length ||
styleData[i].cslID.substr(0, Zotero.ENConverter.uriPrefix.length) != Zotero.ENConverter.uriPrefix) {
cslCell.setAttribute('src', 'chrome://zotero/skin/tick.png');
Zotero.debug("ISCSL");
}
treerow.appendChild(titleCell);
@ -1029,12 +1028,13 @@ function addStyle() {
**/
function deleteStyle() {
var tree = document.getElementById('styleManager');
if(tree.currentIndex == -1) return;
var treeitem = tree.lastChild.childNodes[tree.currentIndex];
Zotero.debug(treeitem.getAttribute('id'));
var cslID = treeitem.getAttribute('id').substr(11);
Zotero.Cite.deleteStyle(cslID);
this.refreshStylesList();
document.getElementById('styleManager-delete').disabled = true;
}
/**
@ -1042,4 +1042,80 @@ function deleteStyle() {
**/
function styleImportError() {
alert(Zotero.getString('styles.installError', "This"));
}
/**
* Adds a proxy to the proxy pane
*/
function showProxyEditor(index) {
if(index == -1) return;
window.openDialog('chrome://zotero/content/preferences/proxyEditor.xul',
"zotero-preferences-proxyEditor", "chrome, modal", index !== undefined ? proxies[index] : null);
refreshProxyList();
}
/**
* Deletes the currently selected proxy
*/
function deleteProxy() {
if(document.getElementById('proxyTree').currentIndex == -1) return;
proxies[document.getElementById('proxyTree').currentIndex].erase();
refreshProxyList();
document.getElementById('proxyTree-delete').disabled = true;
}
/**
* Refreshes the proxy pane
*/
function refreshProxyList() {
// get and sort proxies
proxies = Zotero.Proxies.get();
proxies = proxies.sort(function(a, b) {
if(a.multiHost) {
if(b.multiHost) {
if(a.hosts[0] < b.hosts[0]) {
return -1;
} else {
return 1;
}
} else {
return -1;
}
} else if(b.multiHost) {
return 1;
}
if(a.scheme < b.scheme) {
return -1;
} else if(b.scheme > a.scheme) {
return 1;
}
return 0;
});
// erase old children
var treechildren = document.getElementById('proxyTree-rows');
while (treechildren.hasChildNodes()) {
treechildren.removeChild(treechildren.firstChild);
}
// add proxies to list
for (var i=0; i<proxies.length; i++) {
var treeitem = document.createElement('treeitem');
var treerow = document.createElement('treerow');
var hostnameCell = document.createElement('treecell');
var schemeCell = document.createElement('treecell');
hostnameCell.setAttribute('label', proxies[i].multiHost ? Zotero.getString("proxies.multiSite") : proxies[i].hosts[0]);
schemeCell.setAttribute('label', proxies[i].scheme);
treerow.appendChild(hostnameCell);
treerow.appendChild(schemeCell);
treeitem.appendChild(treerow);
treechildren.appendChild(treeitem);
}
document.getElementById('proxyTree').currentIndex = -1;
document.getElementById('proxyTree-delete').disabled = true;
}

View file

@ -334,6 +334,7 @@ To add a new preference:
<caption label="&zotero.preferences.styles.styleManager;"/>
<tree flex="1" id="styleManager" hidecolumnpicker="true" rows="6"
onselect="document.getElementById('styleManager-delete').disabled = undefined"
onkeypress="if (event.keyCode == event.DOM_VK_DELETE) { deleteSelectedStyle(); }">
<treecols>
<treecol id="styleManager-title" label="&zotero.preferences.styles.styleManager.title;" flex="3"/>
@ -344,7 +345,7 @@ To add a new preference:
</tree>
<separator class="thin"/>
<hbox pack="end">
<button label="-" onclick="deleteStyle()"/>
<button disabled="true" id="styleManager-delete" label="-" onclick="deleteStyle()"/>
<button label="+" onclick="addStyle()"/>
</hbox>
<separator/>
@ -353,6 +354,44 @@ To add a new preference:
</prefpane>
<prefpane id="zotero-prefpane-proxies"
label="&zotero.preferences.prefpane.proxies;"
image="chrome://zotero/skin/prefs-proxies.png">
<preferences>
<preference id="pref-proxies-autoRecognize" name="extensions.zotero.proxies.autoRecognize" type="bool"/>
<preference id="pref-proxies-transparent" name="extensions.zotero.proxies.transparent" type="bool"/>
</preferences>
<groupbox>
<caption label="&zotero.preferences.proxies.proxyOptions;"/>
<checkbox id="zotero-proxies-autoRecognize" label="&zotero.preferences.proxies.autoRecognize;"
preference="pref-proxies-autoRecognize" oncommand="Zotero.Proxies.init()"/>
<checkbox id="zotero-proxies-transparent" label="&zotero.preferences.proxies.transparent;"
preference="pref-proxies-transparent" oncommand="Zotero.Proxies.init()"/>
</groupbox>
<groupbox flex="1">
<caption label="&zotero.preferences.proxies.configured;"/>
<tree flex="1" id="proxyTree" hidecolumnpicker="true" rows="6" seltype="single"
ondblclick="showProxyEditor(this.currentIndex)" onselect="document.getElementById('proxyTree-delete').disabled = undefined"
onkeypress="if (event.keyCode == event.DOM_VK_DELETE) { deleteProxy(); }">
<treecols>
<treecol id="proxyTree-hostname" label="&zotero.preferences.proxies.hostname;" flex="1"/>
<treecol id="proxyTree-scheme" label="&zotero.preferences.proxies.scheme;" flex="3"/>
</treecols>
<treechildren id="proxyTree-rows"/>
</tree>
<separator class="thin"/>
<hbox pack="end">
<button disabled="true" id="proxyTree-delete" label="-" onclick="deleteProxy()"/>
<button label="+" onclick="showProxyEditor()"/>
</hbox>
</groupbox>
</prefpane>
<prefpane id="zotero-prefpane-keys"
label="&zotero.preferences.prefpane.keys;"
image="chrome://zotero/skin/prefs-keys.png">

View file

@ -0,0 +1,137 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://chnm.gmu.edu
Licensed under the Educational Community License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.opensource.org/licenses/ecl1.php
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***** END LICENSE BLOCK *****
*/
Zotero_ProxyEditor = new function() {
var treechildren;
var tree;
var treecol;
var multiSite;
/**
* Called when this window is first opened. Sets values if necessary
*/
this.load = function() {
treechildren = document.getElementById("zotero-proxies-hostname-multiSite-tree-children");
tree = document.getElementById("zotero-proxies-hostname-multiSite-tree");
multiSite = document.getElementById("zotero-proxies-multiSite");
if(window.arguments && window.arguments[0]) {
var proxy = window.arguments[0];
document.getElementById("zotero-proxies-scheme").value = proxy.scheme;
document.getElementById("zotero-proxies-multiSite").checked = !!proxy.multiHost;
if(proxy.hosts) {
if(proxy.multiHost) {
this.multiSiteChanged();
for (var i=0; i<proxy.hosts.length; i++) {
_addTreeElement(proxy.hosts[i]);
}
document.getElementById("zotero-proxies-autoAssociate").checked = proxy.autoAssociate;
} else {
document.getElementById("zotero-proxies-hostname-text").value = proxy.hosts[0];
}
}
}
window.sizeToContent();
}
/**
* Called when a user checks/unchecks the Multi-Site checkbox. Shows or hides multi-site
* hostname specification box as necessary.
*/
this.multiSiteChanged = function() {
document.getElementById("zotero-proxies-hostname-multiSite").hidden = !multiSite.checked;
document.getElementById("zotero-proxies-hostname-multiSite-description").hidden = !multiSite.checked;
document.getElementById("zotero-proxies-hostname").hidden = multiSite.checked;
window.sizeToContent();
}
/**
* Called when a row is selected
*/
this.select = function() {
document.getElementById("zotero-proxies-delete").disabled = tree.selectedIndex == -1;
}
/**
* Adds a host when in multi-host mode
*/
this.addHost = function() {
_addTreeElement();
tree.startEditing(treechildren.childNodes.length-1, tree.columns.getFirstColumn());
}
/**
* Deletes a host when in multi-host mode
*/
this.deleteHost = function() {
if(tree.currentIndex == -1) return;
treechildren.removeChild(treechildren.childNodes[tree.currentIndex]);
document.getElementById("zotero-proxies-delete").disabled = true;
}
/**
* Called when the user clicks "OK." Updates proxy for Zotero.Proxy.
*/
this.accept = function() {
var proxy = window.arguments && window.arguments[0] ? window.arguments[0] : new Zotero.Proxy();
proxy.scheme = document.getElementById("zotero-proxies-scheme").value;
proxy.multiHost = multiSite.checked;
if(proxy.multiHost) {
proxy.hosts = [];
var treecol = tree.columns.getFirstColumn();
for(var i=0; i<tree.view.rowCount; i++) {
var host = tree.view.getCellText(i, treecol);
if(host) proxy.hosts.push(host);
}
proxy.autoAssociate = document.getElementById("zotero-proxies-autoAssociate").checked;
} else {
proxy.hosts = [document.getElementById("zotero-proxies-hostname-text").value];
}
var hasErrors = proxy.validate();
if(hasErrors) {
Components.interfaces.nsIPromptService.getService().alert(window,
Zotero.getString("proxies.error"), Zotero.getString("proxies.error."+hasErrors));
if(window.arguments && window.arguments[0]) proxy.revert();
return false;
}
proxy.save();
return true;
}
/**
* Adds an element to the tree
*/
function _addTreeElement(label) {
var treeitem = document.createElement('treeitem');
var treerow = document.createElement('treerow');
var treecell = document.createElement('treecell');
if(label) treecell.setAttribute('label', label);
treerow.appendChild(treecell);
treeitem.appendChild(treerow);
treechildren.appendChild(treeitem);
}
}

View file

@ -0,0 +1,46 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/preferences.css"?>
<!DOCTYPE window SYSTEM "chrome://zotero/locale/preferences.dtd">
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="" buttons="cancel,accept"
id="zotero-proxyEditor"
onload="Zotero_ProxyEditor.load();"
ondialogaccept="return Zotero_ProxyEditor.accept();">
<script src="chrome://zotero/content/include.js"/>
<script src="proxyEditor.js"/>
<checkbox id="zotero-proxies-multiSite" label="&zotero.preferences.proxies.multiSite;"
oncommand="Zotero_ProxyEditor.multiSiteChanged()"/>
<separator class="thin"/>
<vbox id="zotero-proxies-hostname-multiSite" hidden="true">
<checkbox id="zotero-proxies-autoAssociate" label="&zotero.preferences.proxies.autoAssociate;"/>
<tree flex="1" id="zotero-proxies-hostname-multiSite-tree" hidecolumnpicker="true" editable="true" rows="6"
onkeypress="if (event.keyCode == event.DOM_VK_DELETE) { Zotero_ProxyEditor.remove(); }"
onselect="Zotero_ProxyEditor.select();">
<treecols>
<treecol label="&zotero.preferences.proxies.hostname;" id="zotero-proxies-hostname-multiSite-tree-col" flex="1"/>
</treecols>
<treechildren id="zotero-proxies-hostname-multiSite-tree-children"/>
</tree>
<hbox pack="end">
<button id="zotero-proxies-delete" label="-" onclick="Zotero_ProxyEditor.deleteHost()" disabled="true"/>
<button id="zotero-proxies-add" label="+" onclick="Zotero_ProxyEditor.addHost()"/>
</hbox>
</vbox>
<vbox id="zotero-proxies-hostname">
<label value="&zotero.preferences.proxies.hostname;:" control="zotero-proxies-hostname-text"/>
<textbox id="zotero-proxies-hostname-text"/>
</vbox>
<separator class="thin"/>
<label value="&zotero.preferences.proxies.scheme;:" control="zotero-proxies-scheme"/>
<textbox id="zotero-proxies-scheme"/>
<label value="&zotero.preferences.proxies.variables;"/>
<label value="&zotero.preferences.proxies.h_variable;" id="zotero-proxies-hostname-multiSite-description" hidden="true"/>
<label value="&zotero.preferences.proxies.p_variable;"/>
<label value="&zotero.preferences.proxies.d_variable;"/>
<label value="&zotero.preferences.proxies.f_variable;"/>
<label value="&zotero.preferences.proxies.a_variable;"/>
</dialog>

View file

@ -26,185 +26,6 @@
Zotero.Ingester = new Object();
/////////////////////////////////////////////////////////////////
//
// Zotero.Ingester.ProxyMonitor
//
/////////////////////////////////////////////////////////////////
// A singleton for recognizing EZProxies and converting URLs such that databases
// will work from outside them. Unfortunately, this only works with the ($495)
// EZProxy software. If there are open source alternatives, we should support
// them too.
/*
* Precompile proxy regexps
*/
Zotero.Ingester.ProxyMonitor = new function() {
var _ezProxyRe = /\?(?:.+&)?(url|qurl)=([^&]+)/i;
var _juniperProxyRe = /^(https?:\/\/[^\/:]+(?:\:[0-9]+)?)\/(.*)?,DanaInfo=([^+,]*)([^+]*)(?:\+(.*))?$/;
var _pathRe = /([^?]*\/)([^?\/]*)(\?(.*))?$/
/*var _hostRe = new RegExp();
_hostRe.compile("^https?://(([^/:]+)(?:\:([0-9]+))?)");*/
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var on = false;
var _mapFromEZProxy = null;
var _mapToJuniperProxy = null;
var _mapToEZProxy = null;
this.init = init;
this.proxyToProper = proxyToProper;
this.properToProxy = properToProxy;
this.observe = observe;
function init() {
if(!on) {
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "http-on-examine-response", false);
}
on = true;
}
function observe(channel) {
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
try {
// remove content-disposition headers for endnote, etc.
var contentType = channel.getResponseHeader("Content-Type").toLowerCase();
for each(var desiredContentType in Zotero.Ingester.MIMEHandler.URIContentListener.desiredContentTypes) {
if(contentType.length < desiredContentType.length) {
break;
} else {
if(contentType.substr(0, desiredContentType.length) == desiredContentType) {
channel.setResponseHeader("Content-Disposition", "", false);
break;
}
}
}
} catch(e) {}
try {
// find ezproxies
if(channel.getResponseHeader("Server") == "EZproxy") {
// We're connected to an EZproxy
if(channel.responseStatus != "302") {
return;
}
// We should be able to scrape the URL out of this
var m = _ezProxyRe.exec(channel.URI.spec);
if(!m) {
return;
}
// Found URL
var variable = m[1];
var properURL = m[2];
if(variable.toLowerCase() == "qurl") {
properURL = unescape(properURL);
}
var properURI = _parseURL(properURL);
if(!properURI) {
return;
}
// Get the new URL
var newURL = channel.getResponseHeader("Location");
if(!newURL) {
return;
}
var newURI = _parseURL(newURL);
if(!newURI) {
return;
}
if((channel.URI.host == newURI.host && channel.URI.port != newURI.port) ||
(newURI.host != channel.URI.host &&
newURI.hostPort.substr(newURI.hostPort.length-channel.URI.hostPort.length) == channel.URI.hostPort)) {
// Different ports but the same server means EZproxy active
Zotero.debug("EZProxy: host "+newURI.hostPort+" is really "+properURI.hostPort);
// Initialize variables here so people who never use EZProxies
// don't get the (very very minor) speed hit
if(!_mapFromEZProxy) {
_mapFromEZProxy = new Object();
_mapToEZProxy = new Object();
}
_mapFromEZProxy[newURI.hostPort] = properURI.hostPort;
_mapToEZProxy[properURI.hostPort] = newURI.hostPort;
}
}
} catch(e) {}
}
/*
* Returns a page's proper url, adjusting for proxying
*/
function proxyToProper(url) {
var m = _juniperProxyRe.exec(url);
if(m) {
url = "http://"+m[3]+"/"+m[2]+m[5];
if(!_mapToJuniperProxy) _mapToJuniperProxy = new Object();
_mapToJuniperProxy[m[3]] = {prePath:m[1], additionalInfo:m[4], danaInfoBeforeFile:(m[2].substr(m[2].length-1) == "/")};
Zotero.debug("Juniper Proxy: proper url is "+url);
} else if(_mapFromEZProxy) {
// EZProxy detection is active
var uri = _parseURL(url);
if(uri && _mapFromEZProxy[uri.hostPort]) {
url = url.replace(uri.hostPort, _mapFromEZProxy[uri.hostPort]);
Zotero.debug("EZProxy: proper url is "+url);
}
}
return url;
}
/*
* Returns a page's proxied url from the proper url
*/
function properToProxy(url) {
if(_mapToJuniperProxy || _mapToEZProxy) {
// Proxy detection is active
var uri = _parseURL(url);
if(uri) {
if(_mapToEZProxy && _mapToEZProxy[uri.hostPort]) {
// Actually need to map
url = url.replace(uri.hostPort, _mapToEZProxy[uri.hostPort]);
Zotero.debug("EZProxy: proxied url is "+url);
} else if(_mapToJuniperProxy && _mapToJuniperProxy[uri.hostPort]) {
var m = _pathRe.exec(uri.path);
if(_mapToJuniperProxy[uri.hostPort].danaInfoBeforeFile) {
url = _mapToJuniperProxy[uri.hostPort].prePath+m[1]+",DanaInfo="+uri.hostPort+_mapToJuniperProxy[uri.hostPort].additionalInfo+"+";
if(m[2]) url += m[2];
} else {
url = _mapToJuniperProxy[uri.hostPort].prePath+m[1]+m[2]+",DanaInfo="+uri.hostPort+_mapToJuniperProxy[uri.hostPort].additionalInfo+"+";
}
if(m[3]) url += m[3];
Zotero.debug("Juniper Proxy: proxied url is "+url);
}
}
}
return url;
}
/*
* Parses a url into components (hostPort, port, host, and spec)
*/
function _parseURL(url) {
// create an nsIURI (not sure if this is faster than the regular
// expression, but it's at least more kosher)
var uri = ioService.newURI(url, null, null);
return uri;
}
}
Zotero.OpenURL = new function() {
this.resolve = resolve;
this.discoverResolvers = discoverResolvers;

View file

@ -0,0 +1,526 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://chnm.gmu.edu
Licensed under the Educational Community License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.opensource.org/licenses/ecl1.php
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Utilities based in part on code taken from Piggy Bank 2.1.1 (BSD-licensed)
***** END LICENSE BLOCK *****
*/
/**
* A singleton to handle URL rewriting proxies
*/
Zotero.Proxies = new function() {
var on = false;
var proxies = false;
var hosts;
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var autoRecognize = false;
var transparent = false;
this.init = function() {
if(!on) {
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "http-on-examine-response", false);
this.get();
}
on = true;
autoRecognize = Zotero.Prefs.get("proxies.autoRecognize");
transparent = Zotero.Prefs.get("proxies.transparent");
}
/**
* Observe method to capture page loads
*/
this.observe = function(channel) {
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
try {
// remove content-disposition headers for endnote, etc.
var contentType = channel.getResponseHeader("Content-Type").toLowerCase();
for each(var desiredContentType in Zotero.Ingester.MIMEHandler.URIContentListener.desiredContentTypes) {
if(contentType.length < desiredContentType.length) {
break;
} else {
if(contentType.substr(0, desiredContentType.length) == desiredContentType) {
channel.setResponseHeader("Content-Disposition", "", false);
break;
}
}
}
} catch(e) {}
// try to detect a proxy
channel.QueryInterface(Components.interfaces.nsIRequest);
if(channel.loadFlags & Components.interfaces.nsIHttpChannel.LOAD_DOCUMENT_URI) {
channel.QueryInterface(Components.interfaces.nsIHttpChannel);
var url = channel.URI.spec;
// see if there is a proxy we already know
var proxy;
for each(proxy in proxies) {
if(proxy.regexp && proxy.autoAssociate) {
var m = proxy.regexp.exec(url);
if(m) break;
}
}
if(m) {
// add this host if we know a proxy
var host = m[proxy.parameters.indexOf("%h")+1];
if(proxy.hosts.indexOf(host) == -1) {
proxy.hosts.push(host);
proxy.save();
}
} else if(autoRecognize) {
// otherwise, try to detect a proxy
var proxy = false;
for each(var detector in Zotero.Proxies.Detectors) {
try {
proxy = detector(channel);
} catch(e) {
Components.utils.reportError(e);
}
if(proxy) {
var checkState = {value:false};
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var window = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
var button = ps.confirmEx(window,
Zotero.getString("proxies.recognized"),
Zotero.getString("proxies.recognized.message"),
((proxies.length ? 0 : ps.BUTTON_DELAY_ENABLE) + ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK +
ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL + ps.BUTTON_POS_1_DEFAULT),
null, null, null, Zotero.getString("proxies.recognized.disable"), checkState);
if(button == 0) proxy.save();
if(checkState.value) {
autoRecognize = false;
Zotero.Prefs.set("proxies.autoRecognize", false);
}
break;
}
}
}
// try to get an applicable proxy
if(transparent) {
var webNav = null;
try {
webNav = channel.notificationCallbacks.QueryInterface(Components.interfaces.nsIWebNavigation);
} catch(e) {}
if(webNav) {
var proxied = this.properToProxy(url, true);
if(proxied) webNav.loadURI(proxied, 0, channel.URI, null, null);
}
}
}
delete channel;
}
/**
* Gets all proxy objects
*/
this.get = function() {
if(!proxies) {
var rows = Zotero.DB.query("SELECT * FROM proxies");
proxies = [new Zotero.Proxy(row) for each(row in rows)];
this.refreshHostMap();
}
return proxies;
}
/**
* Removes a proxy object from the list of proxy objects
*/
this.remove = function(proxy) {
var index = proxies.indexOf(proxy);
if(index == -1) return false;
proxies.splice(index, 1);
this.refreshHostMap();
return true;
}
/**
* Saves a proxy object not previously in the proxy list
*/
this.save = function(proxy) {
proxies.push(proxy);
for each(var host in proxy.hosts) {
if(!hosts[host]) {
hosts[host] = proxy;
}
}
return proxy;
}
/**
* Refreshes host map; necessary when proxies are added, changed, or deleted
*/
this.refreshHostMap = function() {
hosts = {};
for each(var proxy in proxies) {
for each(var host in proxy.hosts) {
if(!hosts[host]) {
hosts[host] = proxy;
}
}
}
}
/**
* Returns a page's proper url, adjusting for proxying
*/
this.proxyToProper = function(url, onlyReturnIfProxied) {
for each(var proxy in proxies) {
if(proxy.regexp) {
var m = proxy.regexp.exec(url);
if(m) {
var toProper = proxy.toProper(m);
Zotero.debug("Zotero.Proxies.proxyToProper: "+url+" to "+toProper);
return toProper;
}
}
}
return (onlyReturnIfProxied ? false : url);
}
/**
* Returns a page's proxied url from the proper url
*/
this.properToProxy = function(url, onlyReturnIfProxied) {
var uri = ioService.newURI(url, null, null);
if(hosts[uri.hostPort]) {
var toProxy = hosts[uri.hostPort].toProxy(uri);
Zotero.debug("Zotero.Proxies.properToProxy: "+url+" to "+toProxy);
return toProxy;
}
return (onlyReturnIfProxied ? false : url);
}
}
/**
* A class to handle individual proxy servers
*
* @constructor
*/
Zotero.Proxy = function(row) {
if(row) {
this._loadFromRow(row);
} else {
this.hosts = [];
this.multiHost = false;
}
}
const Zotero_Proxy_schemeParameters = {
"%p":"(.*?)", // path
"%d":"(.*?)", // directory
"%f":"(.*?)", // filename
"%a":"(.*?)" // filename
};
const Zotero_Proxy_schemeParameterRegexps = {
"%p":/([^%])%p/,
"%d":/([^%])%d/,
"%f":/([^%])%f/,
"%h":/([^%])%h/,
"%a":/([^%])%a/
}
/**
* Compiles the regular expression against which we match URLs for this proxy
*/
Zotero.Proxy.prototype.compileRegexp = function() {
const metaRe = /[-[\]{}()*+?.\\^$|,#\s]/g;
// take host only if flagged as multiHost
var parametersToCheck = Zotero_Proxy_schemeParameters;
if(this.multiHost) parametersToCheck["%h"] = "([a-zA-Z0-9]+\\.[a-zA-Z0-9\.]+)";
indices = this.indices = {};
this.parameters = [];
for(var param in parametersToCheck) {
var index = this.scheme.indexOf(param);
// avoid escaped matches
while(this.scheme[index-1] == "%") {
this.scheme = this.scheme.substr(0, index-1)+this.scheme.substr(index);
index = this.scheme.indexOf(param, index+1);
}
if(index != -1) {
this.indices[param] = index;
this.parameters.push(param);
}
}
// sort params by index
this.parameters = this.parameters.sort(function(a, b) {
return indices[a]-indices[b];
})
// now replace with regexp fragment in reverse order
var re = "^"+this.scheme.replace(metaRe, "\\$&")+"$";
for(var i=this.parameters.length-1; i>=0; i--) {
var param = this.parameters[i];
re = re.replace(Zotero_Proxy_schemeParameterRegexps[param], "$1"+parametersToCheck[param]);
}
this.regexp = new RegExp(re);
}
/**
* Ensures that the proxy scheme and host settings are valid for this proxy type
*/
Zotero.Proxy.prototype.validate = function() {
if(this.scheme.length < 8 || (this.scheme.substr(0, 7) != "http://" && this.scheme.substr(0, 8) != "https://")) {
return "scheme.noHTTP";
}
if(!this.multiSite && (!this.hosts.length || !this.hosts[0])) {
return "host.invalid";
} else if(this.multiSite && !Zotero_Proxy_schemeParameterRegexps["%h"].test(this.scheme)) {
return "scheme.noHost";
}
if(!Zotero_Proxy_schemeParameterRegexps["%p"].test(this.scheme) &&
(!Zotero_Proxy_schemeParameterRegexps["%d"].test(this.scheme) ||
!Zotero_Proxy_schemeParameterRegexps["%f"].test(this.scheme))) {
return "scheme.noPath";
}
return false;
}
/**
* Saves any changes to this proxy
*/
Zotero.Proxy.prototype.save = function() {
// ensure this proxy is valid
Zotero.debug(this);
var hasErrors = this.validate();
if(hasErrors) throw "Proxy could not be saved because it is invalid: error "+hasErrors;
this.autoAssociate = this.multiHost && this.autoAssociate;
this.compileRegexp();
if(this.proxyID) {
Zotero.Proxies.refreshHostMap();
} else {
Zotero.Proxies.save(this);
}
try {
Zotero.DB.beginTransaction();
if(this.proxyID) {
Zotero.DB.query("UPDATE proxies SET multiHost = ?, autoAssociate = ?, scheme = ? WHERE proxyID = ?",
[this.multiHost ? 1 : 0, this.autoAssociate ? 1 : 0, this.scheme, this.proxyID]);
Zotero.DB.query("DELETE FROM proxyHosts WHERE proxyID = ?", [this.proxyID]);
} else {
this.proxyID = Zotero.DB.query("INSERT INTO proxies (multiHost, autoAssociate, scheme) VALUES (?, ?, ?)",
[this.multiHost ? 1 : 0, this.autoAssociate ? 1 : 0, this.scheme])
}
this.hosts = this.hosts.sort();
var host;
for(var i in this.hosts) {
host = this.hosts[i] = this.hosts[i].toLowerCase();
Zotero.DB.query("INSERT INTO proxyHosts (proxyID, hostname) VALUES (?, ?)",
[this.proxyID, host]);
}
Zotero.DB.commitTransaction();
} catch(e) {
Zotero.DB.rollbackTransaction();
throw(e);
}
}
/**
* Reverts to the previously saved version of this proxy
*/
Zotero.Proxy.prototype.revert = function() {
if(!this.proxyID) throw "Cannot revert an unsaved proxy";
this._loadFromRow(Zotero.DB.rowQuery("SELECT * FROM proxies WHERE proxyID = ?", [this.proxyID]));
}
/**
* Deletes this proxy
*/
Zotero.Proxy.prototype.erase = function() {
if(!this.proxyID) throw "Tried to erase an unsaved proxy";
Zotero.Proxies.remove(this);
try {
Zotero.DB.beginTransaction();
Zotero.DB.query("DELETE FROM proxies WHERE proxyID = ?", [this.proxyID]);
Zotero.DB.query("DELETE FROM proxyHosts WHERE proxyID = ?", [this.proxyID]);
Zotero.DB.commitTransaction();
} catch(e) {
Zotero.DB.rollbackTransaction();
throw(e);
}
}
/**
* Converts a proxied URL to an unproxied URL using this proxy
*
* @param m {Array} The match from running this proxy's regexp against a URL spec
*/
Zotero.Proxy.prototype.toProper = function(m) {
if(this.multiHost) {
properURL = "http://"+m[this.parameters.indexOf("%h")+1]+"/";
} else {
properURL = "http://"+this.hosts[0]+"/";
}
if(this.indices["%p"]) {
properURL += m[this.parameters.indexOf("%p")+1];
} else {
var dir = m[this.parameters.indexOf("%d")+1];
var file = m[this.parameters.indexOf("%f")+1];
if(dir !== "") properURL += dir+"/";
properURL += file;
}
return properURL;
}
/**
* Converts an unproxied URL to a proxied URL using this proxy
*
* @param {nsIURI} uri The nsIURI corresponding to the unproxied URL
*/
Zotero.Proxy.prototype.toProxy = function(uri) {
proxyURL = this.scheme;
for(var i=this.parameters.length-1; i>=0; i--) {
var param = this.parameters[i];
var value = "";
if(param == "%h") {
value = uri.hostPort;
} else if(param == "%p") {
value = uri.path.substr(1);
} else if(param == "%d") {
value = uri.path.substr(0, uri.path.lastIndexOf("/"));
} else if(param == "%f") {
value = uri.path.substr(uri.path.lastIndexOf("/")+1)
}
proxyURL = proxyURL.substr(0, this.indices[param])+value+proxyURL.substr(this.indices[param]+2);
}
return proxyURL;
}
/**
* Loads a proxy object from a DB row
*/
Zotero.Proxy.prototype._loadFromRow = function(row) {
this.proxyID = row.proxyID;
this.multiHost = !!row.multiHost;
this.autoAssociate = !!row.autoAssociate;
this.scheme = row.scheme;
this.hosts = Zotero.DB.columnQuery("SELECT hostname FROM proxyHosts WHERE proxyID = ? ORDER BY hostname", row.proxyID);
this.compileRegexp();
}
Zotero.Proxies.Detectors = new Object();
/**
* Detector for OCLC EZProxy
*/
Zotero.Proxies.Detectors.EZProxy = function(channel) {
const ezProxyRe = /\?(?:.+&)?(url|qurl)=([^&]+)/i;
try {
if(channel.getResponseHeader("Server") != "EZproxy" || channel.responseStatus != "302") {
return false;
}
} catch(e) {
return false
}
// We should be able to scrape the URL out of this
var m = ezProxyRe.exec(channel.URI.spec);
if(!m) return false;
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
// Found URL
var properURL = m[2];
if(m[1].toLowerCase() == "qurl") properURL = unescape(properURL);
var properURI = ioService.newURI(properURL, null, null);
if(!properURI) return false;
// Get the new URL
var newURL = channel.getResponseHeader("Location");
if(!newURL) return false;
// Ignore if we already know about it
if(Zotero.Proxies.proxyToProper(newURL, true)) return false;
// parse into nsIURI
var newURI = ioService.newURI(newURL, null, null);
if(!newURI) return false;
if(channel.URI.host == newURI.host && channel.URI.port != newURI.port) {
// Old style per-port
var proxy = new Zotero.Proxy();
proxy.multiHost = false;
proxy.scheme = newURI.scheme+"://"+newURI.hostPort+"/%p";
proxy.hosts = [properURI.hostPort];
} else if(newURI.host != channel.URI.host) {
// New style rewriting
var proxy = new Zotero.Proxy();
proxy.multiHost = proxy.autoAssociate = true;
proxy.scheme = newURI.scheme+"://"+newURI.hostPort.replace(properURI.host, "%h")+"/%p";
proxy.hosts = [properURI.hostPort];
}
return proxy;
}
/**
* Detector for Juniper Networks WebVPN
*/
Zotero.Proxies.Detectors.Juniper = function(channel) {
const juniperRe = /^(https?:\/\/[^\/:]+(?:\:[0-9]+)?)\/(.*),DanaInfo=([^+,]*)([^+]*)(?:\+(.*))?$/;
try {
var url = channel.URI.spec;
var m = juniperRe.exec(url);
} catch(e) {
return false;
}
if(!m) return false;
var proxy = new Zotero.Proxy();
proxy.multiHost = true;
proxy.scheme = m[1]+"/%d"+",DanaInfo=%h%a+%f";
proxy.hosts = [m[3]];
return proxy;
}

View file

@ -1665,6 +1665,12 @@ Zotero.Schema = new function(){
Zotero.DB.query("INSERT INTO itemAttachments (itemID, linkMode) VALUES (?, ?)", [id, 3]);
}
}
if (i==39) {
Zotero.DB.query("CREATE TABLE proxies (\n proxyID INTEGER PRIMARY KEY,\n multiHost INT,\n autoAssociate INT,\n scheme TEXT\n)");
Zotero.DB.query("CREATE TABLE proxyHosts (\n hostID INTEGER PRIMARY KEY,\n proxyID INTEGER,\n hostname TEXT,\n FOREIGN KEY (proxyID) REFERENCES proxies(proxyID)\n)");
Zotero.DB.query("CREATE INDEX proxyHosts_proxyID ON proxyHosts(proxyID)");
}
}
_updateDBVersion('userdata', toVersion);

View file

@ -271,7 +271,7 @@ Zotero.Translate.prototype.setCollection = function(collection) {
Zotero.Translate.prototype.setLocation = function(location) {
if(this.type == "web") {
// account for proxies
this.location = Zotero.Ingester.ProxyMonitor.proxyToProper(location);
this.location = Zotero.Proxies.proxyToProper(location);
if(this.location != location) {
// figure out if this URL is being proxies
this.locationIsProxied = true;
@ -1109,7 +1109,7 @@ Zotero.Translate.prototype._itemDone = function(item, attachedTo) {
// if item was accessed through a proxy, ensure that the proxy
// address remains in the accessed version
if(this.locationIsProxied && item.url) {
item.url = Zotero.Ingester.ProxyMonitor.properToProxy(item.url);
item.url = Zotero.Proxies.properToProxy(item.url);
}
// create new item
@ -1383,7 +1383,7 @@ Zotero.Translate.prototype._itemDone = function(item, attachedTo) {
}
if(this.locationIsProxied) {
attachment.url = Zotero.Ingester.ProxyMonitor.properToProxy(attachment.url);
attachment.url = Zotero.Proxies.properToProxy(attachment.url);
}
var fileBaseName = Zotero.Attachments.getFileBaseNameFromItem(myID);

View file

@ -555,7 +555,7 @@ Zotero.Utilities.Ingester.prototype.processDocuments = function(urls, processor,
if(this.translate.locationIsProxied) {
for(var i in urls) {
if(this.translate.locationIsProxied) {
urls[i] = Zotero.Ingester.ProxyMonitor.properToProxy(urls[i]);
urls[i] = Zotero.Proxies.properToProxy(urls[i]);
}
// check for a protocol colon
if(!Zotero.Utilities.Ingester._protocolRe.test(urls[i])) {
@ -591,7 +591,7 @@ Zotero.Utilities.Ingester.HTTP.prototype.doGet = function(urls, processor, done,
}
if(this.translate.locationIsProxied) {
url = Zotero.Ingester.ProxyMonitor.properToProxy(url);
url = Zotero.Proxies.properToProxy(url);
}
if(!Zotero.Utilities.Ingester._protocolRe.test(url)) {
throw("invalid URL in processDocuments");
@ -620,7 +620,7 @@ Zotero.Utilities.Ingester.HTTP.prototype.doGet = function(urls, processor, done,
Zotero.Utilities.Ingester.HTTP.prototype.doPost = function(url, body, onDone, requestContentType, responseCharset) {
if(this.translate.locationIsProxied) {
url = Zotero.Ingester.ProxyMonitor.properToProxy(url);
url = Zotero.Ingester.Proxies.properToProxy(url);
}
if(!Zotero.Utilities.Ingester._protocolRe.test(url)) {
throw("invalid URL in processDocuments");

View file

@ -61,6 +61,7 @@
<!ENTITY zotero.preferences.quickCopy.siteEditor.outputFormat "Output Format">
<!ENTITY zotero.preferences.prefpane.styles "Styles">
<!ENTITY zotero.preferences.styles.styleManager "Style Manager">
<!ENTITY zotero.preferences.styles.styleManager.title "Title">
<!ENTITY zotero.preferences.styles.styleManager.updated "Updated">
@ -81,6 +82,23 @@
<!ENTITY zotero.preferences.keys.overrideGlobal "Try to override conflicting shortcuts">
<!ENTITY zotero.preferences.keys.changesTakeEffect "Changes take effect in new windows only">
<!ENTITY zotero.preferences.prefpane.proxies "Proxies">
<!ENTITY zotero.preferences.proxies.proxyOptions "Proxy Options">
<!ENTITY zotero.preferences.proxies.autoRecognize "Automatically recognize common URL-rewriting proxy systems">
<!ENTITY zotero.preferences.proxies.transparent "Transparently redirect requests through previously used proxies">
<!ENTITY zotero.preferences.proxies.configured "Configured Proxies">
<!ENTITY zotero.preferences.proxies.hostname "Hostname">
<!ENTITY zotero.preferences.proxies.scheme "Scheme">
<!ENTITY zotero.preferences.proxies.multiSite "Multi-Site">
<!ENTITY zotero.preferences.proxies.autoAssociate "Automatically associate new hosts">
<!ENTITY zotero.preferences.proxies.variables "You may use the following variables in your proxy scheme:">
<!ENTITY zotero.preferences.proxies.h_variable "&#37;h - The hostname of the proxied site (e.g., www.zotero.org)">
<!ENTITY zotero.preferences.proxies.p_variable "&#37;p - The path of the proxied page excluding the leading slash (e.g., about/index.html)">
<!ENTITY zotero.preferences.proxies.d_variable "&#37;d - The directory path (e.g., about/)">
<!ENTITY zotero.preferences.proxies.f_variable "&#37;f - The filename (e.g., index.html)">
<!ENTITY zotero.preferences.proxies.a_variable "&#37;a - Any string">
<!ENTITY zotero.preferences.prefpane.advanced "Advanced">

View file

@ -498,3 +498,13 @@ styles.updateStyle = Update existing style "%1$S" with "%2$S"?
styles.installed = The style "%S" was installed successfully.
styles.installError = %S does not appear to be a valid CSL file.
styles.deleteStyle = Are you sure you want to delete the style "%1$S"?
proxies.multiSite = Multi-Site
proxies.error = Information Validation Error
proxies.error.scheme.noHTTP = Valid proxy schemes must start with "http://" or "https://"
proxies.error.host.invalid = You must enter a full hostname for the site served by this proxy (e.g., jstor.org).
proxies.error.scheme.noHost = A multi-site proxy scheme must contain the host variable (%h).
proxies.error.scheme.noPath = A valid proxy scheme must contain either the path variable (%p) or the directory and filename variables (%d and %f).
proxies.recognized = Proxy Recognized
proxies.recognized.message = Would you like Zotero to store information about this proxy server to enable saving of references accessed through it?\n\nWARNING: Only click "OK" below if you have accessed this site through your library or another institution you trust.
proxies.recognized.disable = Disable automatic recognition of proxy systems

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -20,7 +20,7 @@ var xpcomFiles = ['zotero',
'data/collections', 'data/cachedTypes', 'data/creator', 'data/creators',
'data/itemFields', 'data/notes', 'data/tag', 'data/tags', 'db', 'enstyle',
'file', 'fulltext', 'id', 'ingester', 'integration', 'itemTreeView', 'mime',
'notifier', 'progressWindow', 'quickCopy', 'report', 'schema', 'search',
'notifier', 'progressWindow', 'proxy', 'quickCopy', 'report', 'schema', 'search',
'sync', 'timeline', 'translate', 'utilities', 'zeroconf'];
for (var i=0; i<xpcomFiles.length; i++) {

View file

@ -79,4 +79,8 @@ pref("extensions.zotero.annotations.warnOnClose", true);
// Server
pref("extensions.zotero.sync.server.autoSync", true);
pref("extensions.zotero.sync.server.username", '');
pref("extensions.zotero.sync.server.compressData", true);
pref("extensions.zotero.sync.server.compressData", true);
// Proxy
pref("extensions.zotero.proxies.autoRecognize", true);
pref("extensions.zotero.proxies.transparent", false);

View file

@ -1,4 +1,4 @@
-- 38
-- 39
-- This file creates tables containing user-specific data -- any changes made
-- here must be mirrored in transition steps in schema.js::_migrateSchema()
@ -254,4 +254,19 @@ CREATE TABLE highlights (
dateModified DATE,
FOREIGN KEY (itemID) REFERENCES itemAttachments(itemID)
);
CREATE INDEX highlights_itemID ON highlights(itemID);
CREATE INDEX highlights_itemID ON highlights(itemID);
CREATE TABLE proxies (
proxyID INTEGER PRIMARY KEY,
multiHost INT,
autoAssociate INT,
scheme TEXT
);
CREATE TABLE proxyHosts (
hostID INTEGER PRIMARY KEY,
proxyID INTEGER,
hostname TEXT
FOREIGN KEY (proxyID) REFERENCES proxies(proxyID)
);
CREATE INDEX proxyHosts_proxyID ON proxyHosts(proxyID);