var algo = 'AES-CBC'; var Dropzone = require('dropzone'); var initializeDownload = require('./downloader.js'); /** * * define a dropzone for chill usage * * An event is launched when dropzone is initialize, allowing to customize events * on dropzone : * * ``` * window.addEventListener("chill_dropzone_initialized", (e) => { * // do something with dropzone: * e.detail.dropzone.on("success", (e) => { * // see https://www.dropzonejs.com/#event-success * }); * }); * ``` * */ // load css //require('dropzone/dist/basic.css'); require('dropzone/dist/dropzone.css'); require('./index.scss'); // // disable dropzone autodiscover Dropzone.autoDiscover = false; var keyDefinition = { name: algo, length: 256 }; var searchForZones = function(root) { var zones = root.querySelectorAll('div[data-stored-object]'); for(let i=0; i < zones.length; i++) { initialize(zones[i]); } }; var getUploadUrl = function(zoneData, files) { var generateTempUrlPost = zoneData.zone.querySelector('input[data-async-file-upload]').dataset.generateTempUrlPost, oReq = new XMLHttpRequest() ; // arg, dropzone, you cannot handle async upload... oReq.open("GET", generateTempUrlPost, false); oReq.send(); if (oReq.readyState !== XMLHttpRequest.DONE) { throw new Error("Error while fetching url to upload"); } zoneData.params = JSON.parse(oReq.responseText); return zoneData.params.url; }; var encryptFile = function(originalFile, zoneData, done) { var iv = crypto.getRandomValues(new Uint8Array(16)), reader = new FileReader(), jsKey, rawKey ; zoneData.originalType = originalFile.type; reader.onload = e => { window.crypto.subtle.generateKey(keyDefinition, true, [ "encrypt", "decrypt" ]) .then(key => { jsKey = key; // we register the key somwhere return window.crypto.subtle.exportKey('jwk', key); }).then(exportedKey => { rawKey = exportedKey; // we start encryption return window.crypto.subtle.encrypt({ name: algo, iv: iv}, jsKey, e.target.result); }) .then(encrypted => { zoneData.crypto = { jsKey: jsKey, rawKey: rawKey, iv: iv }; done(new File( [ encrypted ], zoneData.suffix)); }); }; reader.readAsArrayBuffer(originalFile); }; var initialize = function(zone) { var created = document.createElement('div'), initMessage = document.createElement('div'), initContent = zone.dataset.labelInitMessage, zoneData = { zone: zone, suffix: createFilename() }, dropzoneI; created.classList.add('dropzone'); initMessage.classList.add('dz-message'); initMessage.appendChild(document.createTextNode(initContent)); dropzoneI = new Dropzone(created, { url: function(files) { return getUploadUrl(zoneData, files); }, dictDefaultMessage: zone.dataset.dictDefaultMessage, dictFileTooBig: zone.dataset.dictFileTooBig, dictRemoveFile: zone.dataset.dictRemoveFile, dictMaxFilesExceeded: zone.dataset.dictMaxFilesExceeded, dictCancelUpload: zone.dataset.dictCancelUpload, dictCancelUploadConfirm: zone.dataset.dictCancelUploadConfirm, dictUploadCanceled: zone.dataset.dictUploadCanceled, maxFiles: 1, addRemoveLinks: true, transformFile: function(file, done) { encryptFile(file, zoneData, done); }, renameFile: function(file) { return zoneData.suffix; } }); dropzoneI.on("sending", function(file, xhr, formData) { formData.append("redirect", zoneData.params.redirect); formData.append("max_file_size", zoneData.params.max_file_size); formData.append("max_file_count", zoneData.params.max_file_count); formData.append("expires", zoneData.params.expires); formData.append("signature", zoneData.params.signature); }); dropzoneI.on("success", function(file, response) { zoneData.currentFile = file; storeDataInForm(zone, zoneData); }); dropzoneI.on("addedfile", function(file) { if (zoneData.hasOwnProperty('currentFile')) { dropzoneI.removeFile(zoneData.currentFile); } }); dropzoneI.on("removedfile", function(file) { removeDataInForm(zone, zoneData); }); zone.insertBefore(created, zone.firstChild); insertDownloadButton(zone, zoneData); let event = new CustomEvent("chill_dropzone_initialized", { detail: { dropzone: dropzoneI, zoneData: zoneData } }); window.dispatchEvent(event); }; var createFilename = () => { var text = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (let i = 0; i < 7; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; }; var storeDataInForm = (zone, zoneData) => { var inputKey = zone.querySelector('input[data-stored-object-key]'), inputIv = zone.querySelector('input[data-stored-object-iv]'), inputObject = zone.querySelector('input[data-async-file-upload]'), inputType = zone.querySelector('input[data-async-file-type]') ; inputKey.value = JSON.stringify(zoneData.crypto.rawKey); inputIv.value = JSON.stringify(Array.from(zoneData.crypto.iv)); inputType.value = zoneData.originalType; inputObject.value = zoneData.params.prefix + zoneData.suffix; insertDownloadButton(zone); }; var removeDataInForm = (zone, zoneData) => { var inputKey = zone.querySelector('input[data-stored-object-key]'), inputIv = zone.querySelector('input[data-stored-object-iv]'), inputObject = zone.querySelector('input[data-async-file-upload]'), inputType = zone.querySelector('input[data-async-file-type]') ; inputKey.value = ""; inputIv.value = ""; inputType.value = ""; inputObject.value = ""; insertDownloadButton(zone); }; var insertDownloadButton = (zone) => { var existingButtons = zone.querySelectorAll('a[data-download-button]'), newButton = document.createElement('a'), inputKey = zone.querySelector('input[data-stored-object-key]'), inputIv = zone.querySelector('input[data-stored-object-iv]'), inputObject = zone.querySelector('input[data-async-file-upload]'), inputType = zone.querySelector('input[data-async-file-type]'), labelPreparing = zone.dataset.labelPreparing, labelQuietButton = zone.dataset.labelQuietButton, labelReady = zone.dataset.labelReady, tempUrlGenerator = zone.dataset.tempUrlGenerator, tempUrlGeneratorParams = new URLSearchParams() ; // remove existing existingButtons.forEach(function(b) { b.remove(); }); if (inputObject.value === '') { return; } tempUrlGeneratorParams.append('object_name', inputObject.value); newButton.dataset.downloadButton = true; newButton.dataset.key = inputKey.value; newButton.dataset.iv = inputIv.value; newButton.dataset.mimeType = inputType.value; newButton.dataset.labelPreparing = labelPreparing; newButton.dataset.labelReady = labelReady; newButton.dataset.tempUrlGetGenerator = tempUrlGenerator + '?' + tempUrlGeneratorParams.toString(); newButton.classList.add('sc-button', 'bt-download', 'dz-bt-below-dropzone'); newButton.textContent = labelQuietButton; zone.appendChild(newButton); initializeDownload(zone); }; window.addEventListener('load', function(e) { searchForZones(document); }); window.addEventListener('collection-add-entry', function(e) { searchForZones(e.detail.entry); });