Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions js/preload/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,20 @@ window.addEventListener('message', function (e) {
if (e.data?.message === 'downloadFile') {
ipc.send('downloadFile', e.data.url)
}

if (e.data?.message === 'readLocalPDF') {
var requestId = e.data.requestId

if (typeof e.data.url !== 'string') {
window.postMessage({ message: 'readLocalPDFResult', requestId, error: 'invalid PDF URL' }, 'min://app')
return
}

ipc.invoke('readLocalPDF', e.data.url).then(function (fileData) {
var data = new Uint8Array(fileData)
window.postMessage({ message: 'readLocalPDFResult', requestId, data: data.buffer }, 'min://app')
}).catch(function (err) {
window.postMessage({ message: 'readLocalPDFResult', requestId, error: err?.message || 'unable to read local PDF' }, 'min://app')
})
}
})
59 changes: 59 additions & 0 deletions main/download.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,64 @@
const currrentDownloadItems = {}

function isPDFViewerURL (url) {
return typeof url === 'string' && url.startsWith('min://app/pages/pdfViewer/index.html')
}

function getViewerSourceURL (viewerURL) {
if (!isPDFViewerURL(viewerURL)) {
return null
}

try {
var sourceURL = new URLSearchParams(new URL(viewerURL).search).get('url')
if (!sourceURL) {
return null
}

return new URL(sourceURL)
} catch (e) {
return null
}
}

function getAllowedLocalPDFPath (senderFrameURL, requestedURL) {
if (!isPDFViewerURL(senderFrameURL)) {
throw new Error('blocked local PDF read request')
}

if (typeof requestedURL !== 'string') {
throw new Error('invalid PDF URL')
}

var requestedFileURL
try {
requestedFileURL = new URL(requestedURL)
} catch (e) {
throw new Error('invalid PDF URL')
}

if (requestedFileURL.protocol !== 'file:') {
throw new Error('invalid PDF URL protocol')
}

var sourceURL = getViewerSourceURL(senderFrameURL)

if (!sourceURL || sourceURL.protocol !== 'file:' || sourceURL.toString() !== requestedFileURL.toString()) {
throw new Error('blocked local PDF read request')
}

return require('url').fileURLToPath(requestedFileURL)
}

ipc.handle('readLocalPDF', async function (event, requestedURL) {
if (!event.senderFrame) {
throw new Error('blocked local PDF read request')
}

var filePath = getAllowedLocalPDFPath(event.senderFrame.url, requestedURL)
return fs.promises.readFile(filePath)
})

ipc.on('cancelDownload', function (e, path) {
if (currrentDownloadItems[path]) {
currrentDownloadItems[path].cancel()
Expand Down
57 changes: 56 additions & 1 deletion pages/pdfViewer/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import '../../node_modules/pdfjs-dist/web/pdf_viewer.mjs'
pdfjsLib.GlobalWorkerOptions.workerSrc = '../../node_modules/pdfjs-dist/build/pdf.worker.mjs'

var url = new URLSearchParams(window.location.search.replace('?', '')).get('url')
var localPDFRequestId = 0

var eventBus = new pdfjsViewer.EventBus()

Expand Down Expand Up @@ -239,7 +240,61 @@ function setUpPageAnnotationLayer (pageView) {
}
}

pdfjsLib.getDocument({ url: url, withCredentials: true }).promise.then(async function (_pdf) {
function requestLocalPDFData (fileURL) {
return new Promise(function (resolve, reject) {
var requestId = ++localPDFRequestId
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

localPDFRequestId is unique to each viewer instance, so it will be 0 in basically all cases. To create a unique request ID, you probably need to use Math.random() or similar.


function onResponse (e) {
if (e.origin !== 'min://app') {
return
}

if (e.data?.message !== 'readLocalPDFResult' || e.data.requestId !== requestId) {
return
}

window.removeEventListener('message', onResponse)

if (e.data.error) {
reject(new Error(e.data.error))
return
}

if (!e.data.data) {
reject(new Error('missing local PDF data'))
return
}

resolve(new Uint8Array(e.data.data))
}

window.addEventListener('message', onResponse)
window.postMessage({ message: 'readLocalPDF', requestId, url: fileURL }, 'min://app')
})
}

function getPDFDocumentRequest (targetURL, requestLocalPDFData) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can just call the global requestLocalPDFData; it doesn't need to be an argument.

if (typeof targetURL !== 'string') {
return Promise.reject(new Error('invalid PDF URL'))
}

if (targetURL.startsWith('file://')) {
return requestLocalPDFData(targetURL).then(function (fileData) {
return {
data: fileData
}
})
}

return Promise.resolve({
url: targetURL,
withCredentials: true
})
}

getPDFDocumentRequest(url, requestLocalPDFData).then(function (request) {
return pdfjsLib.getDocument(request).promise
}).then(async function (_pdf) {
pdf = _pdf

pageCount = pdf.numPages
Expand Down