Merge pull request #1698 from saebekassebil/fixpassword
Support password protected PDF files (Better commiting)
This commit is contained in:
commit
df3efc0aa0
@ -29,3 +29,4 @@ page_of=af {{pageCount}}
|
||||
no_outline=Ingen dokumentoversigt tilgængelig
|
||||
open_file.title=Åbn fil
|
||||
text_annotation_type=[{{type}} Kommentar]
|
||||
request_password=PDF filen er beskyttet med et kodeord:
|
||||
|
@ -42,3 +42,4 @@ zoom_in_label=Zoom In
|
||||
zoom.title=Zoom
|
||||
thumb_page_title=Page {{page}}
|
||||
thumb_page_canvas=Thumbnail of Page {{page}}
|
||||
request_password=PDF is protected by a password:
|
||||
|
63
src/api.js
63
src/api.js
@ -7,20 +7,46 @@
|
||||
* is used, which means it must follow the same origin rules that any XHR does
|
||||
* e.g. No cross domain requests without CORS.
|
||||
*
|
||||
* @param {string|TypedAray} source Either a url to a PDF is located or a
|
||||
* typed array (Uint8Array) already populated with data.
|
||||
* @param {Object} headers An object containing the http headers like this:
|
||||
* { Authorization: "BASIC XXX" }.
|
||||
* @param {string|TypedAray|object} source Can be an url to where a PDF is
|
||||
* located, a typed array (Uint8Array) already populated with data or
|
||||
* and parameter object with the following possible fields:
|
||||
* - url - The URL of the PDF.
|
||||
* - data - A typed array with PDF data.
|
||||
* - httpHeaders - Basic authentication headers.
|
||||
* - password - For decrypting password-protected PDFs.
|
||||
*
|
||||
* @return {Promise} A promise that is resolved with {PDFDocumentProxy} object.
|
||||
*/
|
||||
PDFJS.getDocument = function getDocument(source, headers) {
|
||||
PDFJS.getDocument = function getDocument(source) {
|
||||
var url, data, headers, password, parameters = {};
|
||||
if (typeof source === 'string') {
|
||||
url = source;
|
||||
} else if (isArrayBuffer(source)) {
|
||||
data = source;
|
||||
} else if (typeof source === 'object') {
|
||||
url = source.url;
|
||||
data = source.data;
|
||||
headers = source.httpHeaders;
|
||||
password = source.password;
|
||||
parameters.password = password || null;
|
||||
|
||||
if (!url && !data)
|
||||
error('Invalid parameter array, need either .data or .url');
|
||||
} else {
|
||||
error('Invalid parameter in getDocument, need either Uint8Array, ' +
|
||||
'string or a parameter object');
|
||||
}
|
||||
|
||||
var promise = new PDFJS.Promise();
|
||||
var transport = new WorkerTransport(promise);
|
||||
if (typeof source === 'string') {
|
||||
if (data) {
|
||||
// assuming the data is array, instantiating directly from it
|
||||
transport.sendData(data, parameters);
|
||||
} else if (url) {
|
||||
// fetch url
|
||||
PDFJS.getPdf(
|
||||
{
|
||||
url: source,
|
||||
url: url,
|
||||
progress: function getPDFProgress(evt) {
|
||||
if (evt.lengthComputable)
|
||||
promise.progress({
|
||||
@ -35,12 +61,10 @@ PDFJS.getDocument = function getDocument(source, headers) {
|
||||
headers: headers
|
||||
},
|
||||
function getPDFLoad(data) {
|
||||
transport.sendData(data);
|
||||
transport.sendData(data, parameters);
|
||||
});
|
||||
} else {
|
||||
// assuming the source is array, instantiating directly from it
|
||||
transport.sendData(source);
|
||||
}
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
@ -122,6 +146,11 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
isEncrypted: function PDFDocumentProxy_isEncrypted() {
|
||||
var promise = new PDFJS.Promise();
|
||||
promise.resolve(this.pdfInfo.encrypted);
|
||||
return promise;
|
||||
},
|
||||
destroy: function PDFDocumentProxy_destroy() {
|
||||
this.transport.destroy();
|
||||
}
|
||||
@ -467,6 +496,14 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
||||
this.workerReadyPromise.resolve(pdfDocument);
|
||||
}, this);
|
||||
|
||||
messageHandler.on('NeedPassword', function transportPassword(data) {
|
||||
this.workerReadyPromise.reject(data.exception.message, data.exception);
|
||||
}, this);
|
||||
|
||||
messageHandler.on('IncorrectPassword', function transportBadPass(data) {
|
||||
this.workerReadyPromise.reject(data.exception.message, data.exception);
|
||||
}, this);
|
||||
|
||||
messageHandler.on('GetPage', function transportPage(data) {
|
||||
var pageInfo = data.pageInfo;
|
||||
var page = new PDFPageProxy(pageInfo, this);
|
||||
@ -569,8 +606,8 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
||||
});
|
||||
},
|
||||
|
||||
sendData: function WorkerTransport_sendData(data) {
|
||||
this.messageHandler.send('GetDocRequest', data);
|
||||
sendData: function WorkerTransport_sendData(data, params) {
|
||||
this.messageHandler.send('GetDocRequest', {data: data, params: params});
|
||||
},
|
||||
|
||||
getPage: function WorkerTransport_getPage(pageNumber, promise) {
|
||||
|
15
src/core.js
15
src/core.js
@ -320,19 +320,19 @@ var Page = (function PageClosure() {
|
||||
* `PDFDocument` objects on the main thread created.
|
||||
*/
|
||||
var PDFDocument = (function PDFDocumentClosure() {
|
||||
function PDFDocument(arg, callback) {
|
||||
function PDFDocument(arg, password) {
|
||||
if (isStream(arg))
|
||||
init.call(this, arg);
|
||||
init.call(this, arg, password);
|
||||
else if (isArrayBuffer(arg))
|
||||
init.call(this, new Stream(arg));
|
||||
init.call(this, new Stream(arg), password);
|
||||
else
|
||||
error('PDFDocument: Unknown argument type');
|
||||
}
|
||||
|
||||
function init(stream) {
|
||||
function init(stream, password) {
|
||||
assertWellFormed(stream.length > 0, 'stream must have data');
|
||||
this.stream = stream;
|
||||
this.setup();
|
||||
this.setup(password);
|
||||
this.acroForm = this.catalog.catDict.get('AcroForm');
|
||||
}
|
||||
|
||||
@ -423,11 +423,12 @@ var PDFDocument = (function PDFDocumentClosure() {
|
||||
}
|
||||
// May not be a PDF file, continue anyway.
|
||||
},
|
||||
setup: function PDFDocument_setup(ownerPassword, userPassword) {
|
||||
setup: function PDFDocument_setup(password) {
|
||||
this.checkHeader();
|
||||
var xref = new XRef(this.stream,
|
||||
this.startXRef,
|
||||
this.mainXRefEntriesOffset);
|
||||
this.mainXRefEntriesOffset,
|
||||
password);
|
||||
this.xref = xref;
|
||||
this.catalog = new Catalog(xref);
|
||||
},
|
||||
|
@ -556,7 +556,9 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
||||
var encryptionKey = prepareKeyData(fileIdBytes, passwordBytes,
|
||||
ownerPassword, userPassword, flags,
|
||||
revision, keyLength, encryptMetadata);
|
||||
if (!encryptionKey && password) {
|
||||
if (!encryptionKey && !password) {
|
||||
throw new PasswordException('No password given', 'needpassword');
|
||||
} else if (!encryptionKey && password) {
|
||||
// Attempting use the password as an owner password
|
||||
var decodedPassword = decodeUserPassword(passwordBytes, ownerPassword,
|
||||
revision, keyLength);
|
||||
@ -566,7 +568,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
||||
}
|
||||
|
||||
if (!encryptionKey)
|
||||
error('incorrect password or encryption data');
|
||||
throw new PasswordException('Incorrect Password', 'incorrectpassword');
|
||||
|
||||
this.encryptionKey = encryptionKey;
|
||||
|
||||
|
@ -298,7 +298,7 @@ var Catalog = (function CatalogClosure() {
|
||||
})();
|
||||
|
||||
var XRef = (function XRefClosure() {
|
||||
function XRef(stream, startXRef, mainXRefEntriesOffset) {
|
||||
function XRef(stream, startXRef, mainXRefEntriesOffset, password) {
|
||||
this.stream = stream;
|
||||
this.entries = [];
|
||||
this.xrefstms = {};
|
||||
@ -311,8 +311,7 @@ var XRef = (function XRefClosure() {
|
||||
var encrypt = trailerDict.get('Encrypt');
|
||||
if (encrypt) {
|
||||
var fileId = trailerDict.get('ID');
|
||||
this.encrypt = new CipherTransformFactory(encrypt,
|
||||
fileId[0] /*, password */);
|
||||
this.encrypt = new CipherTransformFactory(encrypt, fileId[0], password);
|
||||
}
|
||||
|
||||
// get the root dictionary (catalog) object
|
||||
|
19
src/util.js
19
src/util.js
@ -58,6 +58,19 @@ function shadow(obj, prop, value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
var PasswordException = (function PasswordExceptionClosure() {
|
||||
function PasswordException(msg, code) {
|
||||
this.name = 'PasswordException';
|
||||
this.message = msg;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
PasswordException.prototype = new Error();
|
||||
PasswordException.constructor = PasswordException;
|
||||
|
||||
return PasswordException;
|
||||
})();
|
||||
|
||||
function bytesToString(bytes) {
|
||||
var str = '';
|
||||
var length = bytes.length;
|
||||
@ -456,7 +469,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() {
|
||||
}
|
||||
|
||||
this.isResolved = true;
|
||||
this.data = data || null;
|
||||
this.data = (typeof data !== 'undefined') ? data : null;
|
||||
var callbacks = this.callbacks;
|
||||
|
||||
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
||||
@ -471,7 +484,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() {
|
||||
}
|
||||
},
|
||||
|
||||
reject: function Promise_reject(reason) {
|
||||
reject: function Promise_reject(reason, exception) {
|
||||
if (this.isRejected) {
|
||||
error('A Promise can be rejected only once ' + this.name);
|
||||
}
|
||||
@ -484,7 +497,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() {
|
||||
var errbacks = this.errbacks;
|
||||
|
||||
for (var i = 0, ii = errbacks.length; i < ii; i++) {
|
||||
errbacks[i].call(null, reason);
|
||||
errbacks[i].call(null, reason, exception);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -88,14 +88,35 @@ var WorkerMessageHandler = {
|
||||
handler.on('GetDocRequest', function wphSetupDoc(data) {
|
||||
// Create only the model of the PDFDoc, which is enough for
|
||||
// processing the content of the pdf.
|
||||
pdfModel = new PDFDocument(new Stream(data));
|
||||
var pdfData = data.data;
|
||||
var pdfPassword = data.params.password;
|
||||
try {
|
||||
pdfModel = new PDFDocument(new Stream(pdfData), pdfPassword);
|
||||
} catch (e) {
|
||||
if (e instanceof PasswordException) {
|
||||
if (e.code === 'needpassword') {
|
||||
handler.send('NeedPassword', {
|
||||
exception: e
|
||||
});
|
||||
} else if (e.code === 'incorrectpassword') {
|
||||
handler.send('IncorrectPassword', {
|
||||
exception: e
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
var doc = {
|
||||
numPages: pdfModel.numPages,
|
||||
fingerprint: pdfModel.getFingerprint(),
|
||||
destinations: pdfModel.catalog.destinations,
|
||||
outline: pdfModel.catalog.documentOutline,
|
||||
info: pdfModel.getDocumentInfo(),
|
||||
metadata: pdfModel.catalog.metadata
|
||||
metadata: pdfModel.catalog.metadata,
|
||||
encrypted: !!pdfModel.xref.encrypt
|
||||
};
|
||||
handler.send('GetDoc', {pdfInfo: doc});
|
||||
});
|
||||
|
@ -331,10 +331,15 @@ var PDFView = {
|
||||
return currentPageNumber;
|
||||
},
|
||||
|
||||
open: function pdfViewOpen(url, scale) {
|
||||
this.url = url;
|
||||
|
||||
document.title = decodeURIComponent(getFileName(url)) || url;
|
||||
open: function pdfViewOpen(url, scale, password) {
|
||||
var parameters = {password: password};
|
||||
if (typeof url === 'string') { // URL
|
||||
this.url = url;
|
||||
document.title = decodeURIComponent(getFileName(url)) || url;
|
||||
parameters.url = url;
|
||||
} else if (url && 'byteLength' in url) { // ArrayBuffer
|
||||
parameters.data = url;
|
||||
}
|
||||
|
||||
if (!PDFView.loadingBar) {
|
||||
PDFView.loadingBar = new ProgressBar('#loadingBar', {});
|
||||
@ -342,12 +347,23 @@ var PDFView = {
|
||||
|
||||
var self = this;
|
||||
self.loading = true;
|
||||
PDFJS.getDocument(url).then(
|
||||
PDFJS.getDocument(parameters).then(
|
||||
function getDocumentCallback(pdfDocument) {
|
||||
self.load(pdfDocument, scale);
|
||||
self.loading = false;
|
||||
},
|
||||
function getDocumentError(message, exception) {
|
||||
if (exception.name === 'PasswordException') {
|
||||
if (exception.code === 'needpassword') {
|
||||
var promptString = mozL10n.get('request_password', null,
|
||||
'PDF is protected by a password:');
|
||||
password = prompt(promptString);
|
||||
if (password && password.length > 0) {
|
||||
return PDFView.open(url, scale, password);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var loadingIndicator = document.getElementById('loading');
|
||||
loadingIndicator.textContent = mozL10n.get('loading_error_indicator',
|
||||
null, 'Error');
|
||||
@ -1531,10 +1547,7 @@ window.addEventListener('change', function webViewerChange(evt) {
|
||||
for (var i = 0; i < data.length; i++)
|
||||
uint8Array[i] = data.charCodeAt(i);
|
||||
|
||||
// TODO using blob instead?
|
||||
PDFJS.getDocument(uint8Array).then(function(pdfDocument) {
|
||||
PDFView.load(pdfDocument);
|
||||
});
|
||||
PDFView.open(uint8Array, 0);
|
||||
};
|
||||
|
||||
// Read as a binary string since "readAsArrayBuffer" is not yet
|
||||
|
Loading…
Reference in New Issue
Block a user