diff --git a/chrome/content/zotero/preferences/preferences_search.js b/chrome/content/zotero/preferences/preferences_search.js index 84469e7fae..2bc54a4bee 100644 --- a/chrome/content/zotero/preferences/preferences_search.js +++ b/chrome/content/zotero/preferences/preferences_search.js @@ -161,148 +161,126 @@ Zotero_Preferences.Search = { * Check available versions of PDF tools from server and prompt for installation * if a newer version is available */ - checkPDFToolsDownloadVersion: function () { + checkPDFToolsDownloadVersion: Zotero.Promise.coroutine(function* () { var url = Zotero.Fulltext.pdfToolsDownloadBaseURL + 'latest.json'; // Find latest version for this platform - var self = this; - var sent = Zotero.HTTP.doGet(url, function (xmlhttp) { - try { - if (xmlhttp.status != 200) { - throw new Error("Unexpected response code " + xmlhttp.status); - } - - var platform = Zotero.platform.replace(/\s/g, '-'); - var json = JSON.parse(xmlhttp.responseText); - var latestVersion = json[platform] || json['default']; - - Zotero.debug("Latest PDF tools version for " + platform + " is " + latestVersion); - - var converterIsRegistered = Zotero.Fulltext.pdfConverterIsRegistered(); - var infoIsRegistered = Zotero.Fulltext.pdfInfoIsRegistered(); - var bothRegistered = converterIsRegistered && infoIsRegistered; - - // On Windows, install if not installed or anything other than 3.02a - if (Zotero.isWin) { - var converterVersionAvailable = !converterIsRegistered - || Zotero.Fulltext.pdfConverterVersion != '3.02a'; - var infoVersionAvailable = !infoIsRegistered - || Zotero.Fulltext.pdfInfoVersion != '3.02a'; - var bothAvailable = converterVersionAvailable && infoVersionAvailable; - latestVersion = "3.02a"; - } - // Install if not installed, version unknown, outdated, or - // Xpdf 3.02/3.04 (to upgrade to Poppler), - else { - var converterVersionAvailable = (!converterIsRegistered || - Zotero.Fulltext.pdfConverterVersion == 'UNKNOWN' - || latestVersion > Zotero.Fulltext.pdfConverterVersion - || (!latestVersion.startsWith('3.02') - && Zotero.Fulltext.pdfConverterVersion.startsWith('3.02')) - || (!latestVersion.startsWith('3.02') && latestVersion != '3.04' - && Zotero.Fulltext.pdfConverterVersion == '3.04')); - var infoVersionAvailable = (!infoIsRegistered || - Zotero.Fulltext.pdfInfoVersion == 'UNKNOWN' - || latestVersion > Zotero.Fulltext.pdfInfoVersion - || (!latestVersion.startsWith('3.02') - && Zotero.Fulltext.pdfInfoVersion.startsWith('3.02')) - || (!latestVersion.startsWith('3.02') && latestVersion != '3.04' - && Zotero.Fulltext.pdfInfoVersion == '3.04')); - var bothAvailable = converterVersionAvailable && infoVersionAvailable; - } - - // Up to date -- disable update button - if (!converterVersionAvailable && !infoVersionAvailable) { - var button = document.getElementById('pdftools-update-button'); - button.setAttribute('label', Zotero.getString('zotero.preferences.update.upToDate')); - button.setAttribute('disabled', true); - } - // New version available -- display update prompt - else { - var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. - createInstance(Components.interfaces.nsIPromptService); - var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) - + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL); - - var msg = Zotero.getString('zotero.preferences.search.pdf.available' - + ((converterIsRegistered || infoIsRegistered) ? 'Updates' : 'Downloads'), - [Zotero.platform, 'zotero.org']) + '\n\n'; - - if (converterVersionAvailable) { - let tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform', - [Zotero.Fulltext.pdfConverterName, latestVersion]); - msg += '- ' + tvp + '\n'; - } - if (infoVersionAvailable) { - let tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform', - [Zotero.Fulltext.pdfInfoName, latestVersion]); - msg += '- ' + tvp + '\n'; - } - msg += '\n'; - msg += Zotero.getString('zotero.preferences.search.pdf.zoteroCanInstallVersion' - + (bothAvailable ? 's' : '')); - - var index = ps.confirmEx(null, - converterIsRegistered ? - Zotero.getString('general.updateAvailable') : '', - msg, - buttonFlags, - converterIsRegistered ? - Zotero.getString('general.upgrade') : - Zotero.getString('general.install'), - null, null, null, {}); - - if (index == 0) { - document.getElementById('pdftools-update-button').disabled = true; - var str = Zotero.getString('zotero.preferences.search.pdf.downloading'); - document.getElementById('pdftools-update-button').setAttribute('label', str); - - if (converterVersionAvailable && infoVersionAvailable) { - Zotero.Fulltext.downloadPDFTool('converter', latestVersion, function (success) { - if (!success) { - self.onPDFToolsDownloadError("Error downloading pdftotext"); - return; - } - Zotero.Fulltext.downloadPDFTool('info', latestVersion, function (success) { - if (!success) { - self.onPDFToolsDownloadError("Error downloading pdfinfo"); - return; - } - self.updatePDFToolsStatus(); - }); - }); - } - else if (converterVersionAvailable) { - Zotero.Fulltext.downloadPDFTool('converter', latestVersion, function (success) { - if (!success) { - self.onPDFToolsDownloadError("Error downloading pdftotext"); - return; - } - self.updatePDFToolsStatus(); - }); - } - else { - Zotero.Fulltext.downloadPDFTool('info', latestVersion, function (success) { - if (!success) { - self.onPDFToolsDownloadError("Error downloading pdfinfo"); - return; - } - self.updatePDFToolsStatus(); - }); - } - } - } + try { + var xmlhttp = yield Zotero.HTTP.request("GET", url, { successCodes: [200, 404] }); + + if (xmlhttp.status == 404) { + throw 404; } - catch (e) { - self.onPDFToolsDownloadError(e); + + var platform = Zotero.platform.replace(/\s/g, '-'); + var json = JSON.parse(xmlhttp.responseText); + var latestVersion = json[platform] || json['default']; + + Zotero.debug("Latest PDF tools version for " + platform + " is " + latestVersion); + + var converterIsRegistered = Zotero.Fulltext.pdfConverterIsRegistered(); + var infoIsRegistered = Zotero.Fulltext.pdfInfoIsRegistered(); + var bothRegistered = converterIsRegistered && infoIsRegistered; + + // On Windows, install if not installed or anything other than 3.02a + if (Zotero.isWin) { + var converterVersionAvailable = !converterIsRegistered + || Zotero.Fulltext.pdfConverterVersion != '3.02a'; + var infoVersionAvailable = !infoIsRegistered + || Zotero.Fulltext.pdfInfoVersion != '3.02a'; + var bothAvailable = converterVersionAvailable && infoVersionAvailable; + latestVersion = "3.02a"; } - }); - - // Browser is offline - if (!sent) { - this.onPDFToolsDownloadError(); + // Install if not installed, version unknown, outdated, or + // Xpdf 3.02/3.04 (to upgrade to Poppler), + else { + var converterVersionAvailable = (!converterIsRegistered || + Zotero.Fulltext.pdfConverterVersion == 'UNKNOWN' + || latestVersion > Zotero.Fulltext.pdfConverterVersion + || (!latestVersion.startsWith('3.02') + && Zotero.Fulltext.pdfConverterVersion.startsWith('3.02')) + || (!latestVersion.startsWith('3.02') && latestVersion != '3.04' + && Zotero.Fulltext.pdfConverterVersion == '3.04')); + var infoVersionAvailable = (!infoIsRegistered || + Zotero.Fulltext.pdfInfoVersion == 'UNKNOWN' + || latestVersion > Zotero.Fulltext.pdfInfoVersion + || (!latestVersion.startsWith('3.02') + && Zotero.Fulltext.pdfInfoVersion.startsWith('3.02')) + || (!latestVersion.startsWith('3.02') && latestVersion != '3.04' + && Zotero.Fulltext.pdfInfoVersion == '3.04')); + var bothAvailable = converterVersionAvailable && infoVersionAvailable; + } + + // Up to date -- disable update button + if (!converterVersionAvailable && !infoVersionAvailable) { + var button = document.getElementById('pdftools-update-button'); + button.setAttribute('label', Zotero.getString('zotero.preferences.update.upToDate')); + button.setAttribute('disabled', true); + return; + } + + // New version available -- display update prompt + var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. + createInstance(Components.interfaces.nsIPromptService); + var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) + + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL); + + var msg = Zotero.getString('zotero.preferences.search.pdf.available' + + ((converterIsRegistered || infoIsRegistered) ? 'Updates' : 'Downloads'), + [Zotero.platform, 'zotero.org']) + '\n\n'; + + if (converterVersionAvailable) { + let tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform', + [Zotero.Fulltext.pdfConverterName, latestVersion]); + msg += '- ' + tvp + '\n'; + } + if (infoVersionAvailable) { + let tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform', + [Zotero.Fulltext.pdfInfoName, latestVersion]); + msg += '- ' + tvp + '\n'; + } + msg += '\n'; + msg += Zotero.getString('zotero.preferences.search.pdf.zoteroCanInstallVersion' + + (bothAvailable ? 's' : '')); + + var index = ps.confirmEx(null, + converterIsRegistered ? + Zotero.getString('general.updateAvailable') : '', + msg, + buttonFlags, + converterIsRegistered ? + Zotero.getString('general.upgrade') : + Zotero.getString('general.install'), + null, null, null, {}); + + if (index != 0) { + return; + } + + document.getElementById('pdftools-update-button').disabled = true; + var str = Zotero.getString('zotero.preferences.search.pdf.downloading'); + document.getElementById('pdftools-update-button').setAttribute('label', str); + + if (converterVersionAvailable) { + yield Zotero.Fulltext.downloadPDFTool('converter', latestVersion) + .catch(function (e) { + Zotero.logError(e); + throw new Error("Error downloading pdftotext"); + }); + } + if (infoVersionAvailable) { + yield Zotero.Fulltext.downloadPDFTool('info', latestVersion) + .catch(function (e) { + Zotero.logError(e); + throw new Error("Error downloading pdfinfo"); + }); + } + this.updatePDFToolsStatus(); } - }, + catch (e) { + this.onPDFToolsDownloadError(e); + } + }), onPDFToolsDownloadError: function (e) { @@ -311,17 +289,11 @@ Zotero_Preferences.Search = { Zotero.Fulltext.pdfToolsName) + ' ' + Zotero.getString('zotero.preferences.search.pdf.viewManualInstructions'); } - else if (e) { + else { Components.utils.reportError(e); var str = Zotero.getString('zotero.preferences.search.pdf.toolsDownloadError', Zotero.Fulltext.pdfToolsName) + ' ' + 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"] .createInstance(Components.interfaces.nsIPromptService); diff --git a/chrome/content/zotero/xpcom/file.js b/chrome/content/zotero/xpcom/file.js index 66b43f6dd6..413ae8e35f 100644 --- a/chrome/content/zotero/xpcom/file.js +++ b/chrome/content/zotero/xpcom/file.js @@ -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)) { var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]. createInstance(Components.interfaces.nsIScriptableUnicodeConverter); @@ -363,9 +363,9 @@ Zotero.File = new function(){ data = converter.convertToInputStream(data); } - var deferred = Zotero.Promise.defer(), - ostream = FileUtils.openSafeFileOutputStream(path); - NetUtil.asyncCopy(data, ostream, function(inputStream, status) { + var deferred = Zotero.Promise.defer(); + var os = FileUtils.openSafeFileOutputStream(new FileUtils.File(path)); + NetUtil.asyncCopy(data, os, function(inputStream, status) { if (!Components.isSuccessCode(status)) { deferred.reject(new Components.Exception("File write operation failed", status)); 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 * diff --git a/chrome/content/zotero/xpcom/fulltext.js b/chrome/content/zotero/xpcom/fulltext.js index 64bdc34784..df1c01a2ae 100644 --- a/chrome/content/zotero/xpcom/fulltext.js +++ b/chrome/content/zotero/xpcom/fulltext.js @@ -30,7 +30,7 @@ Zotero.Fulltext = new function(){ this.pdfInfoIsRegistered = pdfInfoIsRegistered; 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__("pdfToolsURL", function() { return 'http://www.foolabs.com/xpdf/'; }); this.__defineGetter__("pdfConverterName", function() { return 'pdftotext'; }); @@ -132,129 +132,99 @@ Zotero.Fulltext = new function(){ /* * Download and install latest PDF tool */ - this.downloadPDFTool = function (tool, version, callback) { - try { - var ioService = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - - if (tool == 'converter') { - var fileName = this.pdfConverterFileName; + this.downloadPDFTool = Zotero.Promise.coroutine(function* (tool, version) { + var ioService = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + + if (tool == 'converter') { + var fileName = this.pdfConverterFileName; + } + else { + var fileName = this.pdfInfoFileName; + } + + var spec = this.pdfToolsDownloadBaseURL + version + "/" + fileName; + var uri = ioService.newURI(spec, null, null); + var tmpFile = OS.Path.join(Zotero.getTempDirectory().path, fileName); + + yield Zotero.File.download(uri, tmpFile); + + var fileInfo = yield OS.File.stat(tmpFile); + + // Delete if too small, since a 404 might not be detected above + if (fileInfo.size < 50000) { + let msg = tmpFile + " is too small -- deleting"; + Zotero.logError(msg); + try { + yield OS.File.remove(tmpFile); } - else { - var fileName = this.pdfInfoFileName; + catch (e) { + Zotero.logError(e); } - - var spec = this.pdfToolsDownloadBaseURL + version + "/" + fileName; - var uri = ioService.newURI(spec, null, null); - - var file = Zotero.getTempDirectory(); - file.append(fileName); - - Components.utils.import("resource://gre/modules/NetUtil.jsm"); - 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 - if (file.fileSize < 50000) { - var msg = file.path + " is too small -- deleting"; - Zotero.debug(msg, 1); - Components.utils.reportError(msg); - try { - file.remove(false); - } - catch (e) { - Zotero.debug(e, 1); - Components.utils.reportError(e); - } - if (callback) { - callback(false); - } - return; - } - - var scriptExt = _getScriptExtension(); - // On Windows, write out script to hide pdftotext console window - // TEMP: disabled - if (false && tool == 'converter') { - if (Zotero.isWin) { - var content = Zotero.File.getContentsFromURL('resource://zotero/hide.' + scriptExt); - var scriptFile = Zotero.getTempDirectory(); - scriptFile.append('pdftotext.' + scriptExt); - Zotero.File.putContents(scriptFile, content); - } - } - // Write out output redirection script for pdfinfo - // TEMP: disabled on Windows - else if (!Zotero.isWin && tool == 'info') { - var content = Zotero.File.getContentsFromURL('resource://zotero/redirect.' + scriptExt); - var scriptFile = Zotero.getTempDirectory(); - scriptFile.append('pdfinfo.' + scriptExt); - Zotero.File.putContents(scriptFile, content); - } - - // Set permissions to 755 - if (Zotero.isMac) { - file.permissions = 33261; - if (scriptFile) { - scriptFile.permissions = 33261; - } - } - else if (Zotero.isLinux) { - file.permissions = 493; - if (scriptFile) { - scriptFile.permissions = 493; - } - } - - var destDir = Zotero.getZoteroDirectory() - // Move redirect script and executable into data dir - if (scriptFile) { - scriptFile.moveTo(destDir, null); - } - file.moveTo(destDir, null); - - // Write the version number to a file - var versionFile = destDir.clone(); - versionFile.append(fileName + '.version'); - // TEMP - if (Zotero.isWin) { - version = '3.02a'; - } - Zotero.File.putContents(versionFile, version + ''); - - Zotero.Fulltext.registerPDFTool(tool); - - if (callback) { - callback(true); - } - }) - .catch(function (e) { - Zotero.debug(e, 1); - Components.utils.reportError(e); - callback(false); - }); + throw new Error(msg); + } + + var scriptExt = _getScriptExtension(); + // On Windows, write out script to hide pdftotext console window + // TEMP: disabled + if (false && tool == 'converter') { + if (Zotero.isWin) { + let content = yield Zotero.File.getContentsFromURLAsync( + 'resource://zotero/hide.' + scriptExt + ); + var tmpScriptFile = OS.Path.join( + Zotero.getTempDirectory().path, + 'pdftotext.' + scriptExt + ); + yield Zotero.File.putContentsAsync(tmpScriptFile, content); + } + } + // Write out output redirection script for pdfinfo + // TEMP: disabled on Windows + else if (!Zotero.isWin && tool == 'info') { + let content = yield Zotero.File.getContentsFromURLAsync( + 'resource://zotero/redirect.' + scriptExt + ); + var tmpScriptFile = OS.Path.join( + Zotero.getTempDirectory().path, + 'pdfinfo.' + scriptExt + ); + yield Zotero.File.putContentsAsync(tmpScriptFile, content); + } + + // Set permissions to 755 + if (Zotero.isMac || Zotero.isLinux) { + yield OS.File.setPermissions(tmpFile, { + unixMode: 0o755 }); - } - catch (e) { - Zotero.debug(e, 1); - Components.utils.reportError(e); - if (callback) { - callback(false); + if (tmpScriptFile) { + yield OS.File.setPermissions(tmpScriptFile, { + unixMode: 0o755 + }); } } - }; + + var destDir = Zotero.getZoteroDirectory() + // Move redirect script and executable into data dir + if (tmpScriptFile) { + yield OS.File.move( + tmpScriptFile, + OS.Path.join(destDir.path, OS.Path.basename(tmpScriptFile)) + ); + } + yield OS.File.move(tmpFile, OS.Path.join(destDir.path, fileName)); + + // Write the version number to a file + var versionFile = destDir.clone(); + versionFile.append(fileName + '.version'); + // TEMP + if (Zotero.isWin) { + version = '3.02a'; + } + yield Zotero.File.putContentsAsync(versionFile, version + ''); + + yield Zotero.Fulltext.registerPDFTool(tool); + }); /* diff --git a/test/tests/fulltextTest.js b/test/tests/fulltextTest.js new file mode 100644 index 0000000000..1c54f43fcd --- /dev/null +++ b/test/tests/fulltextTest.js @@ -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); + }) + }) +})