Fixes #724, PDF indexing binaries not downloading

And adds Zotero.File.download(uri, path)
This commit is contained in:
Dan Stillman 2015-05-23 18:03:25 -04:00
parent 6b87c641d9
commit ebe41ac51a
4 changed files with 296 additions and 266 deletions

View file

@ -161,15 +161,15 @@ Zotero_Preferences.Search = {
* Check available versions of PDF tools from server and prompt for installation * Check available versions of PDF tools from server and prompt for installation
* if a newer version is available * if a newer version is available
*/ */
checkPDFToolsDownloadVersion: function () { checkPDFToolsDownloadVersion: Zotero.Promise.coroutine(function* () {
var url = Zotero.Fulltext.pdfToolsDownloadBaseURL + 'latest.json'; var url = Zotero.Fulltext.pdfToolsDownloadBaseURL + 'latest.json';
// Find latest version for this platform // Find latest version for this platform
var self = this;
var sent = Zotero.HTTP.doGet(url, function (xmlhttp) {
try { try {
if (xmlhttp.status != 200) { var xmlhttp = yield Zotero.HTTP.request("GET", url, { successCodes: [200, 404] });
throw new Error("Unexpected response code " + xmlhttp.status);
if (xmlhttp.status == 404) {
throw 404;
} }
var platform = Zotero.platform.replace(/\s/g, '-'); var platform = Zotero.platform.replace(/\s/g, '-');
@ -216,9 +216,10 @@ Zotero_Preferences.Search = {
var button = document.getElementById('pdftools-update-button'); var button = document.getElementById('pdftools-update-button');
button.setAttribute('label', Zotero.getString('zotero.preferences.update.upToDate')); button.setAttribute('label', Zotero.getString('zotero.preferences.update.upToDate'));
button.setAttribute('disabled', true); button.setAttribute('disabled', true);
return;
} }
// New version available -- display update prompt // New version available -- display update prompt
else {
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
createInstance(Components.interfaces.nsIPromptService); createInstance(Components.interfaces.nsIPromptService);
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
@ -252,57 +253,34 @@ Zotero_Preferences.Search = {
Zotero.getString('general.install'), Zotero.getString('general.install'),
null, null, null, {}); null, null, null, {});
if (index == 0) { if (index != 0) {
return;
}
document.getElementById('pdftools-update-button').disabled = true; document.getElementById('pdftools-update-button').disabled = true;
var str = Zotero.getString('zotero.preferences.search.pdf.downloading'); var str = Zotero.getString('zotero.preferences.search.pdf.downloading');
document.getElementById('pdftools-update-button').setAttribute('label', str); document.getElementById('pdftools-update-button').setAttribute('label', str);
if (converterVersionAvailable && infoVersionAvailable) { if (converterVersionAvailable) {
Zotero.Fulltext.downloadPDFTool('converter', latestVersion, function (success) { yield Zotero.Fulltext.downloadPDFTool('converter', latestVersion)
if (!success) { .catch(function (e) {
self.onPDFToolsDownloadError("Error downloading pdftotext"); Zotero.logError(e);
return; throw new Error("Error downloading pdftotext");
}
Zotero.Fulltext.downloadPDFTool('info', latestVersion, function (success) {
if (!success) {
self.onPDFToolsDownloadError("Error downloading pdfinfo");
return;
}
self.updatePDFToolsStatus();
});
}); });
} }
else if (converterVersionAvailable) { if (infoVersionAvailable) {
Zotero.Fulltext.downloadPDFTool('converter', latestVersion, function (success) { yield Zotero.Fulltext.downloadPDFTool('info', latestVersion)
if (!success) { .catch(function (e) {
self.onPDFToolsDownloadError("Error downloading pdftotext"); Zotero.logError(e);
return; throw new Error("Error downloading pdfinfo");
}
self.updatePDFToolsStatus();
}); });
} }
else { this.updatePDFToolsStatus();
Zotero.Fulltext.downloadPDFTool('info', latestVersion, function (success) {
if (!success) {
self.onPDFToolsDownloadError("Error downloading pdfinfo");
return;
}
self.updatePDFToolsStatus();
});
}
}
}
} }
catch (e) { catch (e) {
self.onPDFToolsDownloadError(e); this.onPDFToolsDownloadError(e);
} }
}); }),
// Browser is offline
if (!sent) {
this.onPDFToolsDownloadError();
}
},
onPDFToolsDownloadError: function (e) { onPDFToolsDownloadError: function (e) {
@ -311,17 +289,11 @@ Zotero_Preferences.Search = {
Zotero.Fulltext.pdfToolsName) + ' ' Zotero.Fulltext.pdfToolsName) + ' '
+ Zotero.getString('zotero.preferences.search.pdf.viewManualInstructions'); + Zotero.getString('zotero.preferences.search.pdf.viewManualInstructions');
} }
else if (e) { else {
Components.utils.reportError(e); Components.utils.reportError(e);
var str = Zotero.getString('zotero.preferences.search.pdf.toolsDownloadError', Zotero.Fulltext.pdfToolsName) var str = Zotero.getString('zotero.preferences.search.pdf.toolsDownloadError', Zotero.Fulltext.pdfToolsName)
+ ' ' + Zotero.getString('zotero.preferences.search.pdf.tryAgainOrViewManualInstructions'); + ' ' + Zotero.getString('zotero.preferences.search.pdf.tryAgainOrViewManualInstructions');
} }
else {
var info = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULAppInfo);
var browser = info.name; // Returns "Firefox" for Firefox
var str = Zotero.getString('general.browserIsOffline', browser);
}
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.createInstance(Components.interfaces.nsIPromptService); .createInstance(Components.interfaces.nsIPromptService);

View file

@ -355,7 +355,7 @@ Zotero.File = new function(){
)); ));
} }
// Create a stream for async stream copying // Convert text data to stream
if(!(data instanceof Components.interfaces.nsIInputStream)) { if(!(data instanceof Components.interfaces.nsIInputStream)) {
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]. var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Components.interfaces.nsIScriptableUnicodeConverter); createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
@ -363,9 +363,9 @@ Zotero.File = new function(){
data = converter.convertToInputStream(data); data = converter.convertToInputStream(data);
} }
var deferred = Zotero.Promise.defer(), var deferred = Zotero.Promise.defer();
ostream = FileUtils.openSafeFileOutputStream(path); var os = FileUtils.openSafeFileOutputStream(new FileUtils.File(path));
NetUtil.asyncCopy(data, ostream, function(inputStream, status) { NetUtil.asyncCopy(data, os, function(inputStream, status) {
if (!Components.isSuccessCode(status)) { if (!Components.isSuccessCode(status)) {
deferred.reject(new Components.Exception("File write operation failed", status)); deferred.reject(new Components.Exception("File write operation failed", status));
return; return;
@ -376,6 +376,23 @@ Zotero.File = new function(){
}; };
this.download = Zotero.Promise.coroutine(function* (uri, path) {
var msg = "Saving " + (uri.spec ? uri.spec : uri) + " to " + (path.path ? path.path : path);
var deferred = Zotero.Promise.defer();
NetUtil.asyncFetch(uri, function (is, status, request) {
if (!Components.isSuccessCode(status)) {
Zotero.logError(status);
deferred.reject(new Error("Download failed with status " + status));
return;
}
deferred.resolve(is);
});
var is = yield deferred.promise;
yield Zotero.File.putContentsAsync(path, is);
});
/** /**
* Delete a file if it exists, asynchronously * Delete a file if it exists, asynchronously
* *

View file

@ -30,7 +30,7 @@ Zotero.Fulltext = new function(){
this.pdfInfoIsRegistered = pdfInfoIsRegistered; this.pdfInfoIsRegistered = pdfInfoIsRegistered;
this.isCachedMIMEType = isCachedMIMEType; this.isCachedMIMEType = isCachedMIMEType;
this.__defineGetter__("pdfToolsDownloadBaseURL", function() { return 'https://www.zotero.org/download/xpdf/'; }); this.pdfToolsDownloadBaseURL = 'https://www.zotero.org/download/xpdf/';
this.__defineGetter__("pdfToolsName", function() { return 'Xpdf'; }); this.__defineGetter__("pdfToolsName", function() { return 'Xpdf'; });
this.__defineGetter__("pdfToolsURL", function() { return 'http://www.foolabs.com/xpdf/'; }); this.__defineGetter__("pdfToolsURL", function() { return 'http://www.foolabs.com/xpdf/'; });
this.__defineGetter__("pdfConverterName", function() { return 'pdftotext'; }); this.__defineGetter__("pdfConverterName", function() { return 'pdftotext'; });
@ -132,8 +132,7 @@ Zotero.Fulltext = new function(){
/* /*
* Download and install latest PDF tool * Download and install latest PDF tool
*/ */
this.downloadPDFTool = function (tool, version, callback) { this.downloadPDFTool = Zotero.Promise.coroutine(function* (tool, version) {
try {
var ioService = Components.classes["@mozilla.org/network/io-service;1"] var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService); .getService(Components.interfaces.nsIIOService);
@ -146,42 +145,23 @@ Zotero.Fulltext = new function(){
var spec = this.pdfToolsDownloadBaseURL + version + "/" + fileName; var spec = this.pdfToolsDownloadBaseURL + version + "/" + fileName;
var uri = ioService.newURI(spec, null, null); var uri = ioService.newURI(spec, null, null);
var tmpFile = OS.Path.join(Zotero.getTempDirectory().path, fileName);
var file = Zotero.getTempDirectory(); yield Zotero.File.download(uri, tmpFile);
file.append(fileName);
Components.utils.import("resource://gre/modules/NetUtil.jsm"); var fileInfo = yield OS.File.stat(tmpFile);
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Zotero.debug("Saving " + uri.spec + " to " + file.path);
NetUtil.asyncFetch(uri, function (is, status) {
if (!Components.isSuccessCode(status)) {
Zotero.debug(status, 1);
Components.utils.reportError(status);
if (callback) {
callback(false);
}
return;
}
Zotero.File.putContentsAsync(file, is)
.then(function () {
// Delete if too small, since a 404 might not be detected above // Delete if too small, since a 404 might not be detected above
if (file.fileSize < 50000) { if (fileInfo.size < 50000) {
var msg = file.path + " is too small -- deleting"; let msg = tmpFile + " is too small -- deleting";
Zotero.debug(msg, 1); Zotero.logError(msg);
Components.utils.reportError(msg);
try { try {
file.remove(false); yield OS.File.remove(tmpFile);
} }
catch (e) { catch (e) {
Zotero.debug(e, 1); Zotero.logError(e);
Components.utils.reportError(e);
} }
if (callback) { throw new Error(msg);
callback(false);
}
return;
} }
var scriptExt = _getScriptExtension(); var scriptExt = _getScriptExtension();
@ -189,41 +169,50 @@ Zotero.Fulltext = new function(){
// TEMP: disabled // TEMP: disabled
if (false && tool == 'converter') { if (false && tool == 'converter') {
if (Zotero.isWin) { if (Zotero.isWin) {
var content = Zotero.File.getContentsFromURL('resource://zotero/hide.' + scriptExt); let content = yield Zotero.File.getContentsFromURLAsync(
var scriptFile = Zotero.getTempDirectory(); 'resource://zotero/hide.' + scriptExt
scriptFile.append('pdftotext.' + scriptExt); );
Zotero.File.putContents(scriptFile, content); var tmpScriptFile = OS.Path.join(
Zotero.getTempDirectory().path,
'pdftotext.' + scriptExt
);
yield Zotero.File.putContentsAsync(tmpScriptFile, content);
} }
} }
// Write out output redirection script for pdfinfo // Write out output redirection script for pdfinfo
// TEMP: disabled on Windows // TEMP: disabled on Windows
else if (!Zotero.isWin && tool == 'info') { else if (!Zotero.isWin && tool == 'info') {
var content = Zotero.File.getContentsFromURL('resource://zotero/redirect.' + scriptExt); let content = yield Zotero.File.getContentsFromURLAsync(
var scriptFile = Zotero.getTempDirectory(); 'resource://zotero/redirect.' + scriptExt
scriptFile.append('pdfinfo.' + scriptExt); );
Zotero.File.putContents(scriptFile, content); var tmpScriptFile = OS.Path.join(
Zotero.getTempDirectory().path,
'pdfinfo.' + scriptExt
);
yield Zotero.File.putContentsAsync(tmpScriptFile, content);
} }
// Set permissions to 755 // Set permissions to 755
if (Zotero.isMac) { if (Zotero.isMac || Zotero.isLinux) {
file.permissions = 33261; yield OS.File.setPermissions(tmpFile, {
if (scriptFile) { unixMode: 0o755
scriptFile.permissions = 33261; });
} if (tmpScriptFile) {
} yield OS.File.setPermissions(tmpScriptFile, {
else if (Zotero.isLinux) { unixMode: 0o755
file.permissions = 493; });
if (scriptFile) {
scriptFile.permissions = 493;
} }
} }
var destDir = Zotero.getZoteroDirectory() var destDir = Zotero.getZoteroDirectory()
// Move redirect script and executable into data dir // Move redirect script and executable into data dir
if (scriptFile) { if (tmpScriptFile) {
scriptFile.moveTo(destDir, null); yield OS.File.move(
tmpScriptFile,
OS.Path.join(destDir.path, OS.Path.basename(tmpScriptFile))
);
} }
file.moveTo(destDir, null); yield OS.File.move(tmpFile, OS.Path.join(destDir.path, fileName));
// Write the version number to a file // Write the version number to a file
var versionFile = destDir.clone(); var versionFile = destDir.clone();
@ -232,29 +221,10 @@ Zotero.Fulltext = new function(){
if (Zotero.isWin) { if (Zotero.isWin) {
version = '3.02a'; version = '3.02a';
} }
Zotero.File.putContents(versionFile, version + ''); yield Zotero.File.putContentsAsync(versionFile, version + '');
Zotero.Fulltext.registerPDFTool(tool); yield Zotero.Fulltext.registerPDFTool(tool);
if (callback) {
callback(true);
}
})
.catch(function (e) {
Zotero.debug(e, 1);
Components.utils.reportError(e);
callback(false);
}); });
});
}
catch (e) {
Zotero.debug(e, 1);
Components.utils.reportError(e);
if (callback) {
callback(false);
}
}
};
/* /*

View file

@ -0,0 +1,71 @@
describe("Zotero.Fulltext", function () {
describe("#downloadPDFTool()", function () {
var originalBaseURL;
before(function* () {
originalBaseURL = Zotero.Fulltext.pdfToolsDownloadBaseURL;
})
after(function () {
Zotero.Fulltext.pdfToolsDownloadBaseURL = originalBaseURL;
})
it("should install the PDF tools", function* () {
var version = "3.04";
var dataDir = Zotero.getZoteroDirectory().path;
var execFileName = 'pdfinfo-' + Zotero.platform;
var execContents = new Array(50001).join('a');
var execPath = OS.Path.join(dataDir, execFileName);
var versionFileName = execFileName + '.version';
var versionPath = OS.Path.join(dataDir, versionFileName);
var scriptExt = Zotero.isWin ? 'vbs' : 'sh';
var scriptPath = OS.Path.join(dataDir, 'pdfinfo.' + scriptExt);
var scriptContents = yield Zotero.File.getContentsFromURLAsync(
'resource://zotero/redirect.' + scriptExt
);
// Delete existing files
try {
yield OS.File.remove(execPath);
}
catch (e) {}
try {
yield OS.File.remove(versionPath);
}
catch (e) {}
try {
yield OS.File.remove(scriptPath);
}
catch (e) {}
var tmpDir = Zotero.getTempDirectory();
// Create temp version directory
var tmpVersionDir = OS.Path.join(tmpDir.path, version);
yield OS.File.makeDir(tmpVersionDir);
// Create dummy executable file to download
var tmpExecPath = OS.Path.join(tmpVersionDir, execFileName);
yield Zotero.File.putContentsAsync(tmpExecPath, execContents);
// Override the download URL with a file URL for the temp directory
Zotero.Fulltext.pdfToolsDownloadBaseURL = OS.Path.toFileURI(tmpDir.path) + "/";
yield Zotero.Fulltext.downloadPDFTool('info', version);
assert.ok(Zotero.Fulltext.pdfInfoIsRegistered());
assert.equal(
(yield Zotero.File.getContentsAsync(execPath)),
execContents
);
assert.equal((yield OS.File.stat(execPath)).unixMode, 0o755);
assert.equal(
(yield Zotero.File.getContentsAsync(versionPath)),
version
);
assert.equal(
(yield Zotero.File.getContentsAsync(scriptPath)),
scriptContents
);
assert.equal((yield OS.File.stat(scriptPath)).unixMode, 0o755);
yield OS.File.removeDir(tmpVersionDir);
})
})
})