From 2f55b79b4c8158123081b8be45da598f2c7cbd9f Mon Sep 17 00:00:00 2001 From: Omay <70179858+Omay238@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:05:59 -0700 Subject: [PATCH 1/8] do stuff --- .DS_Store | Bin 6148 -> 0 bytes README.md | 9 +- __pycache__/app.cpython-312.pyc | Bin 2948 -> 0 bytes app.py | 116 +++++++++++++++- partstostitch/.DS_Store | Bin 6148 -> 0 bytes requirements.txt | 1 + stitch.py | 46 ------- templates/download.html | 112 +++++++++++++++ templates/index.html | 178 +++++------------------- templates/upload.html | 234 ++++++++++++++++++++++++++++++++ 10 files changed, 501 insertions(+), 195 deletions(-) delete mode 100644 .DS_Store delete mode 100644 __pycache__/app.cpython-312.pyc delete mode 100644 partstostitch/.DS_Store delete mode 100644 stitch.py create mode 100644 templates/download.html create mode 100644 templates/upload.html diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 0e9fe459c3afabe3a729104e47a17208ed8983c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK!D<^Z5FI>HC8Xdy>Ib2N!vw=SiZjqCnEwj_t7xVv5xFWp@&-gSL2hCPPn zhjQwzzmN~f7j#BaOcFcPr&1aX%)D7?M)Ev?#kK#be7>^em3>ZU#!7+xDp9k|i z3t}1I>{XD(QC_s$*Q~NrefGS@YrM|CcoRAI3cr};eSdmNU!QTg;MVtp<7hk|HeY;{ zY2iodc&ZYjaEu{mCs7*8d0)=bFju*rp5P95hRxl@qTB1WMEC8xWlJpHzizk0n{IEp zbok3x2Oo}x-{K^dKQ!x%Dj;cSSlLVTHdOY9!sGLfHv5*cn8nN7CY`MWnwW0{`gZnuTs`Ki{m3|I!9JOk!t;PofZgeAZ-U>SJG0NoD) zmC!XkAbQ%++YEWxz6UpMf3y*`)XX)8qaB{UrNk8L$leR}8S~z#H_jB=fee y6-RHaK>dnJLUE3~WA diff --git a/README.md b/README.md index 989f27b..10d6712 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,14 @@ To deploy this project you'll need to install the dependencies included in the r pip install -r requirements.txt ``` -Replace the client token and the channel ID in app.py with your client token and your channel ID. If you don't have a Discord bot made and invited to a server already, you will need to do so. +If you don't have a Discord bot made and invited to a server already, you will need to do so. + +Create a file called `.env` and add the following to it: + +```env +DISCORD_TOKEN=your_discord_token +DISCORD_CHANNEL=your_discord_channel_id +``` Following that, run diff --git a/__pycache__/app.cpython-312.pyc b/__pycache__/app.cpython-312.pyc deleted file mode 100644 index 3a60b6ed8bb1e29f323288b0ba5491836af6730f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2948 zcmaJ@O>7&-6`on{E=jFKiIx)mK{i#ZwbqEmI#C+bk!&cICCjmx6jl+74Y1g7hZL#( zQD>J{sMr7q1dsCtwTA_!&+2vBS zrFKBR`Frzb=DqKIvwvu7Ye7)H=>LZNM?mOrtP>Qz-qX4L--EiJ?i0RmSIGJhQCIlV!-|YdhhMano z5X3mx_X?7^)JjVpY`#6R;264$sR-J?LAxN)-bC`}HlcP>)l?&6nagS^Ica5z2HP%q zb#fv(nYS|*)ut^gr;|yVzeZlMoaA{+BT02>X=JJ73Z8M=2IgAfZ?i@jeJk~?kJY5% z3O`IG7&-jl8!*#+@IpOgFx)6XsBSb}un@qyp@qY3_^ubzQ;JaYM9d%ACtHT^eUcW|2=M{2Rv)+>`dgbO+Nr52B2= zr6XI?U{xBdNyp2R+q|^J_f`46_0df}@vR`;e)Hy=-*H$txf?>_ksa3hr-)$u(#=bi z_W4JA+Vk%m9667 zE$Vt{P?jijfh({jHf3hrXx>&bx@8lM9%20+K!yU8V-=meNfc*+LEl!h#O<2->=1}VfHBEGqGyk?7h-^^C?FVl z9r}V`G)h@tSnD!k_i&u))=^AtS3>@ZaTN^dO5| zM~8tb2r7`>|Ao~)fqCI?KnwP(7pFmc1;#f|71;d{NF9DAc+cwuzR`HMZ^5_4JK*_W zaciLu1OWla8h397SqZ&_KJ4&VQUI2`0Pu=3`K6Z&Y>Q?M(c^a&&o_IgC6GH9tc+Y0}@b1LP ziF5d?=kUZh|JA8DwB3lJ7;4^B6!v0h(?uL|N9~6{9+{-a93%sYEKF7wq+=}|y z1b$^kn9mf&4Z%C^hG+aw3~&s|sw|ZlL%HI4?~(G|D6pI)-b3%vu`AAb$>|CnU3*F7 z`-goFKJ|+2MaB$EE9&Gd9fMg00sD6#c0w5AKch1>bmmVe=KVVUDoU#tZ&~Hgj)))= zlXsF^ErZpT!CK4la`SiU;dnW^g*vLJqtYWk7}`WHZK3fh8s9|cg4I7=zqY@VIlQ74faBD}N02KIX%>r*BT*9V$;h;=3VGe4m%k{{?4QYG?oe diff --git a/app.py b/app.py index 84d1986..36e6186 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,13 @@ -from flask import Flask, request, jsonify, render_template +from flask import Flask, request, jsonify, render_template, send_from_directory import discord import asyncio import threading +import io +from dotenv import load_dotenv +import os +import json + +load_dotenv() app = Flask(__name__) @@ -9,10 +15,31 @@ intents = discord.Intents.default() client = discord.Client(intents=intents) + @app.route('/') def index(): return render_template('index.html') +@app.route('/upload') +def upload(): + return render_template('upload.html') + +@app.route('/download') +def download(): + # Read the existing data from db.json + try: + with open('db.json', 'r') as db_file: + db_data = json.load(db_file) + files = db_data.get('files', {}) + except (FileNotFoundError, json.JSONDecodeError): + files = {} + + # Convert the files dictionary to a list including the number of parts + files_list = [name for name, info in files.items()] + + return render_template('download.html', files=files_list) + + def start_discord_bot(): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) @@ -21,29 +48,104 @@ def start_discord_bot(): async def on_ready(): print('Discord client is ready.') - client.run('') #Replace with client token + client.run(os.environ['DISCORD_TOKEN']) + # Run Discord client in a background thread threading.Thread(target=start_discord_bot, daemon=True).start() -@app.route('/upload', methods=['POST']) + +@app.route('/uploadFile', methods=['POST']) def upload_file(): file = request.files['file'] - + filename = file.filename + # Ensure the Discord client is ready before attempting to send if not client.is_closed(): future = asyncio.run_coroutine_threadsafe(send_file_to_discord(file), client.loop) result = future.result() # This waits for the coroutine to finish and returns its result print(result) - + # Read the existing data from db.json + try: + with open('db.json', 'r') as db_file: + db_data = json.load(db_file) + except (FileNotFoundError, json.JSONDecodeError): + db_data = {'files': {}} + + # Increment the parts count for the uploaded file + if filename in db_data['files']: + db_data['files'][filename]['parts'] += 1 + else: + db_data['files'][filename] = {'parts': 1} + + # Write the updated data back to db.json + with open('db.json', 'w') as db_file: + json.dump(db_data, db_file, indent=4) + return jsonify({'message': 'File uploaded successfully!'}) +@app.route('/downloadFile/') +def download_file(filename): + # Ensure the directory exists + os.makedirs('partstostitch', exist_ok=True) + + # Remove the files from partstostich after finish + @app.teardown_request + def remove_files(): + for file in os.listdir('partstostitch'): + os.remove(os.path.join('partstostitch', file)) + + # Ensure the Discord client is ready before attempting to fetch messages + if not client.is_closed(): + # Run the coroutine in the same thread as the Discord client's event loop + future = asyncio.run_coroutine_threadsafe( + fetch_and_save_files(filename), client.loop + ) + result = future.result() # This waits for the coroutine to finish and returns its result + if result > 1: # Check if multiple files were downloaded + # Merge the files + with open(f'partstostitch/{filename}', 'wb') as merged_file: + for part_num in range(1, result + 1): + part_filename = f'{filename}.part{part_num}' + part_path = os.path.join('partstostitch', part_filename) + with open(part_path, 'rb') as part_file: + merged_file.write(part_file.read()) + os.remove(part_path) # Remove the part file after merging + return send_from_directory('partstostitch', filename, as_attachment=True, mimetype='application/octet-stream') + elif result == 1: + # Only one file, no need to merge + return send_from_directory('partstostitch', filename, as_attachment=True, mimetype='application/octet-stream') + else: + # No files were downloaded + return jsonify({'error': 'No files were found to download'}), 404 + else: + return jsonify({'error': 'Discord client is not ready'}), 503 + +async def fetch_and_save_files(filename): + channel = client.get_channel(int(os.environ['DISCORD_CHANNEL'])) + messages = [message async for message in channel.history()] + count = 0 + + for message in messages: + for attachment in message.attachments: + if attachment.filename.startswith(filename): + file_bytes = await attachment.read() + file_path = os.path.join('partstostitch', attachment.filename) + with open(file_path, 'wb') as file: + file.write(file_bytes) + count += 1 + + return count + + async def send_file_to_discord(file): print("Sending file to Discord:", file.filename) - channel = client.get_channel() # Replace with channel ID - message = await channel.send(file=discord.File(file.stream, filename=file.filename)) + channel = client.get_channel(int(os.environ['DISCORD_CHANNEL'])) + file_bytes = file.read() + message = await channel.send(file=discord.File(io.BytesIO(file_bytes), filename=file.filename)) print(message) + if __name__ == '__main__': app.run(debug=True) diff --git a/partstostitch/.DS_Store b/partstostitch/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 + + + + Download Files + + + + + + + + + + +
+
+
+
+ +
+ +
+ {% for file in files %} +

+ {{ file }} +

+
+ {% endfor %} +
+
+ + + + + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 08137e3..c9c8740 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,144 +1,40 @@ - - - - - - - - - - - -
-
-
-
- Drop file here or click to upload - -
- -
-
-
- - + + + + + - + + \ No newline at end of file diff --git a/templates/upload.html b/templates/upload.html new file mode 100644 index 0000000..af7e8d6 --- /dev/null +++ b/templates/upload.html @@ -0,0 +1,234 @@ + + + + + Upload Files + + + + + + + + + + +
+
+
+
+ +
+ + + +
+ Drop file here or click to upload + +
+ +
+
+
+ + + + + + + + \ No newline at end of file From d76b3201d2ef72bb828bf6cafc1b5945e0cdf5d0 Mon Sep 17 00:00:00 2001 From: Omay <70179858+Omay238@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:06:57 -0700 Subject: [PATCH 2/8] oops i forgot the .gitignore --- .gitignore | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index a756780..462882f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ -partstostitch/aduhm.mp4 -partstostitch/aduhm.part1 -partstostitch/aduhm.part2 -partstostitch/aduhm.part3 -partstostitch/aduhm.part4 -partstostitch/aduhm.part5 -partstostitch/aduhm.part6 +partstostitch +.env +__pycache__/ +.idea/ +venv/ +.vscode/ +db.json +.DS_Store +**/.DS_Store \ No newline at end of file From ecbd4c45b40b850acd2dac5ca5b10dbe8bbc155d Mon Sep 17 00:00:00 2001 From: Omay <70179858+Omay238@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:06:45 -0700 Subject: [PATCH 3/8] download page not working right now, but i'm just making a backup so i don't delete everything --- app.py | 24 +++++--- templates/download.html | 73 ++++++++++++++++++---- templates/upload.html | 130 +++++++++++++++++++++++++++++----------- 3 files changed, 172 insertions(+), 55 deletions(-) diff --git a/app.py b/app.py index 36e6186..c292368 100644 --- a/app.py +++ b/app.py @@ -75,9 +75,9 @@ def upload_file(): # Increment the parts count for the uploaded file if filename in db_data['files']: - db_data['files'][filename]['parts'] += 1 + db_data['files'][filename.split(".part")[0]]['parts'] += 1 else: - db_data['files'][filename] = {'parts': 1} + db_data['files'][filename.split(".part")[0]] = {'parts': 1} # Write the updated data back to db.json with open('db.json', 'w') as db_file: @@ -85,17 +85,23 @@ def upload_file(): return jsonify({'message': 'File uploaded successfully!'}) +should_remove_files = False + +@app.after_request +def remove_files(response): + global should_remove_files + if should_remove_files: + for file in os.listdir('partstostitch'): + os.remove(os.path.join('partstostitch', file)) + should_remove_files = False + return response + @app.route('/downloadFile/') def download_file(filename): + global should_remove_files # Ensure the directory exists os.makedirs('partstostitch', exist_ok=True) - # Remove the files from partstostich after finish - @app.teardown_request - def remove_files(): - for file in os.listdir('partstostitch'): - os.remove(os.path.join('partstostitch', file)) - # Ensure the Discord client is ready before attempting to fetch messages if not client.is_closed(): # Run the coroutine in the same thread as the Discord client's event loop @@ -112,9 +118,11 @@ def remove_files(): with open(part_path, 'rb') as part_file: merged_file.write(part_file.read()) os.remove(part_path) # Remove the part file after merging + should_remove_files = True return send_from_directory('partstostitch', filename, as_attachment=True, mimetype='application/octet-stream') elif result == 1: # Only one file, no need to merge + should_remove_files = True return send_from_directory('partstostitch', filename, as_attachment=True, mimetype='application/octet-stream') else: # No files were downloaded diff --git a/templates/download.html b/templates/download.html index f937042..7338237 100644 --- a/templates/download.html +++ b/templates/download.html @@ -78,7 +78,7 @@ let reader = new FileReader(); reader.onload = function (event) { // Set the key in the input fields - document.getElementById('secretKey').value = event.targe.result; + document.getElementById('secretKey').value = event.target.result; }; reader.readAsText(file); } @@ -88,18 +88,65 @@ .then(response => response.blob()) .then(blob => blob.text()) .then(data => { - // Decrypt the content using AES - let decrypted = CryptoJS.AES.decrypt(data, document.getElementById('secretKey').value); - let decryptedData = decrypted.toString(CryptoJS.enc - .Utf8); // Convert decrypted data to UTF-8 string - let blob2 = new Blob([decryptedData], { - type: 'application/octet-stream' - }); // Create a Blob from the decrypted data - // Create a download link and click it to start the download - let a = document.createElement('a'); - a.href = window.URL.createObjectURL(blob2); - a.download = filename; - a.click(); + if (document.getElementById('secretKey').value != "") { + let keyJson = { + "alg": "A256GCM", + "ext": true, + "k": document.getElementById('secretKey').value, + "key_ops": [ + "encrypt", + "decrypt" + ], + "kty": "oct" + } + crypto.subtle.importKey( + "jwk", + keyJson, + { + name: "AES-GCM", + }, + true, + ["encrypt", "decrypt"] + ).then(key => { + let iv = data.slice(0, 12); + let ivBuffer = new Uint8Array(iv.split('').map(byte => byte.charCodeAt(0))).buffer; + let encryptedData = data.slice(12); + let encryptedDataBuffer = new Uint8Array(encryptedData.split('').map(x => x.charCodeAt(0))).buffer; + window.crypto.subtle.decrypt( + { + name: "AES-GCM", + iv: ivBuffer + }, + key, + encryptedDataBuffer + ).then(decryptedData => { + let blob2 = new Blob([decryptedData], { + type: 'application/octet-stream' + }); // Create a Blob from the decrypted data + // Create a download link and click it to start the download + let a = document.createElement('a'); + a.href = window.URL.createObjectURL(blob2); + a.download = filename; + a.click(); + }).catch(error => { + console.error('Error:', error); + // Handle errors here, such as displaying a notification to the user + }); + }).catch(error => { + console.error('Error:', error); + // Handle errors here, such as displaying a notification to the user + }); + } else { + let decryptedData = data; + let blob2 = new Blob([decryptedData], { + type: 'application/octet-stream' + }); // Create a Blob from the decrypted data + // Create a download link and click it to start the download + let a = document.createElement('a'); + a.href = window.URL.createObjectURL(blob2); + a.download = filename; + a.click(); + } }) .catch(error => { console.error('Error:', error); diff --git a/templates/upload.html b/templates/upload.html index af7e8d6..77ee146 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -85,23 +85,26 @@ document.getElementById('generateKeyButton').addEventListener('click', function () { // Generate random key - let secretKey = generateRandomKey(); - document.getElementById('secretKey').value = secretKey; + let key = window.crypto.subtle.generateKey({ + name: "AES-GCM", + length: 256 + }, + true, + ["encrypt", "decrypt"] + ) + .then(key => { + // Return key as text + window.crypto.subtle.exportKey('jwk', key) + .then(keyText => { + console.log(keyText); + document.getElementById('secretKey').value = keyText.k; + }); + }); }); - function generateRandomKey() { - // Generate a random key of length 32 characters (128 bits) - let characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - let randomKey = ""; - for (let i = 0; i < 32; i++) { - randomKey += characters.charAt(Math.floor(Math.random() * characters.length)); - } - return randomKey; - } - document.getElementById('downloadKeyButton').addEventListener('click', function () { // Create a Blob with the key string - let blob = new Blob(document.getElementById('secretKey').value, { + let blob = new Blob([document.getElementById('secretKey').value], { type: 'text/plain' }); @@ -122,7 +125,7 @@ let reader = new FileReader(); reader.onload = function (event) { // Set the key in the input fields - document.getElementById('secretKey').value = event.targe.result; + document.getElementById('secretKey').value = event.target.result; }; reader.readAsText(file); } @@ -138,26 +141,85 @@ // Define chunk size let chunkSize = 20 * 1024 * 1024; // 20 MB, as non-Nitro users only get 25MB each - // Encrypt the file before sending to server - let reader = new FileReader(); - reader.onload = function (event) { - // Encrypt the content using AES - let fileContent = new TextDecoder().decode(event.target.result); - let encrypted = CryptoJS.AES.encrypt(fileContent, document.getElementById('secretKey').value); - let encryptedString = encrypted.toString(); - // Create a new Blob with the encrypted content - let encryptedBlob = new Blob([encryptedString], { - type: 'application/octet-stream' + // Read the file as an ArrayBuffer + file.arrayBuffer().then(fileBuffer => { + // Convert the ArrayBuffer to a Uint8Array + let fileData = new Uint8Array(fileBuffer); + + window.crypto.subtle.importKey("jwk", { + "alg": "A256GCM", + "ext": true, + "k": document.getElementById('secretKey').value, + "key_ops": [ + "encrypt", + "decrypt" + ], + "kty": "oct" + }, "AES-GCM", true, ["encrypt", "decrypt"]).then(key => { + // Encrypt the file data using the generated key + let iv = window.crypto.getRandomValues(new Uint8Array(12)); + console.log(iv); + if (document.getElementById('secretKey').value != "") { + window.crypto.subtle.encrypt({ + name: "AES-GCM", + iv: iv + }, + key, + fileData + ).then(encryptedData => { + console.log(encryptedData) + let encryptedBlob = new Blob([[...iv].map(x => String.fromCharCode(x)).join(""), new Uint8Array(encryptedData)], { + type: 'application/octet-stream' + }); + + if (encryptedBlob.size > chunkSize) { + let parts = splitFile(encryptedBlob, chunkSize); + uploadParts(parts, file.name); + } else { + uploadFile(encryptedBlob, file.name); + } + }); + } else { + // Use unencrypted file + let encryptedData = fileData; + let encryptedBlob = new Blob([encryptedData], { + type: 'application/octet-stream' + }); + + if (encryptedBlob.size > chunkSize) { + let parts = splitFile(encryptedBlob, chunkSize); + uploadParts(parts, file.name); + } else { + uploadFile(encryptedBlob, file.name); + } + } }); - // Continue with the upload process using the encrypted file - if (encryptedBlob.size > chunkSize) { - let parts = splitFile(encryptedBlob, chunkSize); - uploadParts(parts, file.name); - } else { - uploadFile(encryptedBlob, file.name); - } - }; - reader.readAsArrayBuffer(file); + }); + + // Encrypt the file before sending to server + // let reader = new FileReader(); + // reader.onload = function (event) { + // // Encrypt the content using AES + // let fileContent = new TextDecoder().decode(event.target.result); + // if (document.getElementById('secretKey').value != "") { + // let encrypted = CryptoJS.AES.encrypt(fileContent, document.getElementById('secretKey').value); + // let encryptedString = encrypted.toString(); + // } else { + // let encryptedString = fileContent; + // } + // // Create a new Blob with the encrypted content + // let encryptedBlob = new Blob([encryptedString], { + // type: 'application/octet-stream' + // }); + // // Continue with the upload process using the encrypted file + // if (encryptedBlob.size > chunkSize) { + // let parts = splitFile(encryptedBlob, chunkSize); + // uploadParts(parts, file.name); + // } else { + // uploadFile(encryptedBlob, file.name); + // } + // }; + // reader.readAsArrayBuffer(file); } function splitFile(file, chunkSize) { @@ -207,7 +269,7 @@ .then(data => { console.log(data); document.getElementById('fileName').textContent = 'Uploaded: ' + - originalFileName; // Use the original file name + originalFileName; // Use the original file name // Handle success response, update UI or alert the user. }) .catch(error => { From 91740214ea44fd0e0dfaa84bcc19c9789ff2bbea Mon Sep 17 00:00:00 2001 From: Omay <70179858+Omay238@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:09:56 -0700 Subject: [PATCH 4/8] fix upload with no encryption --- templates/upload.html | 116 +++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 68 deletions(-) diff --git a/templates/upload.html b/templates/upload.html index 77ee146..7cd8b7d 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -86,20 +86,20 @@ document.getElementById('generateKeyButton').addEventListener('click', function () { // Generate random key let key = window.crypto.subtle.generateKey({ - name: "AES-GCM", - length: 256 - }, - true, - ["encrypt", "decrypt"] - ) - .then(key => { - // Return key as text - window.crypto.subtle.exportKey('jwk', key) - .then(keyText => { - console.log(keyText); - document.getElementById('secretKey').value = keyText.k; - }); - }); + name: "AES-GCM", + length: 256 + }, + true, + ["encrypt", "decrypt"] + ) + .then(key => { + // Return key as text + window.crypto.subtle.exportKey('jwk', key) + .then(keyText => { + console.log(keyText); + document.getElementById('secretKey').value = keyText.k; + }); + }); }); document.getElementById('downloadKeyButton').addEventListener('click', function () { @@ -146,20 +146,22 @@ // Convert the ArrayBuffer to a Uint8Array let fileData = new Uint8Array(fileBuffer); - window.crypto.subtle.importKey("jwk", { - "alg": "A256GCM", - "ext": true, - "k": document.getElementById('secretKey').value, - "key_ops": [ - "encrypt", - "decrypt" - ], - "kty": "oct" - }, "AES-GCM", true, ["encrypt", "decrypt"]).then(key => { - // Encrypt the file data using the generated key - let iv = window.crypto.getRandomValues(new Uint8Array(12)); - console.log(iv); - if (document.getElementById('secretKey').value != "") { + + if (document.getElementById('secretKey').value != "") { + + window.crypto.subtle.importKey("jwk", { + "alg": "A256GCM", + "ext": true, + "k": document.getElementById('secretKey').value, + "key_ops": [ + "encrypt", + "decrypt" + ], + "kty": "oct" + }, "AES-GCM", true, ["encrypt", "decrypt"]).then(key => { + // Encrypt the file data using the generated key + let iv = window.crypto.getRandomValues(new Uint8Array(12)); + console.log(iv); window.crypto.subtle.encrypt({ name: "AES-GCM", iv: iv @@ -168,10 +170,13 @@ fileData ).then(encryptedData => { console.log(encryptedData) - let encryptedBlob = new Blob([[...iv].map(x => String.fromCharCode(x)).join(""), new Uint8Array(encryptedData)], { + let encryptedBlob = new Blob([ + [...iv].map(x => String.fromCharCode(x)).join(""), + new Uint8Array(encryptedData) + ], { type: 'application/octet-stream' }); - + if (encryptedBlob.size > chunkSize) { let parts = splitFile(encryptedBlob, chunkSize); uploadParts(parts, file.name); @@ -179,47 +184,22 @@ uploadFile(encryptedBlob, file.name); } }); + }); + } else { + // Use unencrypted file + let encryptedData = fileData; + let encryptedBlob = new Blob([encryptedData], { + type: 'application/octet-stream' + }); + + if (encryptedBlob.size > chunkSize) { + let parts = splitFile(encryptedBlob, chunkSize); + uploadParts(parts, file.name); } else { - // Use unencrypted file - let encryptedData = fileData; - let encryptedBlob = new Blob([encryptedData], { - type: 'application/octet-stream' - }); - - if (encryptedBlob.size > chunkSize) { - let parts = splitFile(encryptedBlob, chunkSize); - uploadParts(parts, file.name); - } else { - uploadFile(encryptedBlob, file.name); - } + uploadFile(encryptedBlob, file.name); } - }); + } }); - - // Encrypt the file before sending to server - // let reader = new FileReader(); - // reader.onload = function (event) { - // // Encrypt the content using AES - // let fileContent = new TextDecoder().decode(event.target.result); - // if (document.getElementById('secretKey').value != "") { - // let encrypted = CryptoJS.AES.encrypt(fileContent, document.getElementById('secretKey').value); - // let encryptedString = encrypted.toString(); - // } else { - // let encryptedString = fileContent; - // } - // // Create a new Blob with the encrypted content - // let encryptedBlob = new Blob([encryptedString], { - // type: 'application/octet-stream' - // }); - // // Continue with the upload process using the encrypted file - // if (encryptedBlob.size > chunkSize) { - // let parts = splitFile(encryptedBlob, chunkSize); - // uploadParts(parts, file.name); - // } else { - // uploadFile(encryptedBlob, file.name); - // } - // }; - // reader.readAsArrayBuffer(file); } function splitFile(file, chunkSize) { From 8130ce31a4d26e0a2ba5602c4018ebf9db53e132 Mon Sep 17 00:00:00 2001 From: Omay <70179858+Omay238@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:13:33 -0700 Subject: [PATCH 5/8] remove unused crypto-js library, improve code formatting --- app.py | 104 +++++---- templates/download.html | 283 +++++++++++------------ templates/index.html | 66 +++--- templates/upload.html | 489 ++++++++++++++++++---------------------- 4 files changed, 436 insertions(+), 506 deletions(-) diff --git a/app.py b/app.py index c292368..34ca3f7 100644 --- a/app.py +++ b/app.py @@ -16,28 +16,30 @@ client = discord.Client(intents=intents) -@app.route('/') +@app.route("/") def index(): - return render_template('index.html') + return render_template("index.html") -@app.route('/upload') + +@app.route("/upload") def upload(): - return render_template('upload.html') + return render_template("upload.html") + -@app.route('/download') +@app.route("/download") def download(): # Read the existing data from db.json try: - with open('db.json', 'r') as db_file: + with open("db.json", "r") as db_file: db_data = json.load(db_file) - files = db_data.get('files', {}) + files = db_data.get("files", {}) except (FileNotFoundError, json.JSONDecodeError): files = {} # Convert the files dictionary to a list including the number of parts files_list = [name for name, info in files.items()] - return render_template('download.html', files=files_list) + return render_template("download.html", files=files_list) def start_discord_bot(): @@ -46,61 +48,68 @@ def start_discord_bot(): @client.event async def on_ready(): - print('Discord client is ready.') + print("Discord client is ready.") - client.run(os.environ['DISCORD_TOKEN']) + client.run(os.environ["DISCORD_TOKEN"]) # Run Discord client in a background thread threading.Thread(target=start_discord_bot, daemon=True).start() -@app.route('/uploadFile', methods=['POST']) +@app.route("/uploadFile", methods=["POST"]) def upload_file(): - file = request.files['file'] + file = request.files["file"] filename = file.filename # Ensure the Discord client is ready before attempting to send if not client.is_closed(): - future = asyncio.run_coroutine_threadsafe(send_file_to_discord(file), client.loop) - result = future.result() # This waits for the coroutine to finish and returns its result + future = asyncio.run_coroutine_threadsafe( + send_file_to_discord(file), client.loop + ) + result = ( + future.result() + ) # This waits for the coroutine to finish and returns its result print(result) # Read the existing data from db.json try: - with open('db.json', 'r') as db_file: + with open("db.json", "r") as db_file: db_data = json.load(db_file) except (FileNotFoundError, json.JSONDecodeError): - db_data = {'files': {}} + db_data = {"files": {}} # Increment the parts count for the uploaded file - if filename in db_data['files']: - db_data['files'][filename.split(".part")[0]]['parts'] += 1 + if filename in db_data["files"]: + db_data["files"][filename.split(".part")[0]]["parts"] += 1 else: - db_data['files'][filename.split(".part")[0]] = {'parts': 1} + db_data["files"][filename.split(".part")[0]] = {"parts": 1} # Write the updated data back to db.json - with open('db.json', 'w') as db_file: + with open("db.json", "w") as db_file: json.dump(db_data, db_file, indent=4) - return jsonify({'message': 'File uploaded successfully!'}) + return jsonify({"message": "File uploaded successfully!"}) + should_remove_files = False + @app.after_request def remove_files(response): global should_remove_files if should_remove_files: - for file in os.listdir('partstostitch'): - os.remove(os.path.join('partstostitch', file)) + for file in os.listdir("partstostitch"): + os.remove(os.path.join("partstostitch", file)) should_remove_files = False return response -@app.route('/downloadFile/') + +@app.route("/downloadFile/") def download_file(filename): global should_remove_files # Ensure the directory exists - os.makedirs('partstostitch', exist_ok=True) + os.makedirs("partstostitch", exist_ok=True) # Ensure the Discord client is ready before attempting to fetch messages if not client.is_closed(): @@ -108,30 +117,43 @@ def download_file(filename): future = asyncio.run_coroutine_threadsafe( fetch_and_save_files(filename), client.loop ) - result = future.result() # This waits for the coroutine to finish and returns its result + result = ( + future.result() + ) # This waits for the coroutine to finish and returns its result if result > 1: # Check if multiple files were downloaded # Merge the files - with open(f'partstostitch/{filename}', 'wb') as merged_file: + with open(f"partstostitch/{filename}", "wb") as merged_file: for part_num in range(1, result + 1): - part_filename = f'{filename}.part{part_num}' - part_path = os.path.join('partstostitch', part_filename) - with open(part_path, 'rb') as part_file: + part_filename = f"{filename}.part{part_num}" + part_path = os.path.join("partstostitch", part_filename) + with open(part_path, "rb") as part_file: merged_file.write(part_file.read()) os.remove(part_path) # Remove the part file after merging should_remove_files = True - return send_from_directory('partstostitch', filename, as_attachment=True, mimetype='application/octet-stream') + return send_from_directory( + "partstostitch", + filename, + as_attachment=True, + mimetype="application/octet-stream", + ) elif result == 1: # Only one file, no need to merge should_remove_files = True - return send_from_directory('partstostitch', filename, as_attachment=True, mimetype='application/octet-stream') + return send_from_directory( + "partstostitch", + filename, + as_attachment=True, + mimetype="application/octet-stream", + ) else: # No files were downloaded - return jsonify({'error': 'No files were found to download'}), 404 + return jsonify({"error": "No files were found to download"}), 404 else: - return jsonify({'error': 'Discord client is not ready'}), 503 + return jsonify({"error": "Discord client is not ready"}), 503 + async def fetch_and_save_files(filename): - channel = client.get_channel(int(os.environ['DISCORD_CHANNEL'])) + channel = client.get_channel(int(os.environ["DISCORD_CHANNEL"])) messages = [message async for message in channel.history()] count = 0 @@ -139,8 +161,8 @@ async def fetch_and_save_files(filename): for attachment in message.attachments: if attachment.filename.startswith(filename): file_bytes = await attachment.read() - file_path = os.path.join('partstostitch', attachment.filename) - with open(file_path, 'wb') as file: + file_path = os.path.join("partstostitch", attachment.filename) + with open(file_path, "wb") as file: file.write(file_bytes) count += 1 @@ -149,11 +171,13 @@ async def fetch_and_save_files(filename): async def send_file_to_discord(file): print("Sending file to Discord:", file.filename) - channel = client.get_channel(int(os.environ['DISCORD_CHANNEL'])) + channel = client.get_channel(int(os.environ["DISCORD_CHANNEL"])) file_bytes = file.read() - message = await channel.send(file=discord.File(io.BytesIO(file_bytes), filename=file.filename)) + message = await channel.send( + file=discord.File(io.BytesIO(file_bytes), filename=file.filename) + ) print(message) -if __name__ == '__main__': +if __name__ == "__main__": app.run(debug=True) diff --git a/templates/download.html b/templates/download.html index 7338237..c55bbec 100644 --- a/templates/download.html +++ b/templates/download.html @@ -1,159 +1,130 @@ - - - Download Files - - - - - - - - - - -
-
-
-
- -
- -
- {% for file in files %} -

- {{ file }} -

-
- {% endfor %} -
-
- - - - - + + Download Files + + + + + + + + + +
+
+
+
+ +
+ +
{% for file in files %}

+ {{ file }} +

+
{% endfor %} +
+
+ + + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index c9c8740..cae1c24 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,40 +1,32 @@ - - - Google Drive Replacement Bot - - - - - - - - - - - - + + Google Drive Replacement Bot + + + + + + + + + +
+
+ +
+
+ \ No newline at end of file diff --git a/templates/upload.html b/templates/upload.html index 7cd8b7d..f017d4b 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -1,276 +1,219 @@ - - - Upload Files - - - - - - - - - - -
-
-
-
- -
- - - -
- Drop file here or click to upload - -
- -
-
-
- - - - - - - + + Upload Files + + + + + + + + + +
+
+
+
+ +
+ + + +
Drop file here or click to upload +
+ +
+
+
+ + \ No newline at end of file From 593e7c53b39d1b4d7f22b25f403263d8a4dace41 Mon Sep 17 00:00:00 2001 From: Omay <70179858+Omay238@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:16:55 -0700 Subject: [PATCH 6/8] maybe using blob.arrayBuffer instead of blob.text will work --- templates/download.html | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/templates/download.html b/templates/download.html index c55bbec..20f54ea 100644 --- a/templates/download.html +++ b/templates/download.html @@ -72,43 +72,9 @@ } function downloadFile(filename) { - fetch('/downloadFile/' + encodeURIComponent(filename)).then(response => response.blob()).then(blob => blob.text()).then(data => { + fetch('/downloadFile/' + encodeURIComponent(filename)).then(response => response.blob()).then(blob => blob.arrayBuffer()).then(data => { if (document.getElementById('secretKey').value != "") { - let keyJson = { - "alg": "A256GCM", - "ext": true, - "k": document.getElementById('secretKey').value, - "key_ops": ["encrypt", "decrypt"], - "kty": "oct" - } - crypto.subtle.importKey("jwk", keyJson, { - name: "AES-GCM", - }, true, - ["encrypt", "decrypt"]).then(key => { - let iv = data.slice(0, 12); - let ivBuffer = new Uint8Array(iv.split('').map(byte => byte.charCodeAt(0))).buffer; - let encryptedData = data.slice(12); - let encryptedDataBuffer = new Uint8Array(encryptedData.split('').map(x => x.charCodeAt(0))).buffer; - window.crypto.subtle.decrypt({ - name: "AES-GCM", - iv: ivBuffer - }, key, encryptedDataBuffer).then(decryptedData => { - let blob2 = new Blob([decryptedData], { - type: 'application/octet-stream' - }); // Create a Blob from the decrypted data - // Create a download link and click it to start the download - let a = document.createElement('a'); - a.href = window.URL.createObjectURL(blob2); - a.download = filename; - a.click(); - }).catch(error => { - console.error('Error:', error); - // Handle errors here, such as displaying a notification to the user - }); - }).catch(error => { - console.error('Error:', error); - // Handle errors here, such as displaying a notification to the user - }); + // todo: make it work } else { let decryptedData = data; let blob2 = new Blob([decryptedData], { From bc1aae9927d4de5978640bfd73a379dc36140d04 Mon Sep 17 00:00:00 2001 From: Omay <70179858+Omay238@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:32:41 -0700 Subject: [PATCH 7/8] improve error messages --- templates/download.html | 33 +++++++++++++++++++++++++++++---- templates/upload.html | 10 ++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/templates/download.html b/templates/download.html index 20f54ea..84eb900 100644 --- a/templates/download.html +++ b/templates/download.html @@ -74,7 +74,33 @@ function downloadFile(filename) { fetch('/downloadFile/' + encodeURIComponent(filename)).then(response => response.blob()).then(blob => blob.arrayBuffer()).then(data => { if (document.getElementById('secretKey').value != "") { - // todo: make it work + let iv = new Uint8Array(data.slice(0, 12)); + let realData = data.slice(12); + window.crypto.subtle.importKey("jwk", { + "alg": "A256GCM", + "ext": true, + "k": document.getElementById('secretKey').value, + "key_ops": ["encrypt", "decrypt"], + "kty": "oct" + }, "AES-GCM", true, ["encrypt", "decrypt"]).then(key => { + window.crypto.subtle.decrypt({ + name: "AES-GCM", + iv: iv + }, key, realData).then(decryptedData => { + let blob2 = new Blob([decryptedData], { + type: 'application/octet-stream' + }); // Create a Blob from the decrypted data + // Create a download link and click it to start the download + let a = document.createElement('a'); + a.href = window.URL.createObjectURL(blob2); + a.download = filename; + a.click(); + }).catch(error => { + console.error('Error during decryption:', error); + }); + }).catch(error => { + console.error('Error during key import:', error); + }); } else { let decryptedData = data; let blob2 = new Blob([decryptedData], { @@ -87,9 +113,8 @@ a.click(); } }).catch(error => { - console.error('Error:', error); - // Handle errors here, such as displaying a notification to the user - }); + console.error('Error during file download:', error); + }); } diff --git a/templates/upload.html b/templates/upload.html index f017d4b..74f6aa4 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -75,7 +75,11 @@ window.crypto.subtle.exportKey('jwk', key).then(keyText => { console.log(keyText); document.getElementById('secretKey').value = keyText.k; + }).catch(error => { + console.error('Error:', error); }); + }).catch(error => { + console.error('Error:', error); }); }); document.getElementById('downloadKeyButton').addEventListener('click', function () { @@ -143,7 +147,11 @@ } else { uploadFile(encryptedBlob, file.name); } + }).catch(error => { + console.error('Error during encryption:', error); }); + }).catch(error => { + console.error('Error during key import:', error); }); } else { // Use unencrypted file @@ -158,6 +166,8 @@ uploadFile(encryptedBlob, file.name); } } + }).catch(error => { + console.error('Error during file import:', error); }); } From 6862df00ddc4f5c2ec5e3ed2a0adaa48e053e082 Mon Sep 17 00:00:00 2001 From: Omay <70179858+Omay238@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:40:25 -0700 Subject: [PATCH 8/8] i'm honestly not sure what exactly i've changed since it wasn't working before, but it works --- templates/upload.html | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/templates/upload.html b/templates/upload.html index 74f6aa4..8163fc8 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -73,7 +73,6 @@ ["encrypt", "decrypt"]).then(key => { // Return key as text window.crypto.subtle.exportKey('jwk', key).then(keyText => { - console.log(keyText); document.getElementById('secretKey').value = keyText.k; }).catch(error => { console.error('Error:', error); @@ -129,15 +128,13 @@ }, "AES-GCM", true, ["encrypt", "decrypt"]).then(key => { // Encrypt the file data using the generated key let iv = window.crypto.getRandomValues(new Uint8Array(12)); - console.log(iv); window.crypto.subtle.encrypt({ name: "AES-GCM", iv: iv }, key, fileData).then(encryptedData => { - console.log(encryptedData) let encryptedBlob = new Blob([ - [...iv].map(x => String.fromCharCode(x)).join(""), - new Uint8Array(encryptedData) + iv, + encryptedData ], { type: 'application/octet-stream' }); @@ -208,7 +205,6 @@ method: 'POST', body: formData, }).then(response => response.json()).then(data => { - console.log(data); document.getElementById('fileName').textContent = 'Uploaded: ' + originalFileName; // Use the original file name // Handle success response, update UI or alert the user. }).catch(error => {