From 75e10efcbda1f794cd461a4bf7412e5d60d35649 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com.> Date: Sun, 7 Jul 2024 22:49:21 +0100 Subject: [PATCH 1/2] auto decrypt, update discord, fix multi file support for some inputs --- .github/ISSUE_TEMPLATE/config.yml | 2 +- README.md | 2 +- .../api/security/PasswordController.java | 2 +- src/main/resources/static/js/downloader.js | 116 ++++++++++++++++-- .../convert/pdf-to-presentation.html | 2 +- .../templates/convert/pdf-to-word.html | 2 +- src/main/resources/templates/error.html | 2 +- .../templates/fragments/errorBanner.html | 2 +- .../fragments/errorBannerPerPage.html | 2 +- .../resources/templates/fragments/navbar.html | 2 +- src/main/resources/templates/home.html | 4 +- src/main/resources/templates/pipeline.html | 2 +- .../resources/templates/remove-pages.html | 2 +- .../templates/security/add-watermark.html | 4 +- .../templates/security/auto-redact.html | 6 +- 15 files changed, 125 insertions(+), 27 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 0f3580d5a8..2725e5f5a9 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: true contact_links: - name: 💬 Discord Server - url: https://discord.gg/Cn8pWhQRxZ + url: https://discord.gg/HYmhKj45pU about: You can join our Discord server for real time discussion and support diff --git a/README.md b/README.md index 4d5eae28b4..dbaab98224 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

Stirling-PDF

[![Docker Pulls](https://img.shields.io/docker/pulls/frooodle/s-pdf)](https://hub.docker.com/r/frooodle/s-pdf) -[![Discord](https://img.shields.io/discord/1068636748814483718?label=Discord)](https://discord.gg/Cn8pWhQRxZ) +[![Discord](https://img.shields.io/discord/1068636748814483718?label=Discord)](https://discord.gg/HYmhKj45pU) [![Docker Image Version (tag latest semver)](https://img.shields.io/docker/v/frooodle/s-pdf/latest)](https://github.com/Stirling-Tools/Stirling-PDF/) [![GitHub Repo stars](https://img.shields.io/github/stars/stirling-tools/stirling-pdf?style=social)](https://github.com/Stirling-Tools/stirling-pdf) [![Paypal Donate](https://img.shields.io/badge/Paypal%20Donate-yellow?style=flat&logo=paypal)](https://www.paypal.com/donate/?hosted_button_id=MN7JPG5G6G3JL) diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java b/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java index 84c4493314..b8e36e0e10 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java @@ -68,7 +68,7 @@ public ResponseEntity addPassword(@ModelAttribute AddPasswordRequest req boolean canModifyAnnotations = request.isCanModifyAnnotations(); boolean canPrint = request.isCanPrint(); boolean canPrintFaithful = request.isCanPrintFaithful(); - + System.out.println(fileInput.getOriginalFilename()); PDDocument document = Loader.loadPDF(fileInput.getBytes()); AccessPermission ap = new AccessPermission(); ap.setCanAssembleDocument(!canAssembleDocument); diff --git a/src/main/resources/static/js/downloader.js b/src/main/resources/static/js/downloader.js index c955bb1bbc..07f7c5b68c 100644 --- a/src/main/resources/static/js/downloader.js +++ b/src/main/resources/static/js/downloader.js @@ -12,7 +12,7 @@ $(document).ready(function () { event.preventDefault(); firstErrorOccurred = false; const url = this.action; - const files = $("#fileInput-input")[0].files; + var files = $("#fileInput-input")[0].files; const formData = new FormData(this); // Remove empty file entries @@ -36,6 +36,17 @@ $(document).ready(function () { }, 5000); try { + + if (!url.includes("remove-password")) { + // Check if any PDF files are encrypted and handle decryption if necessary + const decryptedFiles = await checkAndDecryptFiles(url ,files); + files = decryptedFiles + // Append decrypted files to formData + decryptedFiles.forEach((file, index) => { + formData.set(`fileInput`, file); + }); + } + if (remoteCall === true) { if (override === "multi" || (!multiple && files.length > 1 && override !== "single")) { await submitMultiPdfForm(url, files); @@ -45,24 +56,24 @@ $(document).ready(function () { } clearTimeout(timeoutId); $("#submitBtn").text(originalButtonText); - + // After process finishes, check for boredWaiting and gameDialog open status const boredWaiting = localStorage.getItem("boredWaiting") || "disabled"; const gameDialog = document.getElementById('game-container-wrapper'); if (boredWaiting === "enabled" && gameDialog && gameDialog.open) { // Display a green banner at the bottom of the screen saying "Download complete" let downloadCompleteText = "Download Complete"; - if(window.downloadCompleteText){ + if (window.downloadCompleteText) { downloadCompleteText = window.downloadCompleteText; } - $("body").append('
'+ downloadCompleteText + '
'); - setTimeout(function() { - $("#download-complete-banner").fadeOut("slow", function() { + $("body").append('
' + downloadCompleteText + '
'); + setTimeout(function () { + $("#download-complete-banner").fadeOut("slow", function () { $(this).remove(); // Remove the banner after fading out }); }, 5000); // Banner will fade out after 5 seconds } - + } catch (error) { clearTimeout(timeoutId); handleDownloadError(error); @@ -72,6 +83,97 @@ $(document).ready(function () { }); }); +async function checkAndDecryptFiles(url, files) { + const decryptedFiles = []; + pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs'; + +// Extract the base URL + const baseUrl = new URL(url); + let removePasswordUrl = `${baseUrl.origin}`; + + // Check if there's a path before /api/ + const apiIndex = baseUrl.pathname.indexOf('/api/'); + if (apiIndex > 0) { + removePasswordUrl += baseUrl.pathname.substring(0, apiIndex); + } + + // Append the new endpoint + removePasswordUrl += '/api/v1/security/remove-password'; + + console.log(`Remove password URL: ${removePasswordUrl}`); + + + for (const file of files) { + console.log(`Processing file: ${file.name}`); + if (file.type !== 'application/pdf') { + console.log(`Skipping non-PDF file: ${file.name}`); + decryptedFiles.push(file); + continue; + } + try { + const arrayBuffer = await file.arrayBuffer(); + const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer }); + + console.log(`Attempting to load PDF: ${file.name}`); + const pdf = await loadingTask.promise; + console.log(`File is not encrypted: ${file.name}`); + decryptedFiles.push(file); // If no error, file is not encrypted + } catch (error) { + if (error.name === 'PasswordException' && error.code === 1) { + console.log(`PDF requires password: ${file.name}`, error); + console.log(`Attempting to remove password from PDF: ${file.name} with password.`); + const password = prompt(`This PDF (${file.name}) is encrypted. Please enter the password:`); + + if (!password) { + console.error(`No password provided for encrypted PDF: ${file.name}`); + showErrorBanner(`No password provided for encrypted PDF: ${file.name}`, 'Please enter a valid password.'); + throw error; + } + + try { + // Prepare FormData for the decryption request + const formData = new FormData(); + formData.append('fileInput', file); + formData.append('password', password); + + // Use handleSingleDownload to send the request + const decryptionResult = await fetch(removePasswordUrl, { method: "POST", body: formData }); + + if (decryptionResult && decryptionResult.blob) { + const decryptedBlob = await decryptionResult.blob(); + const decryptedFile = new File([decryptedBlob], file.name, { type: 'application/pdf' }); + + /* // Create a link element to download the file + const link = document.createElement('a'); + link.href = URL.createObjectURL(decryptedBlob); + link.download = 'test.pdf'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +*/ + decryptedFiles.push(decryptedFile); + console.log(`Successfully decrypted PDF: ${file.name}`); + } else { + throw new Error('Decryption failed: No valid response from server'); + } + } catch (decryptError) { + console.error(`Failed to decrypt PDF: ${file.name}`, decryptError); + showErrorBanner(`Failed to decrypt PDF: ${file.name}`, 'Incorrect password or unsupported encryption.'); + throw decryptError; + } + } else { + console.log(`Error loading PDF: ${file.name}`, error); + throw error; + } + } + } + return decryptedFiles; +} + + + + + async function handleSingleDownload(url, formData, isMulti = false, isZip = false) { try { const response = await fetch(url, { method: "POST", body: formData }); diff --git a/src/main/resources/templates/convert/pdf-to-presentation.html b/src/main/resources/templates/convert/pdf-to-presentation.html index 794317609c..ce1f455dd5 100644 --- a/src/main/resources/templates/convert/pdf-to-presentation.html +++ b/src/main/resources/templates/convert/pdf-to-presentation.html @@ -22,8 +22,8 @@
diff --git a/src/main/resources/templates/convert/pdf-to-word.html b/src/main/resources/templates/convert/pdf-to-word.html index 7e5f96c03a..f91b6a60e4 100644 --- a/src/main/resources/templates/convert/pdf-to-word.html +++ b/src/main/resources/templates/convert/pdf-to-word.html @@ -22,8 +22,8 @@
diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html index 314d28b399..00f22c27b0 100644 --- a/src/main/resources/templates/error.html +++ b/src/main/resources/templates/error.html @@ -19,7 +19,7 @@

- +
diff --git a/src/main/resources/templates/fragments/errorBanner.html b/src/main/resources/templates/fragments/errorBanner.html index 32fb9019c5..d682dcb922 100644 --- a/src/main/resources/templates/fragments/errorBanner.html +++ b/src/main/resources/templates/fragments/errorBanner.html @@ -20,7 +20,7 @@ - + diff --git a/src/main/resources/templates/pipeline.html b/src/main/resources/templates/pipeline.html index 0a1d879f2a..1f5f1c7cb1 100644 --- a/src/main/resources/templates/pipeline.html +++ b/src/main/resources/templates/pipeline.html @@ -16,7 +16,7 @@ /> diff --git a/src/main/resources/templates/remove-pages.html b/src/main/resources/templates/remove-pages.html index 58dc18c788..84471ef295 100644 --- a/src/main/resources/templates/remove-pages.html +++ b/src/main/resources/templates/remove-pages.html @@ -21,7 +21,7 @@
- +
diff --git a/src/main/resources/templates/security/add-watermark.html b/src/main/resources/templates/security/add-watermark.html index 12e706e411..71c323746d 100644 --- a/src/main/resources/templates/security/add-watermark.html +++ b/src/main/resources/templates/security/add-watermark.html @@ -20,9 +20,7 @@
-
- -
+
diff --git a/src/main/resources/templates/security/auto-redact.html b/src/main/resources/templates/security/auto-redact.html index 7709af87ab..5be0f0dd6d 100644 --- a/src/main/resources/templates/security/auto-redact.html +++ b/src/main/resources/templates/security/auto-redact.html @@ -17,10 +17,8 @@
-
- -
- +
+
From f4082e3f965d037bb85f99f2508f08180cbb251b Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:51:59 +0100 Subject: [PATCH 2/2] Update PasswordController.java --- .../SPDF/controller/api/security/PasswordController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java b/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java index b8e36e0e10..8a947c8e0b 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/PasswordController.java @@ -68,7 +68,6 @@ public ResponseEntity addPassword(@ModelAttribute AddPasswordRequest req boolean canModifyAnnotations = request.isCanModifyAnnotations(); boolean canPrint = request.isCanPrint(); boolean canPrintFaithful = request.isCanPrintFaithful(); - System.out.println(fileInput.getOriginalFilename()); PDDocument document = Loader.loadPDF(fileInput.getBytes()); AccessPermission ap = new AccessPermission(); ap.setCanAssembleDocument(!canAssembleDocument);