From adc69d499a20f96f647c2018c8e867bd65749083 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Fri, 23 Jan 2026 21:56:39 -0600 Subject: [PATCH] privacy screen --- .github/patches/privacyScreen.py | 32 ++++++++++ .github/workflows/generator-linux.yml | 4 +- .github/workflows/generator-macos.yml | 4 +- .../third-party-RustDeskTempTopMostWindow.yml | 60 +++++++++++++++++++ rdgenerator/forms.py | 2 + rdgenerator/templates/generator.html | 22 +++++++ rdgenerator/views.py | 13 ++++ 7 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 .github/patches/privacyScreen.py diff --git a/.github/patches/privacyScreen.py b/.github/patches/privacyScreen.py new file mode 100644 index 0000000..5197c4e --- /dev/null +++ b/.github/patches/privacyScreen.py @@ -0,0 +1,32 @@ +import os + +def convert_png_to_cpp(input_file, output_file, array_name="g_img"): + if not os.path.exists(input_file): + print(f"Error: {input_file} not found.") + return + + with open(input_file, "rb") as f: + data = f.read() + + with open(output_file, "w") as f: + f.write('#include "pch.h"\n') + f.write('#include "./img.h"\n\n') + f.write(f"const unsigned char {array_name}[] = {{\n") + + for i in range(0, len(data), 20): + chunk = data[i : i + 20] + hex_chunk = [f"0x{b:02x}" for b in chunk] + + line = ", ".join(hex_chunk) + + if i + 20 < len(data): + f.write(f"{line},\n") + else: + f.write(f"{line}\n") + + f.write("};\n\n") + f.write(f"const long long {array_name}Len = sizeof({array_name});\n") + + #print(f"Successfully converted {input_file} to {output_file}") + +convert_png_to_cpp("privacy.png", "img.cpp") \ No newline at end of file diff --git a/.github/workflows/generator-linux.yml b/.github/workflows/generator-linux.yml index bead508..3998c90 100644 --- a/.github/workflows/generator-linux.yml +++ b/.github/workflows/generator-linux.yml @@ -301,8 +301,8 @@ jobs: run: | sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs - wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.diff - git apply allowCustom.diff + wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py + python allowCustom.py wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff git apply removeSetupServerTip.diff echo -n "${{ env.custom }}" | cat > ./custom_.txt diff --git a/.github/workflows/generator-macos.yml b/.github/workflows/generator-macos.yml index d481ace..07a4c2a 100644 --- a/.github/workflows/generator-macos.yml +++ b/.github/workflows/generator-macos.yml @@ -237,8 +237,8 @@ jobs: sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs - wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.diff - git apply allowCustom.diff + wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py + python allowCustom.py wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff git apply removeSetupServerTip.diff diff --git a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml index 2f89092..5af0879 100644 --- a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml +++ b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml @@ -45,10 +45,70 @@ jobs: run: | git clone https://github.com/rustdesk-org/RustDeskTempTopMostWindow RustDeskTempTopMostWindow + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + import time + + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + + - name: Finalize and Cleanup zip/json + if: always() # Run even if previous steps fail + continue-on-error: true + uses: fjogeleit/http-request-action@v1 + with: + url: "${{ secrets.GENURL }}/cleanzip" + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}"}' + # Build. commit 53b548a5398624f7149a382000397993542ad796 is tag v0.3 - name: Build the project run: | cd RustDeskTempTopMostWindow && git checkout 53b548a5398624f7149a382000397993542ad796 + if [[ "${{ env.logolink_url }}" != "false" ]]; then + wget -O ./privacy.png ${{ env.privacylink_url }}/get_png?filename=${{ env.privacylink_file }}"&"uuid=${{ env.privacylink_uuid }} + wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/privacyScreen.py + python privacyScreen.py + rm ./WindowInjection/img.cpp + mv img.cpp ./WindowInjection/img.cpp + fi msbuild ${{ env.project_path }} -p:Configuration=${{ inputs.configuration }} -p:Platform=${{ inputs.platform }} /p:TargetVersion=${{ inputs.target_version }} - name: Archive build artifacts diff --git a/rdgenerator/forms.py b/rdgenerator/forms.py index db5cb29..7ae27d0 100644 --- a/rdgenerator/forms.py +++ b/rdgenerator/forms.py @@ -37,8 +37,10 @@ class GenerateForm(forms.Form): #Visual iconfile = forms.FileField(label="Custom App Icon (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'})) logofile = forms.FileField(label="Custom App Logo (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'})) + privacyfile = forms.FileField(label="Custom privacy screen (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'})) iconbase64 = forms.CharField(required=False) logobase64 = forms.CharField(required=False) + privacybase64 = forms.CharField(required=False) theme = forms.ChoiceField(choices=[ ('light', 'Light'), ('dark', 'Dark'), diff --git a/rdgenerator/templates/generator.html b/rdgenerator/templates/generator.html index 4fb0d46..babfba3 100644 --- a/rdgenerator/templates/generator.html +++ b/rdgenerator/templates/generator.html @@ -330,6 +330,7 @@

Visual

{{ form.iconbase64.as_hidden }} {{ form.logobase64.as_hidden }} + {{ form.privacybase64.as_hidden }} {{ form.iconfile }}

@@ -338,6 +339,10 @@ {{ form.logofile }}



+ + {{ form.privacyfile }}

+ +


{{ form.theme }} {{ form.themeDorO }} *Default sets the theme but allows the client to change it, Override sets the theme permanently.

@@ -415,6 +420,9 @@ document.getElementById("{{ form.logofile.id_for_label }}").addEventListener('change', function(event) { previewImage(event.target, 'logo-preview'); }); + document.getElementById("{{ form.privacyfile.id_for_label }}").addEventListener('change', function(event) { + previewImage(event.target, 'privacy-preview'); + }); document.getElementById("{{ form.hidecm.id_for_label }}").addEventListener('change',function() { if (this.checked) { @@ -594,6 +602,16 @@ } } + const privacyPreview = document.getElementById('privacy-preview'); + if (privacyPreview.firstChild && logoPrprivacyPrevieweview.firstChild.src.startsWith('data:image/png;base64')) { + formData.privacyfile = privacyPreview.firstChild.src; // Use existing base64 + } else { //if it's a file upload + const privacyFile = document.getElementById("{{ form.privacyfile.id_for_label }}").files[0]; + if (privacyFile) { + formData.privacyfile = await readFileAsBase64(privacyFile); + } + } + const jsonData = JSON.stringify(formData, null, 2); const blob = new Blob([jsonData], { type: "application/json" }); const url = URL.createObjectURL(blob); @@ -655,6 +673,10 @@ document.getElementById('id_logobase64').value = formData[key]; document.getElementById('logo-preview').innerHTML = ``; } + if (key === 'privacyfile' && formData[key]) { + document.getElementById('id_privacybase64').value = formData[key]; + document.getElementById('privacy-preview').innerHTML = ``; + } }); } } diff --git a/rdgenerator/views.py b/rdgenerator/views.py index 7c535f8..65d8e39 100644 --- a/rdgenerator/views.py +++ b/rdgenerator/views.py @@ -115,6 +115,16 @@ def generator_view(request): logolink_url = "false" logolink_uuid = "false" logolink_file = "false" + try: + privacyfile = form.cleaned_data.get('privacyfile') + if not privacyfile: + privacyfile = form.cleaned_data.get('privacybase64') + privacylink_url, privacylink_uuid, privacylink_file = save_png(privacyfile,myuuid,full_url,"privacy.png") + except: + print("failed to get logo") + privacylink_url = "false" + privacylink_uuid = "false" + privacylink_file = "false" ###create the custom.txt json here and send in as inputs below decodedCustom = {} @@ -239,6 +249,9 @@ def generator_view(request): "logolink_url":logolink_url, "logolink_uuid":logolink_uuid, "logolink_file":logolink_file, + "privacylink_url":privacylink_url, + "privacylink_uuid":privacylink_uuid, + "privacylink_file":privacylink_file, "appname":appname, "genurl":_settings.GENURL, "urlLink":urlLink,