فهرست منبع

Folders can now be dropped into the CyberChef input

n1474335 2 سال پیش
والد
کامیت
dd6eae52ef
1فایلهای تغییر یافته به همراه89 افزوده شده و 2 حذف شده
  1. 89 2
      src/web/waiters/InputWaiter.mjs

+ 89 - 2
src/web/waiters/InputWaiter.mjs

@@ -905,7 +905,7 @@ class InputWaiter {
      *
      *
      * @param {event} e
      * @param {event} e
      */
      */
-    inputDrop(e) {
+    async inputDrop(e) {
         // This will be set if we're dragging an operation
         // This will be set if we're dragging an operation
         if (e.dataTransfer.effectAllowed === "move")
         if (e.dataTransfer.effectAllowed === "move")
             return false;
             return false;
@@ -917,8 +917,95 @@ class InputWaiter {
         // Dropped text is handled by the editor itself
         // Dropped text is handled by the editor itself
         if (e.dataTransfer.getData("Text")) return;
         if (e.dataTransfer.getData("Text")) return;
 
 
+        // Dropped files
         if (e?.dataTransfer?.files?.length > 0) {
         if (e?.dataTransfer?.files?.length > 0) {
-            this.loadUIFiles(e.dataTransfer.files);
+            let files = [];
+
+            // Handling the files as FileSystemEntry objects allows us to open directories,
+            // but relies on a function that may be deprecated in future.
+            if (Object.prototype.hasOwnProperty.call(DataTransferItem.prototype, "webkitGetAsEntry")) {
+                const fileEntries = await this.getAllFileEntries(e.dataTransfer.items);
+                // Read all FileEntry objects into File objects
+                files = await Promise.all(fileEntries.map(async fe => await this.getFile(fe)));
+            } else {
+                files = e.dataTransfer.files;
+            }
+
+            this.loadUIFiles(files);
+        }
+    }
+
+    /**
+     *
+     * @param {DataTransferItemList} dataTransferItemList
+     * @returns {FileSystemEntry[]}
+     */
+    async getAllFileEntries(dataTransferItemList) {
+        const fileEntries = [];
+        // Use BFS to traverse entire directory/file structure
+        const queue = [];
+        // Unfortunately dataTransferItemList is not iterable i.e. no forEach
+        for (let i = 0; i < dataTransferItemList.length; i++) {
+            // Note webkitGetAsEntry a non-standard feature and may change
+            // Usage is necessary for handling directories
+            queue.push(dataTransferItemList[i].webkitGetAsEntry());
+        }
+        while (queue.length > 0) {
+            const entry = queue.shift();
+            if (entry.isFile) {
+                fileEntries.push(entry);
+            } else if (entry.isDirectory) {
+                queue.push(...await this.readAllDirectoryEntries(entry.createReader()));
+            }
+        }
+        return fileEntries;
+    }
+
+    /**
+     * Get all the entries (files or sub-directories) in a directory by calling
+     * readEntries until it returns empty array
+     *
+     * @param {FileSystemDirectoryReader} directoryReader
+     * @returns {FileSystemEntry[]}
+     */
+    async readAllDirectoryEntries(directoryReader) {
+        const entries = [];
+        let readEntries = await this.readEntriesPromise(directoryReader);
+        while (readEntries.length > 0) {
+            entries.push(...readEntries);
+            readEntries = await this.readEntriesPromise(directoryReader);
+        }
+        return entries;
+    }
+
+    /**
+     * Wrap readEntries in a promise to make working with readEntries easier.
+     * readEntries will return only some of the entries in a directory
+     * e.g. Chrome returns at most 100 entries at a time
+     *
+     * @param {FileSystemDirectoryReader} directoryReader
+     * @returns {Promise}
+     */
+    async readEntriesPromise(directoryReader) {
+        try {
+            return await new Promise((resolve, reject) => {
+                directoryReader.readEntries(resolve, reject);
+            });
+        } catch (err) {
+            log.error(err);
+        }
+    }
+
+    /**
+     * Reads a FileEntry and returns it as a File object
+     * @param {FileEntry} fileEntry
+     * @returns {File}
+     */
+    async getFile(fileEntry) {
+        try {
+            return new Promise((resolve, reject) => fileEntry.file(resolve, reject));
+        } catch (err) {
+            log.error(err);
         }
         }
     }
     }