Fixes password for range request loading

This commit is contained in:
Yury Delendik 2013-05-09 17:35:23 -05:00
parent 220c827e9e
commit 61a7738a5d
6 changed files with 73 additions and 29 deletions

View File

@ -17,7 +17,7 @@
/* globals CanvasGraphics, combineUrl, createScratchCanvas, error, ErrorFont, /* globals CanvasGraphics, combineUrl, createScratchCanvas, error, ErrorFont,
Font, FontLoader, globalScope, info, isArrayBuffer, loadJpegStream, Font, FontLoader, globalScope, info, isArrayBuffer, loadJpegStream,
MessageHandler, PDFJS, PDFObjects, Promise, StatTimer, warn, MessageHandler, PDFJS, PDFObjects, Promise, StatTimer, warn,
WorkerMessageHandler */ WorkerMessageHandler, PasswordResponses */
'use strict'; 'use strict';
@ -39,9 +39,16 @@
* to manually serve range requests for data in the PDF. See viewer.js for * to manually serve range requests for data in the PDF. See viewer.js for
* an example of pdfDataRangeTransport's interface. * an example of pdfDataRangeTransport's interface.
* *
* @param {function} passwordCallback is optional. It is used to request a
* password if wrong or no password was provided. The callback receives two
* parameters: function that needs to be called with new password and reason
* (see {PasswordResponses}).
*
* @return {Promise} A promise that is resolved with {PDFDocumentProxy} object. * @return {Promise} A promise that is resolved with {PDFDocumentProxy} object.
*/ */
PDFJS.getDocument = function getDocument(source, pdfDataRangeTransport) { PDFJS.getDocument = function getDocument(source,
pdfDataRangeTransport,
passwordCallback) {
var workerInitializedPromise, workerReadyPromise, transport; var workerInitializedPromise, workerReadyPromise, transport;
if (typeof source === 'string') { if (typeof source === 'string') {
@ -71,6 +78,7 @@ PDFJS.getDocument = function getDocument(source, pdfDataRangeTransport) {
transport = new WorkerTransport(workerInitializedPromise, transport = new WorkerTransport(workerInitializedPromise,
workerReadyPromise, pdfDataRangeTransport); workerReadyPromise, pdfDataRangeTransport);
workerInitializedPromise.then(function transportInitialized() { workerInitializedPromise.then(function transportInitialized() {
transport.passwordCallback = passwordCallback;
transport.fetchDocument(params); transport.fetchDocument(params);
}); });
return workerReadyPromise; return workerReadyPromise;
@ -482,6 +490,8 @@ var WorkerTransport = (function WorkerTransportClosure() {
this.pagePromises = []; this.pagePromises = [];
this.embeddedFontsUsed = false; this.embeddedFontsUsed = false;
this.passwordCallback = null;
// If worker support isn't disabled explicit and the browser has worker // If worker support isn't disabled explicit and the browser has worker
// support, create a new web worker and test if it/the browser fullfills // support, create a new web worker and test if it/the browser fullfills
// all requirements to run parts of pdf.js in a web worker. // all requirements to run parts of pdf.js in a web worker.
@ -559,6 +569,10 @@ var WorkerTransport = (function WorkerTransportClosure() {
function WorkerTransport_setupMessageHandler(messageHandler) { function WorkerTransport_setupMessageHandler(messageHandler) {
this.messageHandler = messageHandler; this.messageHandler = messageHandler;
function updatePassword(password) {
messageHandler.send('UpdatePassword', password);
}
var pdfDataRangeTransport = this.pdfDataRangeTransport; var pdfDataRangeTransport = this.pdfDataRangeTransport;
if (pdfDataRangeTransport) { if (pdfDataRangeTransport) {
pdfDataRangeTransport.addRangeListener(function(begin, chunk) { pdfDataRangeTransport.addRangeListener(function(begin, chunk) {
@ -588,10 +602,18 @@ var WorkerTransport = (function WorkerTransportClosure() {
}, this); }, this);
messageHandler.on('NeedPassword', function transportPassword(data) { messageHandler.on('NeedPassword', function transportPassword(data) {
if (this.passwordCallback) {
return this.passwordCallback(updatePassword,
PasswordResponses.NEED_PASSWORD);
}
this.workerReadyPromise.reject(data.exception.message, data.exception); this.workerReadyPromise.reject(data.exception.message, data.exception);
}, this); }, this);
messageHandler.on('IncorrectPassword', function transportBadPass(data) { messageHandler.on('IncorrectPassword', function transportBadPass(data) {
if (this.passwordCallback) {
return this.passwordCallback(updatePassword,
PasswordResponses.INCORRECT_PASSWORD);
}
this.workerReadyPromise.reject(data.exception.message, data.exception); this.workerReadyPromise.reject(data.exception.message, data.exception);
}, this); }, this);

View File

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
/* globals bytesToString, DecryptStream, error, isInt, isName, Name, /* globals bytesToString, DecryptStream, error, isInt, isName, Name,
PasswordException, stringToBytes */ PasswordException, PasswordResponses, stringToBytes */
'use strict'; 'use strict';
@ -575,7 +575,8 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
ownerPassword, userPassword, flags, ownerPassword, userPassword, flags,
revision, keyLength, encryptMetadata); revision, keyLength, encryptMetadata);
if (!encryptionKey && !password) { if (!encryptionKey && !password) {
throw new PasswordException('No password given', 'needpassword'); throw new PasswordException('No password given',
PasswordResponses.NEED_PASSWORD);
} else if (!encryptionKey && password) { } else if (!encryptionKey && password) {
// Attempting use the password as an owner password // Attempting use the password as an owner password
var decodedPassword = decodeUserPassword(passwordBytes, ownerPassword, var decodedPassword = decodeUserPassword(passwordBytes, ownerPassword,
@ -586,7 +587,8 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
} }
if (!encryptionKey) if (!encryptionKey)
throw new PasswordException('Incorrect Password', 'incorrectpassword'); throw new PasswordException('Incorrect Password',
PasswordResponses.INCORRECT_PASSWORD);
this.encryptionKey = encryptionKey; this.encryptionKey = encryptionKey;

View File

@ -56,6 +56,13 @@ var BasePdfManager = (function BasePdfManagerClosure() {
requestLoadedStream: function BasePdfManager_requestLoadedStream() { requestLoadedStream: function BasePdfManager_requestLoadedStream() {
return new NotImplementedException(); return new NotImplementedException();
},
updatePassword: function BasePdfManager_updatePassword(password) {
this.pdfModel.xref.password = this.password = password;
if (this.passwordChangedPromise) {
this.passwordChangedPromise.resolve();
}
} }
}; };

View File

@ -139,6 +139,11 @@ function shadow(obj, prop, value) {
return value; return value;
} }
var PasswordResponses = PDFJS.PasswordResponses = {
NEED_PASSWORD: 1,
INCORRECT_PASSWORD: 2
};
var PasswordException = (function PasswordExceptionClosure() { var PasswordException = (function PasswordExceptionClosure() {
function PasswordException(msg, code) { function PasswordException(msg, code) {
this.name = 'PasswordException'; this.name = 'PasswordException';

View File

@ -18,7 +18,7 @@
MissingPDFException, PasswordException, PDFDocument, PDFJS, Promise, MissingPDFException, PasswordException, PDFDocument, PDFJS, Promise,
Stream, UnknownErrorException, warn, NetworkManager, LocalPdfManager, Stream, UnknownErrorException, warn, NetworkManager, LocalPdfManager,
NetworkPdfManager, XRefParseException, NotImplementedException, NetworkPdfManager, XRefParseException, NotImplementedException,
isInt */ isInt, PasswordResponses */
'use strict'; 'use strict';
@ -267,11 +267,11 @@ var WorkerMessageHandler = {
var onFailure = function(e) { var onFailure = function(e) {
if (e instanceof PasswordException) { if (e instanceof PasswordException) {
if (e.code === 'needpassword') { if (e.code === PasswordResponses.NEED_PASSWORD) {
handler.send('NeedPassword', { handler.send('NeedPassword', {
exception: e exception: e
}); });
} else if (e.code === 'incorrectpassword') { } else if (e.code === PasswordResponses.INCORRECT_PASSWORD) {
handler.send('IncorrectPassword', { handler.send('IncorrectPassword', {
exception: e exception: e
}); });
@ -291,10 +291,17 @@ var WorkerMessageHandler = {
} }
}; };
getPdfManager(data).then(function() { getPdfManager(data).then(function pdfManagerReady() {
loadDocument(false).then(onSuccess, function(ex) { loadDocument(false).then(onSuccess, function loadFailure(ex) {
// Try again with recoveryMode == true // Try again with recoveryMode == true
if (!(ex instanceof XRefParseException)) { if (!(ex instanceof XRefParseException)) {
if (ex instanceof PasswordException) {
// after password exception prepare to receive a new password
// to repeat loading
pdfManager.passwordChangedPromise = new Promise();
pdfManager.passwordChangedPromise.then(pdfManagerReady);
}
onFailure(ex); onFailure(ex);
return; return;
} }
@ -349,6 +356,10 @@ var WorkerMessageHandler = {
}); });
}); });
handler.on('UpdatePassword', function wphSetupUpdatePassword(data) {
pdfManager.updatePassword(data);
});
handler.on('GetAnnotationsRequest', function wphSetupGetAnnotations(data) { handler.on('GetAnnotationsRequest', function wphSetupGetAnnotations(data) {
pdfManager.getPage(data.pageIndex).then(function(page) { pdfManager.getPage(data.pageIndex).then(function(page) {
pdfManager.ensure(page, 'getAnnotationsData', []).then( pdfManager.ensure(page, 'getAnnotationsData', []).then(

View File

@ -1048,30 +1048,27 @@ var PDFView = {
this.pdfDocument = null; this.pdfDocument = null;
var self = this; var self = this;
self.loading = true; self.loading = true;
PDFJS.getDocument(parameters, pdfDataRangeTransport).then( var passwordNeeded = function passwordNeeded(updatePassword, reason) {
var promptString = mozL10n.get('request_password', null,
'PDF is protected by a password:');
if (reason === PDFJS.PasswordResponses.INCORRECT_PASSWORD) {
promptString += '\n' + mozL10n.get('invalid_password', null,
'Invalid Password.');
}
password = prompt(promptString);
if (password && password.length > 0) {
return updatePassword(password);
}
};
PDFJS.getDocument(parameters, pdfDataRangeTransport, passwordNeeded).then(
function getDocumentCallback(pdfDocument) { function getDocumentCallback(pdfDocument) {
self.load(pdfDocument, scale); self.load(pdfDocument, scale);
self.loading = false; self.loading = false;
}, },
function getDocumentError(message, exception) { function getDocumentError(message, exception) {
if (exception && exception.name === 'PasswordException') {
if (exception.code === 'needpassword' ||
exception.code === 'incorrectpassword') {
var promptString = mozL10n.get('request_password', null,
'PDF is protected by a password:');
if (exception.code === 'incorrectpassword') {
promptString += '\n' + mozL10n.get('invalid_password', null,
'Invalid Password.');
}
password = prompt(promptString);
if (password && password.length > 0) {
return PDFView.open(url, scale, password);
}
}
}
var loadingErrorMessage = mozL10n.get('loading_error', null, var loadingErrorMessage = mozL10n.get('loading_error', null,
'An error occurred while loading the PDF.'); 'An error occurred while loading the PDF.');