mirror of
https://github.com/element-hq/element-desktop.git
synced 2026-02-18 14:47:24 -05:00
Compare commits
105 Commits
v1.11.101
...
dbkr/esign
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4cac461c2 | ||
|
|
3803e407ef | ||
|
|
e8a88818b7 | ||
|
|
8bc8b01eb6 | ||
|
|
38d815bf01 | ||
|
|
48a138102b | ||
|
|
93a72b3d72 | ||
|
|
343b7d75d0 | ||
|
|
804933023a | ||
|
|
5149911e38 | ||
|
|
c9a53ba25d | ||
|
|
abf15ef471 | ||
|
|
4d0add6309 | ||
|
|
74f2ecb4a9 | ||
|
|
8ce5cf0f10 | ||
|
|
b92d31e7a4 | ||
|
|
fd29e65112 | ||
|
|
75f93ab631 | ||
|
|
4b56f3b1ca | ||
|
|
a1b1245c77 | ||
|
|
053bcd10e1 | ||
|
|
91036dcd22 | ||
|
|
c0f3703c5a | ||
|
|
960a413c8a | ||
|
|
5e8373d6cd | ||
|
|
858e4fdbde | ||
|
|
8e5761be2d | ||
|
|
be6565656d | ||
|
|
02f2274765 | ||
|
|
0d04e3d2ac | ||
|
|
f4b3816888 | ||
|
|
db153374f0 | ||
|
|
0d4f02cde6 | ||
|
|
0061966718 | ||
|
|
9fc64550dd | ||
|
|
51d2f6a29e | ||
|
|
58ef3d277f | ||
|
|
fd62231856 | ||
|
|
0510fa4ee4 | ||
|
|
58d129f565 | ||
|
|
c72c18e998 | ||
|
|
906125c738 | ||
|
|
bf97ef4904 | ||
|
|
273afae84e | ||
|
|
e18ddee37e | ||
|
|
c43e8d684f | ||
|
|
75463f0296 | ||
|
|
2401c1f7ef | ||
|
|
fe7d10539b | ||
|
|
cb5f5bc94a | ||
|
|
b123e0f60c | ||
|
|
39f460b636 | ||
|
|
86f6136257 | ||
|
|
2e039f4bab | ||
|
|
8f24f45090 | ||
|
|
6901bff548 | ||
|
|
cf88e520a0 | ||
|
|
3bc59fb6ce | ||
|
|
f136c7aaad | ||
|
|
5b33e2866d | ||
|
|
c0ca58e930 | ||
|
|
77038a5a86 | ||
|
|
b22d2480ae | ||
|
|
7142b6fe57 | ||
|
|
b4c2bc7165 | ||
|
|
4ea7b679e9 | ||
|
|
89ead2e56f | ||
|
|
360665cd41 | ||
|
|
93c0b81cc4 | ||
|
|
bd194306dd | ||
|
|
b4a23ff505 | ||
|
|
8559a740f4 | ||
|
|
dea64f7e38 | ||
|
|
044de246a1 | ||
|
|
2465607ac5 | ||
|
|
d9ad2faac6 | ||
|
|
0c4c950fc7 | ||
|
|
9660c4b2be | ||
|
|
73ddf2a19b | ||
|
|
cfea34766a | ||
|
|
77bea6db7e | ||
|
|
abd508fc0d | ||
|
|
8e4826b4e9 | ||
|
|
3cd88352b6 | ||
|
|
1d79fa633a | ||
|
|
ec4c610158 | ||
|
|
468d2249d1 | ||
|
|
63e1e0d894 | ||
|
|
e326246669 | ||
|
|
53672fadd7 | ||
|
|
20f28abb47 | ||
|
|
a0db96d50d | ||
|
|
2bb1544064 | ||
|
|
28e558162a | ||
|
|
85556ecd74 | ||
|
|
46080c66d0 | ||
|
|
80b88583b8 | ||
|
|
2aaf42b8e8 | ||
|
|
660ccd414a | ||
|
|
32cf6b2ebf | ||
|
|
5507f2859f | ||
|
|
689179c5ae | ||
|
|
3116f596f2 | ||
|
|
69cfe0bda6 | ||
|
|
194798497f |
10
.github/workflows/build_and_deploy.yaml
vendored
10
.github/workflows/build_and_deploy.yaml
vendored
@@ -107,7 +107,7 @@ jobs:
|
||||
environment: ${{ needs.prepare.outputs.deploy == 'true' && 'packages.element.io' || '' }}
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
|
||||
- name: Prepare artifacts for deployment
|
||||
run: |
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
|
||||
- name: Stash packages.element.io
|
||||
if: needs.prepare.outputs.deploy == 'false'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: packages.element.io
|
||||
path: packages.element.io
|
||||
@@ -235,7 +235,7 @@ jobs:
|
||||
|
||||
- name: Stash debs
|
||||
if: needs.prepare.outputs.deploy == 'false' && needs.linux.result == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: debs
|
||||
path: |
|
||||
@@ -274,14 +274,14 @@ jobs:
|
||||
id-token: write # This is required for requesting the JWT
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::264135176173:role/Push-ElementDesktop-MSI
|
||||
role-session-name: githubaction-run-${{ github.run_id }}
|
||||
aws-region: ${{ env.AWS_REGION }}
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
pattern: win-*
|
||||
|
||||
|
||||
9
.github/workflows/build_and_test.yaml
vendored
9
.github/workflows/build_and_test.yaml
vendored
@@ -15,6 +15,7 @@ jobs:
|
||||
with:
|
||||
config: ${{ (github.event.pull_request.base.ref || github.ref_name) == 'develop' && 'element.io/nightly' || 'element.io/release' }}
|
||||
version: ${{ (github.event.pull_request.base.ref || github.ref_name) == 'develop' && 'develop' || '' }}
|
||||
branch-matching: true
|
||||
|
||||
windows:
|
||||
needs: fetch
|
||||
@@ -52,9 +53,9 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
if: always()
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -63,7 +64,7 @@ jobs:
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
pattern: blob-report-*
|
||||
path: all-blob-reports
|
||||
@@ -74,7 +75,7 @@ jobs:
|
||||
|
||||
- name: Upload HTML report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: html-report
|
||||
path: playwright-report
|
||||
|
||||
12
.github/workflows/build_linux.yaml
vendored
12
.github/workflows/build_linux.yaml
vendored
@@ -60,21 +60,21 @@ jobs:
|
||||
}
|
||||
}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: webapp
|
||||
|
||||
- name: Cache .hak
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
||||
with:
|
||||
key: ${{ runner.os }}-${{ github.ref_name }}-${{ inputs.sqlcipher }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion', 'dockerbuild/*') }}
|
||||
path: |
|
||||
./.hak
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: "yarn"
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
|
||||
# This allows contributors to test changes to the dockerbuild image within a pull request
|
||||
- name: Build docker image
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||
if: steps.changed_files.outputs.any_modified == 'true'
|
||||
with:
|
||||
file: dockerbuild/Dockerfile
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
|
||||
# We exclude *-unpacked as it loses permissions and the tarball contains it with correct permissions
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
|
||||
path: |
|
||||
|
||||
12
.github/workflows/build_macos.yaml
vendored
12
.github/workflows/build_macos.yaml
vendored
@@ -37,15 +37,15 @@ jobs:
|
||||
runs-on: macos-14 # M1
|
||||
environment: ${{ inputs.sign && 'packages.element.io' || '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: webapp
|
||||
|
||||
- name: Cache .hak
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
||||
with:
|
||||
key: ${{ runner.os }}-${{ hashFiles('hakHash', 'electronVersion') }}
|
||||
path: |
|
||||
@@ -60,11 +60,11 @@ jobs:
|
||||
rustup target add x86_64-apple-darwin
|
||||
|
||||
# M1 macos-14 comes without Python preinstalled
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: "yarn"
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
|
||||
# We exclude mac-universal as the unpacked app takes forever to upload and zip and dmg already contains it
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: macos
|
||||
path: |
|
||||
|
||||
31
.github/workflows/build_prepare.yaml
vendored
31
.github/workflows/build_prepare.yaml
vendored
@@ -20,6 +20,11 @@ on:
|
||||
required: false
|
||||
default: false
|
||||
description: "Whether the build should be deployed to production"
|
||||
branch-matching:
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
description: "Whether the branch name should be matched to find the element-web commit"
|
||||
secrets:
|
||||
# Required if `nightly` is set
|
||||
CF_R2_ACCESS_KEY_ID:
|
||||
@@ -49,9 +54,9 @@ jobs:
|
||||
outputs:
|
||||
nightly-version: ${{ steps.versions.outputs.nightly }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: "yarn"
|
||||
@@ -59,7 +64,25 @@ jobs:
|
||||
- name: Install Deps
|
||||
run: "yarn install --frozen-lockfile"
|
||||
|
||||
- name: Fetch Element Web
|
||||
- name: Fetch Element Web (matching branch)
|
||||
id: branch-matching
|
||||
if: inputs.branch-matching
|
||||
continue-on-error: true
|
||||
run: |
|
||||
scripts/branch-match.sh
|
||||
cp "$CONFIG_DIR/config.json" element-web/
|
||||
yarn --cwd element-web install --frozen-lockfile
|
||||
yarn --cwd element-web run build
|
||||
mv element-web/webapp .
|
||||
yarn asar-webapp
|
||||
env:
|
||||
# These must be set for branch-match.sh to get the right branch
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
CONFIG_DIR: ${{ inputs.config }}
|
||||
|
||||
- name: Fetch Element Web (${{ inputs.version }})
|
||||
if: steps.branch-matching.outcome == 'failure' || steps.branch-matching.outcome == 'skipped'
|
||||
run: yarn run fetch --noverify -d ${{ inputs.config }} ${{ inputs.version }}
|
||||
|
||||
# We split this out to save the build_* scripts having to do it to make use of `hashFiles` in the cache action
|
||||
@@ -137,7 +160,7 @@ jobs:
|
||||
echo "| Element Web | [$WEB_VERSION](https://github.com/element-hq/element-web/commit/$WEB_VERSION) |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| JS SDK | [$JS_VERSION](https://github.com/matrix-org/matrix-js-sdk/commit/$JS_VERSION) |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: webapp
|
||||
retention-days: 1
|
||||
|
||||
10
.github/workflows/build_test.yaml
vendored
10
.github/workflows/build_test.yaml
vendored
@@ -27,9 +27,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: "yarn"
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
- name: Install Deps
|
||||
run: "yarn install --frozen-lockfile"
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: ${{ inputs.artifact }}
|
||||
path: dist
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
|
||||
- name: Upload blob report
|
||||
if: always() && inputs.blob_report
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: blob-report-${{ inputs.artifact }}
|
||||
path: blob-report
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
|
||||
- name: Upload HTML report
|
||||
if: always() && inputs.blob_report == false
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: ${{ inputs.artifact }}-test
|
||||
path: playwright-report
|
||||
|
||||
18
.github/workflows/build_windows.yaml
vendored
18
.github/workflows/build_windows.yaml
vendored
@@ -65,15 +65,15 @@ jobs:
|
||||
}
|
||||
}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: webapp
|
||||
|
||||
- name: Cache .hak
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
|
||||
with:
|
||||
key: ${{ runner.os }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion') }}
|
||||
path: |
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
rustup default stable
|
||||
rustup target add ${{ steps.config.outputs.target }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: "yarn"
|
||||
@@ -146,10 +146,8 @@ jobs:
|
||||
run: |
|
||||
Set-StrictMode -Version 'Latest'
|
||||
|
||||
# Download, extract, and rename
|
||||
Invoke-WebRequest -OutFile eSigner_CKA.zip "$env:ESIGNER_URL"
|
||||
Expand-Archive -Path eSigner_CKA.zip -DestinationPath .
|
||||
Get-ChildItem -Path * -Include "*_build_*.exe" | Rename-Item -NewName eSigner_CKA.exe
|
||||
# Download installer
|
||||
Invoke-WebRequest -OutFile eSigner_CKA.exe "$env:ESIGNER_URL"
|
||||
|
||||
# Install
|
||||
New-Item -ItemType Directory -Force -Path "$env:INSTALL_DIR"
|
||||
@@ -181,7 +179,7 @@ jobs:
|
||||
echo "ED_SIGNTOOL_THUMBPRINT=$Thumbprint" >> $env:GITHUB_ENV
|
||||
echo "ED_SIGNTOOL_SUBJECT_NAME=$SubjectName" >> $env:GITHUB_ENV
|
||||
env:
|
||||
ESIGNER_URL: https://github.com/SSLcom/eSignerCKA/releases/download/v1.0.6/SSL.COM-eSigner-CKA_1.0.6.zip
|
||||
ESIGNER_URL: https://app.esigner.com/documents/1589b50d-2e30-452f-a40c-3b81661f95b4/final
|
||||
INSTALL_DIR: C:\Users\runneradmin\eSignerCKA
|
||||
MASTER_KEY_FILE: C:\Users\runneradmin\eSignerCKA\master.key
|
||||
|
||||
@@ -206,7 +204,7 @@ jobs:
|
||||
| ForEach-Object -Process {. $env:SIGNTOOL_PATH verify /pa $_.FullName; if(!$?) { throw }}
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: win-${{ inputs.arch }}
|
||||
path: |
|
||||
|
||||
10
.github/workflows/dockerbuild.yaml
vendored
10
.github/workflows/dockerbuild.yaml
vendored
@@ -19,18 +19,18 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
|
||||
with:
|
||||
install: true
|
||||
|
||||
- name: Build test image
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||
with:
|
||||
file: dockerbuild/Dockerfile
|
||||
push: false
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
run: docker run -v $PWD:/project element-desktop-dockerbuild yarn install
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@6d4b68b490aef8836e8fb5e50ee7b3bdfa5894f0
|
||||
uses: docker/login-action@3d100841f68d4548bf57e52eb27bd33ec5069f55
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
|
||||
- name: Build and push Docker image
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||
with:
|
||||
file: dockerbuild/Dockerfile
|
||||
push: true
|
||||
|
||||
16
.github/workflows/static_analysis.yaml
vendored
16
.github/workflows/static_analysis.yaml
vendored
@@ -9,9 +9,9 @@ jobs:
|
||||
name: "Typescript Syntax Check"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: package.json
|
||||
cache: "yarn"
|
||||
@@ -35,9 +35,9 @@ jobs:
|
||||
name: "ESLint"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: package.json
|
||||
cache: "yarn"
|
||||
@@ -53,9 +53,9 @@ jobs:
|
||||
name: "Workflow Lint"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: package.json
|
||||
cache: "yarn"
|
||||
@@ -71,9 +71,9 @@ jobs:
|
||||
name: "Analyse Dead Code"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: package.json
|
||||
cache: "yarn"
|
||||
|
||||
2
.github/workflows/triage-stale.yml
vendored
2
.github/workflows/triage-stale.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9
|
||||
with:
|
||||
operations-per-run: 250
|
||||
days-before-issue-stale: -1
|
||||
|
||||
@@ -1 +1 @@
|
||||
v22.15.0
|
||||
v22.16.0
|
||||
|
||||
93
CHANGELOG.md
93
CHANGELOG.md
@@ -1,3 +1,96 @@
|
||||
Changes in [1.11.105](https://github.com/element-hq/element-desktop/releases/tag/v1.11.105) (2025-07-01)
|
||||
========================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
* Add support for migrating to kwallet6 ([#2390](https://github.com/element-hq/element-desktop/pull/2390)). Contributed by @t3chguy.
|
||||
* New room list: add context menu to room list item ([#29952](https://github.com/element-hq/element-web/pull/29952)). Contributed by @florianduros.
|
||||
* Support for custom message components via Module API ([#30074](https://github.com/element-hq/element-web/pull/30074)). Contributed by @Half-Shot.
|
||||
* Prompt users to set up recovery ([#30075](https://github.com/element-hq/element-web/pull/30075)). Contributed by @uhoreg.
|
||||
* Update `IconButton` colors ([#30124](https://github.com/element-hq/element-web/pull/30124)). Contributed by @florianduros.
|
||||
* New room list: filter list can be collapsed ([#29992](https://github.com/element-hq/element-web/pull/29992)). Contributed by @florianduros.
|
||||
* Show `EmptyRoomListView` when low priority filter matches zero rooms ([#30122](https://github.com/element-hq/element-web/pull/30122)). Contributed by @MidhunSureshR.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Fix element-desktop-ssoid profile deeplinking for OIDC ([#2396](https://github.com/element-hq/element-desktop/pull/2396)). Contributed by @t3chguy.
|
||||
* Add support for migrating to kwallet6 ([#2390](https://github.com/element-hq/element-desktop/pull/2390)). Contributed by @t3chguy.
|
||||
* Fix untranslatable string "People" in notifications beta ([#30165](https://github.com/element-hq/element-web/pull/30165)). Contributed by @t3chguy.
|
||||
* Force verification even after logging in via delegate ([#30141](https://github.com/element-hq/element-web/pull/30141)). Contributed by @andybalaam.
|
||||
* Hide add integrations button based on UIComponent.AddIntegrations ([#30140](https://github.com/element-hq/element-web/pull/30140)). Contributed by @t3chguy.
|
||||
* Use nav for new room list and label sections ([#30134](https://github.com/element-hq/element-web/pull/30134)). Contributed by @dbkr.
|
||||
* Spacestore should emit event after rebuilding home space ([#30132](https://github.com/element-hq/element-web/pull/30132)). Contributed by @MidhunSureshR.
|
||||
* Handle m.room.pinned\_events being invalid ([#30129](https://github.com/element-hq/element-web/pull/30129)). Contributed by @t3chguy.
|
||||
|
||||
|
||||
|
||||
Changes in [1.11.104](https://github.com/element-hq/element-desktop/releases/tag/v1.11.104) (2025-06-17)
|
||||
========================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
* Update the mobile\_guide page to the new design. ([#30006](https://github.com/element-hq/element-web/pull/30006)). Contributed by @pixlwave.
|
||||
* Provide a devtool for manually verifying other devices ([#30094](https://github.com/element-hq/element-web/pull/30094)). Contributed by @andybalaam.
|
||||
* Implement MSC4155: Invite filtering ([#29603](https://github.com/element-hq/element-web/pull/29603)). Contributed by @Half-Shot.
|
||||
* Add low priority avatar decoration to room tile ([#30065](https://github.com/element-hq/element-web/pull/30065)). Contributed by @MidhunSureshR.
|
||||
* Add ability to prevent window content being captured by other apps (Desktop) ([#30098](https://github.com/element-hq/element-web/pull/30098)). Contributed by @t3chguy.
|
||||
* New room list: move message preview in user settings ([#30023](https://github.com/element-hq/element-web/pull/30023)). Contributed by @florianduros.
|
||||
* New room list: change room options icon ([#30029](https://github.com/element-hq/element-web/pull/30029)). Contributed by @florianduros.
|
||||
* RoomListStore: Sort low priority rooms to the bottom of the list ([#30070](https://github.com/element-hq/element-web/pull/30070)). Contributed by @MidhunSureshR.
|
||||
* Add low priority filter pill to the room list UI ([#30060](https://github.com/element-hq/element-web/pull/30060)). Contributed by @MidhunSureshR.
|
||||
* New room list: remove color gradient in space panel ([#29721](https://github.com/element-hq/element-web/pull/29721)). Contributed by @florianduros.
|
||||
* /share?msg=foo endpoint using forward message dialog ([#29874](https://github.com/element-hq/element-web/pull/29874)). Contributed by @ara4n.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Fix restart loop in safeStorage ([#2373](https://github.com/element-hq/element-desktop/pull/2373)). Contributed by @dbkr.
|
||||
* Do not send empty auth when setting up cross-signing keys ([#29914](https://github.com/element-hq/element-web/pull/29914)). Contributed by @gnieto.
|
||||
* Settings: flip local video feed by default ([#29501](https://github.com/element-hq/element-web/pull/29501)). Contributed by @jbtrystram.
|
||||
* AccessSecretStorageDialog: various fixes ([#30093](https://github.com/element-hq/element-web/pull/30093)). Contributed by @richvdh.
|
||||
* AccessSecretStorageDialog: fix inability to enter recovery key ([#30090](https://github.com/element-hq/element-web/pull/30090)). Contributed by @richvdh.
|
||||
* Fix failure to upload thumbnail causing image to send as file ([#30086](https://github.com/element-hq/element-web/pull/30086)). Contributed by @t3chguy.
|
||||
* Low priority menu item should be a toggle ([#30071](https://github.com/element-hq/element-web/pull/30071)). Contributed by @MidhunSureshR.
|
||||
* Add sanity checks to prevent users from ignoring themselves ([#30079](https://github.com/element-hq/element-web/pull/30079)). Contributed by @MidhunSureshR.
|
||||
* Fix issue with duplicate images ([#30073](https://github.com/element-hq/element-web/pull/30073)). Contributed by @fatlewis.
|
||||
* Handle errors returned from Seshat ([#30083](https://github.com/element-hq/element-web/pull/30083)). Contributed by @richvdh.
|
||||
|
||||
|
||||
|
||||
Changes in [1.11.103](https://github.com/element-hq/element-desktop/releases/tag/v1.11.103) (2025-06-10)
|
||||
========================================================================================================
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
+ Check the sender of an event matches owner of session, preventing sender spoofing by homeserver owners.
|
||||
[13c1d20](https://github.com/matrix-org/matrix-rust-sdk/commit/13c1d2048286bbabf5e7bc6b015aafee98f04d55) (High, [GHSA-x958-rvg6-956w](https://github.com/matrix-org/matrix-rust-sdk/security/advisories/GHSA-x958-rvg6-956w)).
|
||||
|
||||
|
||||
|
||||
Changes in [1.11.102](https://github.com/element-hq/element-desktop/releases/tag/v1.11.102) (2025-06-03)
|
||||
========================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
* Support build-time specified protocol scheme for oidc callback ([#2285](https://github.com/element-hq/element-desktop/pull/2285)). Contributed by @t3chguy.
|
||||
* EW: Modernize the recovery key input modal ([#29819](https://github.com/element-hq/element-web/pull/29819)). Contributed by @uhoreg.
|
||||
* New room list: move secondary filters into primary filters ([#29972](https://github.com/element-hq/element-web/pull/29972)). Contributed by @florianduros.
|
||||
* Prompt the user when key storage is unexpectedly off ([#29912](https://github.com/element-hq/element-web/pull/29912)). Contributed by @andybalaam.
|
||||
* New room list: move sort menu in room list header ([#29983](https://github.com/element-hq/element-web/pull/29983)). Contributed by @florianduros.
|
||||
* New room list: rework spacing of room list item ([#29965](https://github.com/element-hq/element-web/pull/29965)). Contributed by @florianduros.
|
||||
* RLS: Remove forgotten room from skiplist ([#29933](https://github.com/element-hq/element-web/pull/29933)). Contributed by @MidhunSureshR.
|
||||
* Add room list sorting ([#29951](https://github.com/element-hq/element-web/pull/29951)). Contributed by @dbkr.
|
||||
* Don't use the minimised width(68px) on the new room list ([#29778](https://github.com/element-hq/element-web/pull/29778)). Contributed by @langleyd.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Enable plain text encryption before checking if encryption is available ([#2343](https://github.com/element-hq/element-desktop/pull/2343)). Contributed by @MidhunSureshR.
|
||||
* Enable plain text encryption early if we actually mean to use `basic_text` as backend ([#2341](https://github.com/element-hq/element-desktop/pull/2341)). Contributed by @MidhunSureshR.
|
||||
* [Backport staging] Close call options popup menu when option has been selected ([#30054](https://github.com/element-hq/element-web/pull/30054)). Contributed by @RiotRobot.
|
||||
* RoomListStoreV3: Only add new rooms that pass `VisibilityProvider` check ([#29974](https://github.com/element-hq/element-web/pull/29974)). Contributed by @MidhunSureshR.
|
||||
* Re-order primary filters ([#29957](https://github.com/element-hq/element-web/pull/29957)). Contributed by @dbkr.
|
||||
* Fix leaky CSS adding `!` to all H1 elements ([#29964](https://github.com/element-hq/element-web/pull/29964)). Contributed by @t3chguy.
|
||||
* Fix extensions panel style ([#29273](https://github.com/element-hq/element-web/pull/29273)). Contributed by @langleyd.
|
||||
* Fix state events being hidden from widgets in read\_events actions ([#29954](https://github.com/element-hq/element-web/pull/29954)). Contributed by @robintown.
|
||||
* Remove old filter test ([#29963](https://github.com/element-hq/element-web/pull/29963)). Contributed by @dbkr.
|
||||
|
||||
|
||||
|
||||
Changes in [1.11.101](https://github.com/element-hq/element-desktop/releases/tag/v1.11.101) (2025-05-20)
|
||||
========================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Docker image to facilitate building Element Desktop's native bits using a glibc version (2.31)
|
||||
# with broader compatibility, down to Debian bullseye & Ubuntu focal.
|
||||
FROM rust:bullseye
|
||||
FROM rust:bullseye@sha256:af1a29a166198e1295ca667007e95d2e70c866e3928ba9b25f3907035581c39e
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
- [Native Node modules](native-node-modules.md)
|
||||
- [Windows requirements](windows-requirements.md)
|
||||
- [Debugging](debugging.md)
|
||||
- [Using gdb](gdb.md)
|
||||
|
||||
# Distribution
|
||||
|
||||
28
docs/debugging.md
Normal file
28
docs/debugging.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Debugging Element-Desktop
|
||||
|
||||
There are two parts of the desktop app that you might want to debug.
|
||||
|
||||
## The renderer process
|
||||
|
||||
This is the regular element-web codeand can be debugged by just selecting 'toggle developer tools'
|
||||
from the menu, even on ppackaged builds. This then works the same as chrome dev tools for element web.
|
||||
|
||||
## The main process
|
||||
|
||||
This is debugged as a node app, so:
|
||||
|
||||
1. Open any chrome dev tools window
|
||||
1. Start element with the `--inspect-brk` flag
|
||||
1. Notice that you now have a little green icon in the top left of your chrome devtools window, click it.
|
||||
|
||||
You are now debugging the code of the desktop app itself.
|
||||
|
||||
## The main process of a package app
|
||||
|
||||
When the app is shipped, electron's "fuses" are flipped, editing the electron binary itself to prevent certain features from being usable, one of which is debugging using `--inspect-brk` as above. You can flip the fuse back on Linux as follows:
|
||||
|
||||
```
|
||||
sudo npx @electron/fuses write --app /opt/Element/element-desktop EnableNodeCliInspectArguments=on
|
||||
```
|
||||
|
||||
A similar command will work, in theory, on mac and windows, except that this will break code signing (which is the point of fuses) so you would have to re-sign the app or somesuch.
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as os from "node:os";
|
||||
import * as fs from "node:fs";
|
||||
import { Configuration as BaseConfiguration } from "electron-builder";
|
||||
import { type Configuration as BaseConfiguration, type Protocol } from "electron-builder";
|
||||
|
||||
/**
|
||||
* This script has different outputs depending on your os platform.
|
||||
@@ -16,9 +16,13 @@ import { Configuration as BaseConfiguration } from "electron-builder";
|
||||
* Passes $ED_DEBIAN_CHANGELOG to build.deb.fpm if specified
|
||||
*/
|
||||
|
||||
const DEFAULT_APP_ID = "im.riot.app";
|
||||
const NIGHTLY_APP_ID = "im.riot.nightly";
|
||||
const NIGHTLY_DEB_NAME = "element-nightly";
|
||||
|
||||
const DEFAULT_PROTOCOL_SCHEME = "io.element.desktop";
|
||||
const NIGHTLY_PROTOCOL_SCHEME = "io.element.nightly";
|
||||
|
||||
interface Pkg {
|
||||
name: string;
|
||||
productName: string;
|
||||
@@ -33,7 +37,11 @@ type Writable<T> = NonNullable<
|
||||
const pkg: Pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
||||
|
||||
interface Configuration extends BaseConfiguration {
|
||||
extraMetadata: Partial<Pick<Pkg, "version">> & Omit<Pkg, "version">;
|
||||
extraMetadata: Partial<Pick<Pkg, "version">> &
|
||||
Omit<Pkg, "version"> & {
|
||||
electron_appId: string;
|
||||
electron_protocol: string;
|
||||
};
|
||||
linux: BaseConfiguration["linux"];
|
||||
win: BaseConfiguration["win"];
|
||||
mac: BaseConfiguration["mac"];
|
||||
@@ -50,7 +58,7 @@ const config: Omit<Writable<Configuration>, "electronFuses"> & {
|
||||
// Make all fuses required to ensure they are all explicitly specified
|
||||
electronFuses: Required<Configuration["electronFuses"]>;
|
||||
} = {
|
||||
appId: "im.riot.app",
|
||||
appId: DEFAULT_APP_ID,
|
||||
asarUnpack: "**/*.node",
|
||||
electronFuses: {
|
||||
enableCookieEncryption: true,
|
||||
@@ -85,12 +93,15 @@ const config: Omit<Writable<Configuration>, "electronFuses"> & {
|
||||
name: pkg.name,
|
||||
productName: pkg.productName,
|
||||
description: pkg.description,
|
||||
electron_appId: DEFAULT_APP_ID,
|
||||
electron_protocol: DEFAULT_PROTOCOL_SCHEME,
|
||||
},
|
||||
linux: {
|
||||
target: ["tar.gz", "deb"],
|
||||
category: "Network;InstantMessaging;Chat",
|
||||
maintainer: "support@element.io",
|
||||
icon: "build/icons",
|
||||
executableName: pkg.name, // element-desktop or element-desktop-nightly
|
||||
},
|
||||
deb: {
|
||||
packageCategory: "net",
|
||||
@@ -142,12 +153,10 @@ const config: Omit<Writable<Configuration>, "electronFuses"> & {
|
||||
directories: {
|
||||
output: "dist",
|
||||
},
|
||||
protocols: [
|
||||
{
|
||||
name: "element",
|
||||
schemes: ["io.element.desktop", "element"],
|
||||
},
|
||||
],
|
||||
protocols: {
|
||||
name: "element",
|
||||
schemes: [DEFAULT_PROTOCOL_SCHEME, "element"],
|
||||
},
|
||||
nativeRebuilder: "sequential",
|
||||
nodeGypRebuild: false,
|
||||
npmRebuild: true,
|
||||
@@ -170,11 +179,13 @@ if (process.env.ED_SIGNTOOL_SUBJECT_NAME && process.env.ED_SIGNTOOL_THUMBPRINT)
|
||||
if (process.env.ED_NIGHTLY) {
|
||||
config.deb.fpm = []; // Clear the fpm as the breaks deb fields don't apply to nightly
|
||||
|
||||
config.appId = NIGHTLY_APP_ID;
|
||||
config.appId = config.extraMetadata.electron_appId = NIGHTLY_APP_ID;
|
||||
config.extraMetadata.productName += " Nightly";
|
||||
config.extraMetadata.name += "-nightly";
|
||||
config.extraMetadata.description += " (nightly unstable build)";
|
||||
config.linux.executableName += "-nightly";
|
||||
config.deb.fpm.push("--name", NIGHTLY_DEB_NAME);
|
||||
(config.protocols as Protocol).schemes[0] = config.extraMetadata.electron_protocol = NIGHTLY_PROTOCOL_SCHEME;
|
||||
|
||||
let version = process.env.ED_NIGHTLY;
|
||||
if (os.platform() === "win32") {
|
||||
|
||||
23
package.json
23
package.json
@@ -3,7 +3,7 @@
|
||||
"productName": "Element",
|
||||
"main": "lib/electron-main.js",
|
||||
"exports": "./lib/electron-main.js",
|
||||
"version": "1.11.101",
|
||||
"version": "1.11.105",
|
||||
"description": "Element: the future of secure communication",
|
||||
"author": "Element",
|
||||
"homepage": "https://element.io",
|
||||
@@ -58,7 +58,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/electron": "^6.0.0",
|
||||
"@standardnotes/electron-clear-data": "^1.0.5",
|
||||
"auto-launch": "^5.0.5",
|
||||
"counterpart": "^0.18.6",
|
||||
"electron-store": "^10.0.0",
|
||||
@@ -74,22 +73,22 @@
|
||||
"@babel/core": "^7.18.10",
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@electron/asar": "3.4.1",
|
||||
"@playwright/test": "1.52.0",
|
||||
"@electron/asar": "4.0.0",
|
||||
"@playwright/test": "1.53.1",
|
||||
"@stylistic/eslint-plugin": "^4.0.0",
|
||||
"@types/auto-launch": "^5.0.1",
|
||||
"@types/counterpart": "^0.18.1",
|
||||
"@types/minimist": "^1.2.1",
|
||||
"@types/node": "18.19.87",
|
||||
"@types/node": "18.19.112",
|
||||
"@types/pacote": "^11.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"app-builder-lib": "26.0.15",
|
||||
"app-builder-lib": "26.0.16",
|
||||
"chokidar": "^4.0.0",
|
||||
"detect-libc": "^2.0.0",
|
||||
"electron": "36.0.0",
|
||||
"electron-builder": "26.0.15",
|
||||
"electron-builder-squirrel-windows": "26.0.15",
|
||||
"electron": "36.5.0",
|
||||
"electron-builder": "26.0.16",
|
||||
"electron-builder-squirrel-windows": "26.0.16",
|
||||
"electron-devtools-installer": "^4.0.0",
|
||||
"eslint": "^8.26.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
@@ -101,7 +100,7 @@
|
||||
"glob": "^11.0.0",
|
||||
"husky": "^9.1.6",
|
||||
"knip": "^5.0.0",
|
||||
"lint-staged": "^15.2.10",
|
||||
"lint-staged": "^16.0.0",
|
||||
"matrix-web-i18n": "^3.2.1",
|
||||
"mkdirp": "^3.0.0",
|
||||
"pacote": "^21.0.0",
|
||||
@@ -115,8 +114,8 @@
|
||||
"matrix-seshat": "^4.0.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/node": "18.19.87",
|
||||
"@types/node": "18.19.112",
|
||||
"config-file-ts": "0.2.8-rc1",
|
||||
"node-abi": "4.4.0"
|
||||
"node-abi": "4.9.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/playwright:v1.52.0-jammy
|
||||
FROM mcr.microsoft.com/playwright:v1.53.1-jammy@sha256:a51c6393b5a6d85897f347c7e5cf03086f50739dde02077e2bb5dc73044c732a
|
||||
|
||||
WORKDIR /work/element-desktop
|
||||
|
||||
|
||||
@@ -31,6 +31,6 @@ test.describe("OIDC Native", () => {
|
||||
page.evaluate<string>(() => {
|
||||
return window.mxPlatformPeg.get().getOidcCallbackUrl().toString();
|
||||
}),
|
||||
).resolves.toBe("io.element.desktop:/vector/webapp/");
|
||||
).resolves.toMatch(/io\.element\.(desktop|nightly):\/vector\/webapp\//);
|
||||
});
|
||||
});
|
||||
|
||||
48
scripts/branch-match.sh
Executable file
48
scripts/branch-match.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script for downloading a branch of element-web matching the branch a PR is contributed from
|
||||
|
||||
set -x
|
||||
|
||||
deforg="element-hq"
|
||||
defrepo="element-web"
|
||||
|
||||
# The PR_NUMBER variable must be set explicitly.
|
||||
default_org_repo=${GITHUB_REPOSITORY:-"$deforg/$defrepo"}
|
||||
PR_ORG=${PR_ORG:-${default_org_repo%%/*}}
|
||||
PR_REPO=${PR_REPO:-${default_org_repo##*/}}
|
||||
|
||||
# A function that clones a branch of a repo based on the org, repo and branch
|
||||
clone() {
|
||||
org=$1
|
||||
repo=$2
|
||||
branch=$3
|
||||
if [ -n "$branch" ]
|
||||
then
|
||||
echo "Trying to use $org/$repo#$branch"
|
||||
# Disable auth prompts: https://serverfault.com/a/665959
|
||||
GIT_TERMINAL_PROMPT=0 git clone https://github.com/$org/$repo.git $repo --branch "$branch" --depth 1 && exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Getting info about a PR with number $PR_NUMBER"
|
||||
apiEndpoint="https://api.github.com/repos/$PR_ORG/$PR_REPO/pulls/$PR_NUMBER"
|
||||
head=$(curl "$apiEndpoint" | jq -r '.head.label')
|
||||
|
||||
# for forks, $head will be in the format "fork:branch", so we split it by ":"
|
||||
# into an array. On non-forks, this has the effect of splitting into a single
|
||||
# element array given ":" shouldn't appear in the head - it'll just be the
|
||||
# branch name. Based on the results, we clone.
|
||||
BRANCH_ARRAY=(${head//:/ })
|
||||
TRY_ORG=$deforg
|
||||
TRY_BRANCH=${BRANCH_ARRAY[0]}
|
||||
if [[ "$head" == *":"* ]]; then
|
||||
# ... but only match that fork if it's a real fork
|
||||
if [ "${BRANCH_ARRAY[0]}" != "$PR_ORG" ]; then
|
||||
TRY_ORG=${BRANCH_ARRAY[0]}
|
||||
fi
|
||||
TRY_BRANCH=${BRANCH_ARRAY[1]}
|
||||
fi
|
||||
clone "$TRY_ORG" "$defrepo" "$TRY_BRANCH"
|
||||
|
||||
exit 1
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
deforg="$1"
|
||||
defrepo="$2"
|
||||
defbranch="$3"
|
||||
|
||||
[ -z "$defbranch" ] && defbranch="develop"
|
||||
|
||||
rm -r "$defrepo" || true
|
||||
|
||||
clone() {
|
||||
org=$1
|
||||
repo=$2
|
||||
branch=$3
|
||||
if [ -n "$branch" ]
|
||||
then
|
||||
echo "Trying to use $org/$repo#$branch"
|
||||
# Disable auth prompts: https://serverfault.com/a/665959
|
||||
GIT_TERMINAL_PROMPT=0 git clone https://github.com/$org/$repo.git $repo --branch "$branch" --depth 1 && exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Try the PR author's branch in case it exists on the deps as well.
|
||||
# If BUILDKITE_BRANCH is set, it will contain either:
|
||||
# * "branch" when the author's branch and target branch are in the same repo
|
||||
# * "author:branch" when the author's branch is in their fork
|
||||
# We can split on `:` into an array to check.
|
||||
BUILDKITE_BRANCH_ARRAY=(${BUILDKITE_BRANCH//:/ })
|
||||
if [[ "${#BUILDKITE_BRANCH_ARRAY[@]}" == "1" ]]; then
|
||||
clone $deforg $defrepo $BUILDKITE_BRANCH
|
||||
elif [[ "${#BUILDKITE_BRANCH_ARRAY[@]}" == "2" ]]; then
|
||||
clone ${BUILDKITE_BRANCH_ARRAY[0]} $defrepo ${BUILDKITE_BRANCH_ARRAY[1]}
|
||||
fi
|
||||
# Try the target branch of the push or PR.
|
||||
clone $deforg $defrepo $BUILDKITE_PULL_REQUEST_BASE_BRANCH
|
||||
# Try the current branch from Jenkins.
|
||||
clone $deforg $defrepo `"echo $GIT_BRANCH" | sed -e 's/^origin\///'`
|
||||
# Use the default branch as the last resort.
|
||||
clone $deforg $defrepo $defbranch
|
||||
4
src/@types/global.d.ts
vendored
4
src/@types/global.d.ts
vendored
@@ -13,11 +13,13 @@ import { type AppLocalization } from "../language-helper.js";
|
||||
// global type extensions need to use var for whatever reason
|
||||
/* eslint-disable no-var */
|
||||
declare global {
|
||||
type IConfigOptions = Record<string, any>;
|
||||
|
||||
var mainWindow: BrowserWindow | null;
|
||||
var appQuitting: boolean;
|
||||
var appLocalization: AppLocalization;
|
||||
var launcher: AutoLaunch;
|
||||
var vectorConfig: Record<string, any>;
|
||||
var vectorConfig: IConfigOptions;
|
||||
var trayConfig: {
|
||||
// eslint-disable-next-line camelcase
|
||||
icon_path: string;
|
||||
|
||||
26
src/build-config.ts
Normal file
26
src/build-config.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import path, { dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { type JsonObject, loadJsonFile } from "./utils.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
interface BuildConfig {
|
||||
appId: string;
|
||||
protocol: string;
|
||||
}
|
||||
|
||||
export function readBuildConfig(): BuildConfig {
|
||||
const packageJson = loadJsonFile(path.join(__dirname, "..", "package.json")) as JsonObject;
|
||||
return {
|
||||
appId: (packageJson["electron_appId"] as string) || "im.riot.app",
|
||||
protocol: (packageJson["electron_protocol"] as string) || "io.element.desktop",
|
||||
};
|
||||
}
|
||||
@@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
// Squirrel on windows starts the app with various flags as hooks to tell us when we've been installed/uninstalled etc.
|
||||
import "./squirrelhooks.js";
|
||||
import { app, BrowserWindow, Menu, autoUpdater, protocol, dialog, type Input, type Event, session } from "electron";
|
||||
import { app, BrowserWindow, Menu, autoUpdater, dialog, type Input, type Event, session, protocol } from "electron";
|
||||
// eslint-disable-next-line n/file-extension-in-import
|
||||
import * as Sentry from "@sentry/electron/main";
|
||||
import AutoLaunch from "auto-launch";
|
||||
@@ -28,12 +28,13 @@ import Store from "./store.js";
|
||||
import { buildMenuTemplate } from "./vectormenu.js";
|
||||
import webContentsHandler from "./webcontents-handler.js";
|
||||
import * as updater from "./updater.js";
|
||||
import { getProfileFromDeeplink, protocolInit } from "./protocol.js";
|
||||
import ProtocolHandler from "./protocol.js";
|
||||
import { _t, AppLocalization } from "./language-helper.js";
|
||||
import { setDisplayMediaCallback } from "./displayMediaCallback.js";
|
||||
import { setupMacosTitleBar } from "./macos-titlebar.js";
|
||||
import { type Json, loadJsonFile } from "./utils.js";
|
||||
import { setupMediaAuth } from "./media-auth.js";
|
||||
import { readBuildConfig } from "./build-config.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
@@ -72,10 +73,13 @@ function isRealUserDataDir(d: string): boolean {
|
||||
return fs.existsSync(path.join(d, "IndexedDB"));
|
||||
}
|
||||
|
||||
const buildConfig = readBuildConfig();
|
||||
const protocolHandler = new ProtocolHandler(buildConfig.protocol);
|
||||
|
||||
// check if we are passed a profile in the SSO callback url
|
||||
let userDataPath: string;
|
||||
|
||||
const userDataPathInProtocol = getProfileFromDeeplink(argv["_"]);
|
||||
const userDataPathInProtocol = protocolHandler.getProfileFromDeeplink(argv["_"]);
|
||||
if (userDataPathInProtocol) {
|
||||
userDataPath = userDataPathInProtocol;
|
||||
} else if (argv["profile-dir"]) {
|
||||
@@ -311,7 +315,7 @@ if (!gotLock) {
|
||||
}
|
||||
|
||||
// do this after we know we are the primary instance of the app
|
||||
protocolInit();
|
||||
protocolHandler.initialise(userDataPath);
|
||||
|
||||
// Register the scheme the app is served from as 'standard'
|
||||
// which allows things like relative URLs and IndexedDB to
|
||||
@@ -449,14 +453,6 @@ app.on("ready", async () => {
|
||||
store,
|
||||
});
|
||||
|
||||
try {
|
||||
console.debug("Ensuring storage is ready");
|
||||
await store.safeStorageReady();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
app.exit(1);
|
||||
}
|
||||
|
||||
// Load the previous window state with fallback to defaults
|
||||
const mainWindowState = windowStateKeeper({
|
||||
defaultWidth: 1024,
|
||||
@@ -488,6 +484,17 @@ app.on("ready", async () => {
|
||||
webgl: true,
|
||||
},
|
||||
});
|
||||
|
||||
global.mainWindow.setContentProtection(store.get("enableContentProtection"));
|
||||
|
||||
try {
|
||||
console.debug("Ensuring storage is ready");
|
||||
if (!(await store.prepareSafeStorage(global.mainWindow.webContents.session))) return;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
app.exit(1);
|
||||
}
|
||||
|
||||
void global.mainWindow.loadURL("vector://vector/webapp/");
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
@@ -616,4 +623,4 @@ app.on("second-instance", (ev, commandLine, workingDirectory) => {
|
||||
// It must also match the ID found in 'electron-builder'
|
||||
// in order to get the title and icon to show up correctly.
|
||||
// Ref: https://stackoverflow.com/a/77314604/3525780
|
||||
app.setAppUserModelId("im.riot.app");
|
||||
app.setAppUserModelId(buildConfig.appId);
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Vymazat data a znovu načíst?",
|
||||
"backend_changed_detail": "Nelze získat přístup k tajnému klíči ze systémové klíčenky, zdá se, že se změnil.",
|
||||
"backend_changed_title": "Nepodařilo se načíst databázi",
|
||||
"unknown_backend_override": "Váš systém má nepodporovanou klíčenku, což znamená, že databázi nelze otevřít.",
|
||||
"unknown_backend_override_details": "Další podrobnosti naleznete v protokolech.",
|
||||
"unknown_backend_override_title": "Nepodařilo se načíst databázi",
|
||||
"backend_no_encryption": "Váš systém má podporovanou klíčenku, ale šifrování není k dispozici.",
|
||||
"backend_no_encryption_detail": "Electron zjistil, že pro vaši klíčenku %(backend)s není k dispozici šifrování. Ujistěte se, že máte nainstalovanou klíčenku. Pokud ji máte, restartujte počítač a zkuste to znovu. Volitelně můžete povolit %(brand)s použít slabší formu šifrování.",
|
||||
"backend_no_encryption_title": "Bez podpory šifrování",
|
||||
"unsupported_keyring": "Váš systém má nepodporovanou klíčenku, což znamená, že databázi nelze otevřít.",
|
||||
"unsupported_keyring_cta": "Používat slabší šifrování",
|
||||
"unsupported_keyring_detail": "Detekce klíčenky Electronu nenalezla podporovaný backend. Můžete se pokusit ručně nakonfigurovat backend spuštěním %(brand)s s argumentem příkazového řádku, jednorázovou operací. Viz %(link)s.",
|
||||
"unsupported_keyring_title": "Systém není podporován"
|
||||
"unsupported_keyring_title": "Systém není podporován",
|
||||
"unsupported_keyring_use_basic_text": "Používat slabší šifrování",
|
||||
"unsupported_keyring_use_plaintext": "Nepoužívat žádné šifrování"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
"about": "Ynghylch",
|
||||
"brand_help": "Cymorth %(brand)s",
|
||||
"help": "Cymorth",
|
||||
"preferences": "Dewisiadau"
|
||||
"no": "Na",
|
||||
"preferences": "Dewisiadau",
|
||||
"yes": "Iawn"
|
||||
},
|
||||
"confirm_quit": "Ydych chi'n siŵr eich bod am roi'r gorau iddi?",
|
||||
"edit_menu": {
|
||||
@@ -49,6 +51,19 @@
|
||||
"save_image_as_error_description": "Methodd cadw'r ddelwedd",
|
||||
"save_image_as_error_title": "Methodd cadw'r ddelwedd"
|
||||
},
|
||||
"store": {
|
||||
"error": {
|
||||
"backend_changed": "Clirio data ac ail-lwytho?",
|
||||
"backend_changed_detail": "Methu cael mynediad at y gyfrinach o allweddi'r system, mae'n ymddangos ei fod wedi newid.",
|
||||
"backend_changed_title": "Methwyd llwytho'r gronfa ddata",
|
||||
"backend_no_encryption": "Mae gan eich system cylch allwedd sy'n cael ei gefnogi ond nid yw amgryptio ar gael.",
|
||||
"backend_no_encryption_detail": "Mae Electron wedi canfod nad yw amgryptio ar gael ar eich cylch allweddi %(backend)s. Gwnewch yn siŵr bod y cylch allweddi wedi'i osod. Os oes y cylch allweddi wedi'i osod, ail gychwynnwch a cheisiwch eto. Yn ddewisol, gallwch ganiatáu i %(brand)s ddefnyddio ffurf wannach o amgryptio.",
|
||||
"backend_no_encryption_title": "Dim cefnogaeth amgryptio",
|
||||
"unsupported_keyring": "Mae gan eich system allweddell nad yw'n cael ei chefnogi sy'n golygu nad oes modd agor y gronfa ddata.",
|
||||
"unsupported_keyring_detail": "Heb ganfod allweddell Electron gefn. Gallwch geisio ffurfweddu'r gefn â llaw trwy gychwyn %(brand)s gyda dadl llinell orchymyn, gweithrediad untro. Gweler %(link)s.",
|
||||
"unsupported_keyring_title": "System heb ei chefnogi"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
"actual_size": "Maint Gwirioneddol",
|
||||
"toggle_developer_tools": "Toglo Offer Datblygwyr",
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Daten löschen und neu laden?",
|
||||
"backend_changed_detail": "Zugriff auf Schlüssel im Systemschlüsselbund nicht möglich, er scheint sich geändert zu haben.",
|
||||
"backend_changed_title": "Datenbank konnte nicht geladen werden",
|
||||
"unknown_backend_override": "Der Schlüsselbund ihres Systems wird nicht unterstützt, wodurch die Datenbank nicht geöffnet werden kann.",
|
||||
"unknown_backend_override_details": "Weitere Informationen finden Sie in den Protokollen.",
|
||||
"unknown_backend_override_title": "Die Datenbank konnte nicht geladen werden",
|
||||
"backend_no_encryption": "Ihr System verfügt über einen unterstützten Keyring, aber die Verschlüsselung ist nicht verfügbar.",
|
||||
"backend_no_encryption_detail": "Electron hat festgestellt, dass Verschlüsselung in Ihrem Keyring %(backend)s nicht verfügbar ist. Bitte stellen Sie sicher, dass Sie den Keyringinstalliert haben. Wenn Sie den Keyring installiert haben, starten Sie ihn bitte neu und versuchen Sie es erneut. Optional können Sie die Verwendung einer schwächeren Form der Verschlüsselung zulassen %(brand)s.",
|
||||
"backend_no_encryption_title": "Keine Verschlüsselungsunterstützung",
|
||||
"unsupported_keyring": "Der Schlüsselbund ihres Systems wird nicht unterstützt, wodurch die Datenbank nicht geöffnet werden kann.",
|
||||
"unsupported_keyring_cta": "Schwächere Verschlüsselung verwenden",
|
||||
"unsupported_keyring_detail": "Die Schlüsselbunderkennung von Electron hat kein unterstütztes Backend gefunden. Möglicherweise können sie dennoch den ihres Systemes verwenden. Infos unter %(link)s.",
|
||||
"unsupported_keyring_title": "System nicht unterstützt"
|
||||
"unsupported_keyring_title": "System nicht unterstützt",
|
||||
"unsupported_keyring_use_basic_text": "Schwächere Verschlüsselung verwenden",
|
||||
"unsupported_keyring_use_plaintext": "Verwenden Sie keine Verschlüsselung"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Clear data and reload?",
|
||||
"backend_changed_detail": "Unable to access secret from system keyring, it appears to have changed.",
|
||||
"backend_changed_title": "Failed to load database",
|
||||
"unknown_backend_override": "Your system has an unsupported keyring meaning the database cannot be opened.",
|
||||
"unknown_backend_override_details": "Please check the logs for more details.",
|
||||
"unknown_backend_override_title": "Failed to load database",
|
||||
"backend_no_encryption": "Your system has a supported keyring but encryption is not available.",
|
||||
"backend_no_encryption_detail": "Electron has detected that encryption is not available on your keyring %(backend)s. Please ensure that you have the keyring installed. If you do have the keyring installed, please reboot and try again. Optionally, you can allow %(brand)s to use a weaker form of encryption.",
|
||||
"backend_no_encryption_title": "No encryption support",
|
||||
"unsupported_keyring": "Your system has an unsupported keyring meaning the database cannot be opened.",
|
||||
"unsupported_keyring_cta": "Use weaker encryption",
|
||||
"unsupported_keyring_detail": "Electron's keyring detection did not find a supported backend. You can attempt to manually configure the backend by starting %(brand)s with a command-line argument, a one-time operation. See %(link)s.",
|
||||
"unsupported_keyring_title": "System unsupported"
|
||||
"unsupported_keyring_title": "System unsupported",
|
||||
"unsupported_keyring_use_basic_text": "Use weaker encryption",
|
||||
"unsupported_keyring_use_plaintext": "Use no encryption"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Kas kustutame andmed ja laadime uuesti?",
|
||||
"backend_changed_detail": "Süsteemsest võtmerõngast ei õnnestu laadida vajalikku saladust, tundub et ta on muutunud.",
|
||||
"backend_changed_title": "Andmebaasi ei õnnestunud laadida",
|
||||
"unknown_backend_override": "Sinu süsteemis on kasutusel mittetoetatud võtmerõnga versioon ning see tähendab, et andmebaasi ei saa avada.",
|
||||
"unknown_backend_override_details": "Lisateavet leiad logidest.",
|
||||
"unknown_backend_override_title": "Andmebaasi ei õnnestunud laadida",
|
||||
"backend_no_encryption": "Sinu süsteem kasutab toetatud võtmerõngast, kuid krüptimist pole saadaval.",
|
||||
"backend_no_encryption_detail": "Electron on tuvastanud, et krüptimine pole sinu %(backend)s võtmerõnga jaoks saadaval. Palun kontrolli, et võtmerõngas oleks korrektselt paigaldatud. Kui sul on võtmerõngas paigaldatud, siis palun taaskäivita ta ja proovi uuesti. Lisavõimalusena saad lubada, et %(brand)s kasutab nõrgemat krüptimislahendust.",
|
||||
"backend_no_encryption_title": "Krüptimise tugi puudub",
|
||||
"unsupported_keyring": "Sinu süsteemis on kasutusel mittetoetatud võtmerõnga versioon ning see tähendab, et andmebaasi ei saa avada.",
|
||||
"unsupported_keyring_cta": "Kasuta nõrgemat krüptimist",
|
||||
"unsupported_keyring_detail": "Electroni võtmerõnga tuvastamine ei leidnud toetatud taustateenust. Kui käivitad rakenduse %(brand)s käsurealt õigete argumentidega, siis võib taustateenuse käsitsi seadistamine õnnestuda ning seda tegevust peaksid vaid üks kord tegema. Lisateave: %(link)s.",
|
||||
"unsupported_keyring_title": "Süsteem pole toetatud"
|
||||
"unsupported_keyring_title": "Süsteem pole toetatud",
|
||||
"unsupported_keyring_use_basic_text": "Kasuta nõrgemat krüptimist",
|
||||
"unsupported_keyring_use_plaintext": "Ära üldse kasuta krüptimist"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
"about": "Tietoa",
|
||||
"brand_help": "%(brand)s-tuki",
|
||||
"help": "Ohje",
|
||||
"preferences": "Valinnat"
|
||||
"no": "Ei",
|
||||
"preferences": "Valinnat",
|
||||
"yes": "Kyllä"
|
||||
},
|
||||
"confirm_quit": "Haluatko varmasti poistua?",
|
||||
"edit_menu": {
|
||||
@@ -49,6 +51,12 @@
|
||||
"save_image_as_error_description": "Kuvan tallennus epäonnistui",
|
||||
"save_image_as_error_title": "Kuvan tallennus epäonnistui"
|
||||
},
|
||||
"store": {
|
||||
"error": {
|
||||
"backend_changed_title": "Tietokannan lataaminen epäonnistui",
|
||||
"unsupported_keyring_title": "Järjestelmä ei ole tuettu"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
"actual_size": "Alkuperäinen koko",
|
||||
"toggle_developer_tools": "Näytä tai piilota kehittäjätyökalut",
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Effacer les données et recharger ?",
|
||||
"backend_changed_detail": "Impossible d'accéder aux secrets depuis le trousseau de clés du système, il semble avoir changé.",
|
||||
"backend_changed_title": "Impossible de charger la base de données",
|
||||
"unknown_backend_override": "Votre système possède un trousseau de clés non pris en charge, ce qui signifie que la base de données ne peut pas être ouverte.",
|
||||
"unknown_backend_override_details": "Veuillez consulter les journaux pour plus de détails.",
|
||||
"unknown_backend_override_title": "Impossible de charger la base de données",
|
||||
"backend_no_encryption": "Votre système dispose d'un trousseau de clés compatible mais le chiffrement est indisponible.",
|
||||
"backend_no_encryption_detail": "Electron a détecté que le chiffrement n'est pas disponible pour le trousseau %(backend)s. Veuillez vérifier que votre trousseau est installé. Si c'est le cas, redémarrez et réessayez. Comme alternative, vous pouvez autoriser %(brand)s à utiliser une option de chiffrement moins sécurisée.",
|
||||
"backend_no_encryption_title": "Aucune prise en charge du chiffrement",
|
||||
"unsupported_keyring": "Votre système possède un trousseau de clés non pris en charge, la base de données ne peut pas être ouverte.",
|
||||
"unsupported_keyring_cta": "Utiliser un chiffrement plus faible",
|
||||
"unsupported_keyring_detail": "La détection du porte-clés par Electron n'a pas permis de trouver de backend compatible. Vous pouvez essayer de configurer manuellement le backend en utilisant %(brand)s avec un argument de ligne de commande. Cette opération doit être effectuer une seule fois. Voir%(link)s.",
|
||||
"unsupported_keyring_title": "Système non pris en charge"
|
||||
"unsupported_keyring_title": "Système non pris en charge",
|
||||
"unsupported_keyring_use_basic_text": "Utiliser un chiffrement plus faible",
|
||||
"unsupported_keyring_use_plaintext": "N'utilise pas de chiffrement"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Adatok törlése és újratöltés?",
|
||||
"backend_changed_detail": "Nem sikerült hozzáférni a rendszerkulcstartó titkos kódjához, úgy tűnik, megváltozott.",
|
||||
"backend_changed_title": "Nem sikerült betölteni az adatbázist",
|
||||
"unknown_backend_override": "A rendszer nem támogatott kulcstartóval rendelkezik, ami azt jelenti, hogy az adatbázis nem nyitható meg.",
|
||||
"unknown_backend_override_details": "További részletekért ellenőrizze a naplókat.",
|
||||
"unknown_backend_override_title": "Nem sikerült betölteni az adatbázist",
|
||||
"backend_no_encryption": "A rendszer támogatott kulcstartóval rendelkezik, de a titkosítás nem érhető el.",
|
||||
"backend_no_encryption_detail": "Az Electron észlelte, hogy a titkosítás nem érhető el a(z) %(backend)s kulcstartóján. Győződjön meg róla, hogy telepítve van-e a kulcstartó. Ha telepítve van, indítsa újra és próbálja újra. Esetleg engedélyezheti a gyengébb titkosítást az %(brand)s számára.",
|
||||
"backend_no_encryption_title": "Nincs titkosítási támogatás",
|
||||
"unsupported_keyring": "A rendszer nem támogatott kulcstartóval rendelkezik, ami azt jelenti, hogy az adatbázis nem nyitható meg.",
|
||||
"unsupported_keyring_cta": "Gyengébb titkosítás használata",
|
||||
"unsupported_keyring_detail": "Az Electron kulcstartóészlelése nem talált támogatott háttérrendszert. Megpróbálhatja kézileg beállítani a háttérrendszert az %(brand)s egyszeri, parancssori argumentummal való indításával. Lásd: %(link)s.",
|
||||
"unsupported_keyring_title": "A rendszer nem támogatott"
|
||||
"unsupported_keyring_title": "A rendszer nem támogatott",
|
||||
"unsupported_keyring_use_basic_text": "Gyengébb titkosítás használata",
|
||||
"unsupported_keyring_use_plaintext": "Ne használjon titkosítást"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Hapus data dan muat ulang?",
|
||||
"backend_changed_detail": "Tidak dapat mengakses rahasia dari keyring sistem, tampaknya telah berubah.",
|
||||
"backend_changed_title": "Gagal memuat basis data",
|
||||
"unknown_backend_override": "Sistem Anda memiliki keyring yang tidak didukung yang berarti basis data tidak dapat dibuka.",
|
||||
"unknown_backend_override_details": "Silakan periksa log untuk detail lebih lanjut.",
|
||||
"unknown_backend_override_title": "Gagal memuat basis data",
|
||||
"backend_no_encryption": "Sistem Anda memiliki keyring yang didukung tetapi enkripsi tidak tersedia.",
|
||||
"backend_no_encryption_detail": "Electron telah mendeteksi bahwa enkripsi tidak tersedia pada keyring %(backend)s Anda. Harap pastikan bahwa Anda telah memasang keyring. Jika Anda telah memasang keyring, silakan mulai ulang dan coba lagi. Secara opsional, Anda dapat mengizinkan %(brand)s untuk menggunakan bentuk enkripsi yang lebih lemah.",
|
||||
"backend_no_encryption_title": "Tidak ada dukungan enkripsi",
|
||||
"unsupported_keyring": "Sistem Anda memiliki keyring yang tidak didukung yang berarti basis data tidak dapat dibuka.",
|
||||
"unsupported_keyring_cta": "Gunakan enkripsi yang lebih lemah",
|
||||
"unsupported_keyring_detail": "Deteksi keyring Electron tidak menemukan backend yang didukung. Anda dapat mencoba mengonfigurasi backend secara manual dengan memulai %(brand)s dengan argumen baris perintah, operasi satu kali. Lihat %(link)s.",
|
||||
"unsupported_keyring_title": "Sistem tidak didukung"
|
||||
"unsupported_keyring_title": "Sistem tidak didukung",
|
||||
"unsupported_keyring_use_basic_text": "Gunakan enkripsi yang lebih lemah",
|
||||
"unsupported_keyring_use_plaintext": "Jangan gunakan enkripsi"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Tøm data og last inn på nytt?",
|
||||
"backend_changed_detail": "Kan ikke få tilgang til hemmeligheten fra systemnøkkelringen, den ser ut til å ha blitt endret.",
|
||||
"backend_changed_title": "Kunne ikke laste inn databasen",
|
||||
"unknown_backend_override": "Systemet ditt har en nøkkelring som ikke støttes, noe som betyr at databasen ikke kan åpnes.",
|
||||
"unknown_backend_override_details": "Vennligst sjekk loggene for mer informasjon.",
|
||||
"unknown_backend_override_title": "Kunne ikke laste inn databasen",
|
||||
"backend_no_encryption": "Systemet ditt har en støttet nøkkelring, men kryptering er ikke tilgjengelig.",
|
||||
"backend_no_encryption_detail": "Electron har oppdaget at kryptering ikke er tilgjengelig på nøkkelringen %(backend)s din. Forsikre deg om at du har nøkkelringen installert. Hvis du har nøkkelringen installert, vennligst start på nytt og prøv igjen. Eventuelt kan du tillate %(brand)s å bruke en svakere form for kryptering.",
|
||||
"backend_no_encryption_title": "Ingen støtte for kryptering",
|
||||
"unsupported_keyring": "Systemet ditt har en nøkkelring som ikke støttes, noe som betyr at databasen ikke kan åpnes.",
|
||||
"unsupported_keyring_cta": "Bruk svakere kryptering",
|
||||
"unsupported_keyring_detail": "Electrons nøkkelringdeteksjon fant ikke en støttet backend. Du kan prøve å konfigurere backend manuelt ved å starte %(brand)s med et kommandolinjeargument, en engangsoperasjon. Se%(link)s.",
|
||||
"unsupported_keyring_title": "Systemet støttes ikke"
|
||||
"unsupported_keyring_title": "Systemet støttes ikke",
|
||||
"unsupported_keyring_use_basic_text": "Bruk svakere kryptering",
|
||||
"unsupported_keyring_use_plaintext": "Ikke bruk kryptering"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
@@ -56,11 +56,7 @@
|
||||
"backend_changed": "Wyczyścić dane i przeładować?",
|
||||
"backend_changed_detail": "Nie można uzyskać dostępu do sekretnego magazynu, wygląda na to, że uległ zmianie.",
|
||||
"backend_changed_title": "Nie udało się załadować bazy danych",
|
||||
"unknown_backend_override": "System zawiera niewspierany keyring, nie można otworzyć bazy danych.",
|
||||
"unknown_backend_override_details": "Sprawdź dziennik, aby uzyskać więcej informacji.",
|
||||
"unknown_backend_override_title": "Nie udało się załadować bazy danych",
|
||||
"unsupported_keyring": "System zawiera niewspierany keyring, nie można otworzyć bazy danych.",
|
||||
"unsupported_keyring_cta": "Użyj słabszego szyfrowania",
|
||||
"unsupported_keyring_detail": "Wykrywanie keyringu Electron nie znalazł wspieranego backendu. Możesz spróbować ręcznie ustawić backed, uruchamiając %(brand)s za pomocą wiesza poleceń. Zobacz %(link)s.",
|
||||
"unsupported_keyring_title": "System niewspierany"
|
||||
}
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Limpar dados e recarregar?",
|
||||
"backend_changed_detail": "Não foi possível acessar o segredo no cofre do sistema, parece que ele foi alterado.",
|
||||
"backend_changed_title": "Falha ao carregar o banco de dados",
|
||||
"unknown_backend_override": "Seu sistema possui um cofre não compatível, o que impede a abertura do banco de dados.",
|
||||
"unknown_backend_override_details": "Verifique os logs para obter mais detalhes.",
|
||||
"unknown_backend_override_title": "Falha ao carregar o banco de dados",
|
||||
"backend_no_encryption": "Seu sistema tem um cofre compatível, mas a criptografia não está disponível.",
|
||||
"backend_no_encryption_detail": "O Electron detetou que a encriptação não está disponível no seu cofre %(backend)s. Certifique-se de que tem o cofre instalado. Se tiver o cofre instalado, reinicie e tente novamente. Opcionalmente, você pode permitir que %(brand)s use uma forma mais fraca de criptografia.",
|
||||
"backend_no_encryption_title": "Sem suporte para criptografia",
|
||||
"unsupported_keyring": "Seu sistema possui um cofre não compatível, o que impede a abertura do banco de dados.",
|
||||
"unsupported_keyring_cta": "Use criptografia mais fraca",
|
||||
"unsupported_keyring_detail": "A detecção de chaveiro do Electron não encontrou um backend compatível. Você pode tentar configurar manualmente o backend iniciando %(brand)s com um argumento de linha de comando, uma operação única. Consulte %(link)s.",
|
||||
"unsupported_keyring_title": "Sistema não suportado"
|
||||
"unsupported_keyring_detail": "A detecção de cofre do Electron não encontrou um backend compatível. Você pode tentar configurar manualmente o backend iniciando %(brand)s com um argumento de linha de comando, uma operação única. Consulte %(link)s.",
|
||||
"unsupported_keyring_title": "Sistema não suportado",
|
||||
"unsupported_keyring_use_basic_text": "Use criptografia mais fraca",
|
||||
"unsupported_keyring_use_plaintext": "Não usar criptografia"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
"about": "О программе",
|
||||
"brand_help": "Помощь %(brand)s",
|
||||
"help": "Помощь",
|
||||
"preferences": "Предпочтения"
|
||||
"no": "Нет",
|
||||
"preferences": "Предпочтения",
|
||||
"yes": "Да"
|
||||
},
|
||||
"confirm_quit": "Вы уверены, что хотите выйти?",
|
||||
"edit_menu": {
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Vymazať údaje a znova načítať?",
|
||||
"backend_changed_detail": "Nepodarilo sa získať prístup k tajnému kľúču zo systémového zväzku kľúčov, zdá sa, že sa zmenil.",
|
||||
"backend_changed_title": "Nepodarilo sa načítať databázu",
|
||||
"unknown_backend_override": "Váš systém má nepodporovaný zväzok kľúčov, čo znamená, že databázu nemožno otvoriť.",
|
||||
"unknown_backend_override_details": "Pre viac informácií si pozrite protokoly.",
|
||||
"unknown_backend_override_title": "Nepodarilo sa načítať databázu",
|
||||
"backend_no_encryption": "Váš systém má podporovaný zväzok kľúčov, ale šifrovanie nie je k dispozícii.",
|
||||
"backend_no_encryption_detail": "Electron zistil, že šifrovanie nie je k dispozícii na vašom zväzku kľúčov %(backend)s. Uistite sa, že máte nainštalovaný zväzok kľúčov. Ak máte zväzok kľúčov nainštalovaný, reštartujte počítač a skúste to znova. Voliteľne môžete povoliť aplikácii %(brand)s používať slabšiu formu šifrovania.",
|
||||
"backend_no_encryption_title": "Žiadna podpora šifrovania",
|
||||
"unsupported_keyring": "Váš systém má nepodporovaný zväzok kľúčov, čo znamená, že databázu nemožno otvoriť.",
|
||||
"unsupported_keyring_cta": "Použiť slabšie šifrovanie",
|
||||
"unsupported_keyring_detail": "Detekcia zväzku kľúčov aplikácie Electron nenašla podporovaný backend. Môžete sa pokúsiť manuálne nastaviť backend spustením aplikácie %(brand)s s argumentom príkazového riadka, je to jednorazová operácia. Pozrite si %(link)s .",
|
||||
"unsupported_keyring_title": "Systém nie je podporovaný"
|
||||
"unsupported_keyring_title": "Systém nie je podporovaný",
|
||||
"unsupported_keyring_use_basic_text": "Použiť slabšie šifrovanie",
|
||||
"unsupported_keyring_use_plaintext": "Nepoužiť žiadne šifrovanie"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
"about": "Om",
|
||||
"brand_help": "%(brand)s-hjälp",
|
||||
"help": "Hjälp",
|
||||
"preferences": "Inställningar"
|
||||
"no": "Nej",
|
||||
"preferences": "Inställningar",
|
||||
"yes": "Ja"
|
||||
},
|
||||
"confirm_quit": "Är du säker att du vill avsluta?",
|
||||
"edit_menu": {
|
||||
@@ -49,6 +51,16 @@
|
||||
"save_image_as_error_description": "Bilden sparades inte",
|
||||
"save_image_as_error_title": "Misslyckades med att spara bilden"
|
||||
},
|
||||
"store": {
|
||||
"error": {
|
||||
"backend_changed": "Rensa data och ladda om?",
|
||||
"backend_changed_detail": "Kunde inte komma åt hemligheten från systemnyckelringen, det verkar ha ändrats.",
|
||||
"backend_changed_title": "Misslyckades att ladda databasen",
|
||||
"unsupported_keyring": "Ditt system har en nyckelring som inte stöds, vilket innebär att databasen inte kan öppnas.",
|
||||
"unsupported_keyring_detail": "Electrons nyckelringsdetektering hittade inte en backend som stöds. Du kan försöka konfigurera backend manuellt genom att starta %(brand)s med ett kommandoradsargument, en engångsåtgärd. Se %(link)s.",
|
||||
"unsupported_keyring_title": "Systemet stöds inte"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
"actual_size": "Verklig storlek",
|
||||
"toggle_developer_tools": "Växla utvecklarverktyg",
|
||||
|
||||
@@ -56,13 +56,14 @@
|
||||
"backend_changed": "Очистити дані та перезавантажити?",
|
||||
"backend_changed_detail": "Не вдається отримати доступ до таємного ключа з системного набору ключів, видається, він змінився.",
|
||||
"backend_changed_title": "Не вдалося завантажити базу даних",
|
||||
"unknown_backend_override": "Ваша система має непідтримуваний ключ, що означає, що базу даних неможливо відкрити.",
|
||||
"unknown_backend_override_details": "Перегляньте журнал, щоб дізнатися подробиці.",
|
||||
"unknown_backend_override_title": "Не вдалося завантажити базу даних",
|
||||
"backend_no_encryption": "Ваша система підтримує сховище ключів, але шифрування недоступне.",
|
||||
"backend_no_encryption_detail": "Electron виявив, що шифрування недоступне у вашому сховищі ключів %(backend)s. Переконайтеся, що у вас встановлено сховище ключів. Якщо так, тоді перезавантажте комп'ютер і повторіть спробу. За бажанням, ви можете дозволити %(brand)s використовувати слабшу форму шифрування.",
|
||||
"backend_no_encryption_title": "Шифрування не підтримується",
|
||||
"unsupported_keyring": "Ваша система має непідтримуваний набір ключів. Це означає, що базу даних неможливо відкрити.",
|
||||
"unsupported_keyring_cta": "Використовувати слабше шифрування",
|
||||
"unsupported_keyring_detail": "Electron не виявив підтримуваного бекенда для роботи зі сховищем паролів. Ви можете вручну налаштувати його, запустивши %(brand)s з відповідним аргументом у командному рядку. Цю дію потрібно виконати лише один раз. Докладніше – %(link)s.",
|
||||
"unsupported_keyring_title": "Система не підтримується"
|
||||
"unsupported_keyring_title": "Система не підтримується",
|
||||
"unsupported_keyring_use_basic_text": "Використовувати слабше шифрування",
|
||||
"unsupported_keyring_use_plaintext": "Не використовувати шифрування"
|
||||
}
|
||||
},
|
||||
"view_menu": {
|
||||
|
||||
26
src/ipc.ts
26
src/ipc.ts
@@ -8,9 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import { app, autoUpdater, desktopCapturer, ipcMain, powerSaveBlocker, TouchBar, nativeImage } from "electron";
|
||||
|
||||
import IpcMainEvent = Electron.IpcMainEvent;
|
||||
import { recordSSOSession } from "./protocol.js";
|
||||
import { randomArray } from "./utils.js";
|
||||
import { Settings } from "./settings.js";
|
||||
import { getDisplayMediaCallback, setDisplayMediaCallback } from "./displayMediaCallback.js";
|
||||
import Store, { clearDataAndRelaunch } from "./store.js";
|
||||
|
||||
@@ -70,18 +68,6 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
|
||||
case "getUpdateFeedUrl":
|
||||
ret = autoUpdater.getFeedURL();
|
||||
break;
|
||||
case "getSettingValue": {
|
||||
const [settingName] = args;
|
||||
const setting = Settings[settingName];
|
||||
ret = await setting.read();
|
||||
break;
|
||||
}
|
||||
case "setSettingValue": {
|
||||
const [settingName, value] = args;
|
||||
const setting = Settings[settingName];
|
||||
await setting.write(value);
|
||||
break;
|
||||
}
|
||||
case "setLanguage":
|
||||
global.appLocalization.setAppLocale(args[0]);
|
||||
break;
|
||||
@@ -96,9 +82,7 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
|
||||
global.mainWindow.focus();
|
||||
}
|
||||
break;
|
||||
case "getConfig":
|
||||
ret = global.vectorConfig;
|
||||
break;
|
||||
|
||||
case "navigateBack":
|
||||
if (global.mainWindow.webContents.canGoBack()) {
|
||||
global.mainWindow.webContents.goBack();
|
||||
@@ -135,10 +119,6 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
|
||||
ret = global.mainWindow.webContents.session.availableSpellCheckerLanguages;
|
||||
break;
|
||||
|
||||
case "startSSOFlow":
|
||||
recordSSOSession(args[0]);
|
||||
break;
|
||||
|
||||
case "getPickleKey":
|
||||
try {
|
||||
ret = await store.getSecret(`${args[0]}|${args[1]}`);
|
||||
@@ -181,7 +161,7 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
|
||||
break;
|
||||
|
||||
case "clearStorage":
|
||||
await clearDataAndRelaunch();
|
||||
await clearDataAndRelaunch(global.mainWindow.webContents.session);
|
||||
return; // the app is about to stop, we don't need to reply to the IPC
|
||||
|
||||
case "breadcrumbs": {
|
||||
@@ -248,3 +228,5 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
|
||||
reply: ret,
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.handle("getConfig", () => global.vectorConfig);
|
||||
|
||||
@@ -49,4 +49,25 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
}
|
||||
ipcRenderer.send(channel, ...args);
|
||||
},
|
||||
|
||||
async initialise(): Promise<{
|
||||
protocol: string;
|
||||
sessionId: string;
|
||||
config: IConfigOptions;
|
||||
supportedSettings: Record<string, boolean>;
|
||||
}> {
|
||||
const [{ protocol, sessionId }, config, supportedSettings] = await Promise.all([
|
||||
ipcRenderer.invoke("getProtocol"),
|
||||
ipcRenderer.invoke("getConfig"),
|
||||
ipcRenderer.invoke("getSupportedSettings"),
|
||||
]);
|
||||
return { protocol, sessionId, config, supportedSettings };
|
||||
},
|
||||
|
||||
async setSettingValue(settingName: string, value: any): Promise<void> {
|
||||
return ipcRenderer.invoke("setSettingValue", settingName, value);
|
||||
},
|
||||
async getSettingValue(settingName: string): Promise<any> {
|
||||
return ipcRenderer.invoke("getSettingValue", settingName);
|
||||
},
|
||||
});
|
||||
|
||||
199
src/protocol.ts
199
src/protocol.ts
@@ -6,119 +6,136 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { app } from "electron";
|
||||
import { app, ipcMain } from "electron";
|
||||
import { URL } from "node:url";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
const LEGACY_PROTOCOL = "element";
|
||||
const PROTOCOL = "io.element.desktop";
|
||||
const SEARCH_PARAM = "element-desktop-ssoid";
|
||||
const STORE_FILE_NAME = "sso-sessions.json";
|
||||
|
||||
// we getPath userData before electron-main changes it, so this is the default value
|
||||
const storePath = path.join(app.getPath("userData"), STORE_FILE_NAME);
|
||||
|
||||
function processUrl(url: string): void {
|
||||
if (!global.mainWindow) return;
|
||||
export default class ProtocolHandler {
|
||||
private readonly store: Record<string, string> = {};
|
||||
private readonly sessionId: string;
|
||||
|
||||
const parsed = new URL(url);
|
||||
// sanity check: we only register for the one protocol, so we shouldn't
|
||||
// be getting anything else unless the user is forcing a URL to open
|
||||
// with the Element app.
|
||||
if (parsed.protocol !== `${PROTOCOL}:` && parsed.protocol !== `${LEGACY_PROTOCOL}:`) {
|
||||
console.log("Ignoring unexpected protocol: ", parsed.protocol);
|
||||
return;
|
||||
public constructor(private readonly protocol: string) {
|
||||
// get all args except `hidden` as it'd mean the app would not get focused
|
||||
// XXX: passing args to protocol handlers only works on Windows, so unpackaged deep-linking
|
||||
// --profile/--profile-dir are passed via the SEARCH_PARAM var in the callback url
|
||||
const args = process.argv.slice(1).filter((arg) => arg !== "--hidden" && arg !== "-hidden");
|
||||
if (app.isPackaged) {
|
||||
app.setAsDefaultProtocolClient(this.protocol, process.execPath, args);
|
||||
app.setAsDefaultProtocolClient(LEGACY_PROTOCOL, process.execPath, args);
|
||||
} else if (process.platform === "win32") {
|
||||
// on Mac/Linux this would just cause the electron binary to open
|
||||
// special handler for running without being packaged, e.g `electron .` by passing our app path to electron
|
||||
app.setAsDefaultProtocolClient(this.protocol, process.execPath, [app.getAppPath(), ...args]);
|
||||
app.setAsDefaultProtocolClient(LEGACY_PROTOCOL, process.execPath, [app.getAppPath(), ...args]);
|
||||
}
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
// Protocol handler for macos
|
||||
app.on("open-url", (ev, url) => {
|
||||
ev.preventDefault();
|
||||
this.processUrl(url);
|
||||
});
|
||||
} else {
|
||||
// Protocol handler for win32/Linux
|
||||
app.on("second-instance", (ev, commandLine) => {
|
||||
const url = commandLine[commandLine.length - 1];
|
||||
if (!url.startsWith(`${this.protocol}:/`) && !url.startsWith(`${LEGACY_PROTOCOL}://`)) return;
|
||||
this.processUrl(url);
|
||||
});
|
||||
}
|
||||
|
||||
this.store = this.readStore();
|
||||
this.sessionId = randomUUID();
|
||||
|
||||
ipcMain.handle("getProtocol", this.onGetProtocol);
|
||||
}
|
||||
|
||||
const urlToLoad = new URL("vector://vector/webapp/");
|
||||
// ignore anything other than the search (used for SSO login redirect)
|
||||
// and the hash (for general element deep links)
|
||||
// There's no reason to allow anything else, particularly other paths,
|
||||
// since this would allow things like the internal jitsi wrapper to
|
||||
// be loaded, which would get the app stuck on that page and generally
|
||||
// be a bit strange and confusing.
|
||||
urlToLoad.search = parsed.search;
|
||||
urlToLoad.hash = parsed.hash;
|
||||
private readonly onGetProtocol = (): { protocol: string; sessionId: string } => {
|
||||
return {
|
||||
protocol: this.protocol,
|
||||
sessionId: this.sessionId,
|
||||
};
|
||||
};
|
||||
|
||||
console.log("Opening URL: ", urlToLoad.href);
|
||||
void global.mainWindow.loadURL(urlToLoad.href);
|
||||
}
|
||||
private processUrl(url: string): void {
|
||||
if (!global.mainWindow) return;
|
||||
|
||||
function readStore(): Record<string, string> {
|
||||
try {
|
||||
const s = fs.readFileSync(storePath, { encoding: "utf8" });
|
||||
const o = JSON.parse(s);
|
||||
return typeof o === "object" ? o : {};
|
||||
} catch {
|
||||
return {};
|
||||
const parsed = new URL(url);
|
||||
// sanity check: we only register for the one protocol, so we shouldn't
|
||||
// be getting anything else unless the user is forcing a URL to open
|
||||
// with the Element app.
|
||||
if (parsed.protocol !== `${this.protocol}:` && parsed.protocol !== `${LEGACY_PROTOCOL}:`) {
|
||||
console.log("Ignoring unexpected protocol: ", parsed.protocol);
|
||||
return;
|
||||
}
|
||||
|
||||
const urlToLoad = new URL("vector://vector/webapp/");
|
||||
// ignore anything other than the search (used for SSO login redirect)
|
||||
// and the hash (for general element deep links)
|
||||
// There's no reason to allow anything else, particularly other paths,
|
||||
// since this would allow things like the internal jitsi wrapper to
|
||||
// be loaded, which would get the app stuck on that page and generally
|
||||
// be a bit strange and confusing.
|
||||
urlToLoad.search = parsed.search;
|
||||
urlToLoad.hash = parsed.hash;
|
||||
|
||||
console.log("Opening URL: ", urlToLoad.href);
|
||||
void global.mainWindow.loadURL(urlToLoad.href);
|
||||
}
|
||||
}
|
||||
|
||||
function writeStore(data: Record<string, string>): void {
|
||||
fs.writeFileSync(storePath, JSON.stringify(data));
|
||||
}
|
||||
|
||||
export function recordSSOSession(sessionID: string): void {
|
||||
const userDataPath = app.getPath("userData");
|
||||
const store = readStore();
|
||||
for (const key in store) {
|
||||
// ensure each instance only has one (the latest) session ID to prevent the file growing unbounded
|
||||
if (store[key] === userDataPath) {
|
||||
delete store[key];
|
||||
break;
|
||||
private readStore(): Record<string, string> {
|
||||
try {
|
||||
const s = fs.readFileSync(storePath, { encoding: "utf8" });
|
||||
const o = JSON.parse(s);
|
||||
return typeof o === "object" ? o : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
store[sessionID] = userDataPath;
|
||||
writeStore(store);
|
||||
}
|
||||
|
||||
export function getProfileFromDeeplink(args: string[]): string | undefined {
|
||||
// check if we are passed a profile in the SSO callback url
|
||||
const deeplinkUrl = args.find((arg) => arg.startsWith(`${PROTOCOL}://`) || arg.startsWith(`${LEGACY_PROTOCOL}://`));
|
||||
if (deeplinkUrl?.includes(SEARCH_PARAM)) {
|
||||
const parsedUrl = new URL(deeplinkUrl);
|
||||
if (parsedUrl.protocol === `${PROTOCOL}:` || parsedUrl.protocol === `${LEGACY_PROTOCOL}:`) {
|
||||
const store = readStore();
|
||||
let ssoID = parsedUrl.searchParams.get(SEARCH_PARAM);
|
||||
if (!ssoID) {
|
||||
// In OIDC, we must shuttle the value in the `state` param rather than `element-desktop-ssoid`
|
||||
// We encode it as a suffix like `:element-desktop-ssoid:XXYYZZ`
|
||||
ssoID = parsedUrl.searchParams.get("state")!.split(`:${SEARCH_PARAM}:`)[1];
|
||||
private writeStore(): void {
|
||||
fs.writeFileSync(storePath, JSON.stringify(this.store));
|
||||
}
|
||||
|
||||
public initialise(userDataPath: string): void {
|
||||
for (const key in this.store) {
|
||||
// ensure each instance only has one (the latest) session ID to prevent the file growing unbounded
|
||||
if (this.store[key] === userDataPath) {
|
||||
delete this.store[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.store[this.sessionId] = userDataPath;
|
||||
this.writeStore();
|
||||
}
|
||||
|
||||
public getProfileFromDeeplink(args: string[]): string | undefined {
|
||||
// check if we are passed a profile in the SSO callback url
|
||||
const deeplinkUrl = args.find(
|
||||
(arg) => arg.startsWith(`${this.protocol}:/`) || arg.startsWith(`${LEGACY_PROTOCOL}://`),
|
||||
);
|
||||
if (deeplinkUrl?.includes(SEARCH_PARAM)) {
|
||||
const parsedUrl = new URL(deeplinkUrl);
|
||||
if (parsedUrl.protocol === `${this.protocol}:` || parsedUrl.protocol === `${LEGACY_PROTOCOL}:`) {
|
||||
const store = this.readStore();
|
||||
let sessionId = parsedUrl.searchParams.get(SEARCH_PARAM);
|
||||
if (!sessionId) {
|
||||
// In OIDC, we must shuttle the value in the `state` param rather than `element-desktop-ssoid`
|
||||
// We encode it as a suffix like `:element-desktop-ssoid:XXYYZZ`
|
||||
sessionId = parsedUrl.searchParams.get("state")!.split(`:${SEARCH_PARAM}:`)[1];
|
||||
}
|
||||
console.log("Forwarding to profile: ", store[sessionId]);
|
||||
return store[sessionId];
|
||||
}
|
||||
console.log("Forwarding to profile: ", store[ssoID]);
|
||||
return store[ssoID];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function protocolInit(): void {
|
||||
// get all args except `hidden` as it'd mean the app would not get focused
|
||||
// XXX: passing args to protocol handlers only works on Windows, so unpackaged deep-linking
|
||||
// --profile/--profile-dir are passed via the SEARCH_PARAM var in the callback url
|
||||
const args = process.argv.slice(1).filter((arg) => arg !== "--hidden" && arg !== "-hidden");
|
||||
if (app.isPackaged) {
|
||||
app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, args);
|
||||
app.setAsDefaultProtocolClient(LEGACY_PROTOCOL, process.execPath, args);
|
||||
} else if (process.platform === "win32") {
|
||||
// on Mac/Linux this would just cause the electron binary to open
|
||||
// special handler for running without being packaged, e.g `electron .` by passing our app path to electron
|
||||
app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, [app.getAppPath(), ...args]);
|
||||
app.setAsDefaultProtocolClient(LEGACY_PROTOCOL, process.execPath, [app.getAppPath(), ...args]);
|
||||
}
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
// Protocol handler for macos
|
||||
app.on("open-url", function (ev, url) {
|
||||
ev.preventDefault();
|
||||
processUrl(url);
|
||||
});
|
||||
} else {
|
||||
// Protocol handler for win32/Linux
|
||||
app.on("second-instance", (ev, commandLine) => {
|
||||
const url = commandLine[commandLine.length - 1];
|
||||
if (!url.startsWith(`${PROTOCOL}:/`) && !url.startsWith(`${LEGACY_PROTOCOL}://`)) return;
|
||||
processUrl(url);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ const seshatDefaultPassphrase = "DEFAULT_PASSPHRASE";
|
||||
async function getOrCreatePassphrase(store: Store, key: string): Promise<string> {
|
||||
try {
|
||||
const storedPassphrase = await store.getSecret(key);
|
||||
if (storedPassphrase !== null) {
|
||||
if (storedPassphrase !== undefined) {
|
||||
return storedPassphrase;
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -5,15 +5,18 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
import * as tray from "./tray.js";
|
||||
import Store from "./store.js";
|
||||
|
||||
interface Setting {
|
||||
read(): Promise<any>;
|
||||
write(value: any): Promise<void>;
|
||||
supported?(): boolean; // if undefined, the setting is always supported
|
||||
}
|
||||
|
||||
export const Settings: Record<string, Setting> = {
|
||||
const Settings: Record<string, Setting> = {
|
||||
"Electron.autoLaunch": {
|
||||
async read(): Promise<any> {
|
||||
return global.launcher.isEnabled();
|
||||
@@ -35,7 +38,10 @@ export const Settings: Record<string, Setting> = {
|
||||
},
|
||||
},
|
||||
"Electron.alwaysShowMenuBar": {
|
||||
// not supported on macOS
|
||||
// This isn't relevant on Mac as Menu bars don't live in the app window
|
||||
supported(): boolean {
|
||||
return process.platform !== "darwin";
|
||||
},
|
||||
async read(): Promise<any> {
|
||||
return !global.mainWindow!.autoHideMenuBar;
|
||||
},
|
||||
@@ -46,7 +52,10 @@ export const Settings: Record<string, Setting> = {
|
||||
},
|
||||
},
|
||||
"Electron.showTrayIcon": {
|
||||
// not supported on macOS
|
||||
// Things other than Mac support tray icons
|
||||
supported(): boolean {
|
||||
return process.platform !== "darwin";
|
||||
},
|
||||
async read(): Promise<any> {
|
||||
return tray.hasTray();
|
||||
},
|
||||
@@ -68,4 +77,43 @@ export const Settings: Record<string, Setting> = {
|
||||
Store.instance?.set("disableHardwareAcceleration", !value);
|
||||
},
|
||||
},
|
||||
"Electron.enableContentProtection": {
|
||||
// Unsupported on Linux https://www.electronjs.org/docs/latest/api/browser-window#winsetcontentprotectionenable-macos-windows
|
||||
// Broken on macOS https://github.com/electron/electron/issues/19880
|
||||
supported(): boolean {
|
||||
return process.platform === "win32";
|
||||
},
|
||||
async read(): Promise<any> {
|
||||
return Store.instance?.get("enableContentProtection");
|
||||
},
|
||||
async write(value: any): Promise<void> {
|
||||
global.mainWindow?.setContentProtection(value);
|
||||
Store.instance?.set("enableContentProtection", value);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
ipcMain.handle("getSupportedSettings", async () => {
|
||||
const supportedSettings: Record<string, boolean> = {};
|
||||
for (const [key, setting] of Object.entries(Settings)) {
|
||||
supportedSettings[key] = setting.supported?.() ?? true;
|
||||
}
|
||||
return supportedSettings;
|
||||
});
|
||||
ipcMain.handle("setSettingValue", async (_ev, settingName: string, value: any) => {
|
||||
const setting = Settings[settingName];
|
||||
if (!setting) {
|
||||
throw new Error(`Unknown setting: ${settingName}`);
|
||||
}
|
||||
console.debug(`Writing setting value for: ${settingName} = ${value}`);
|
||||
await setting.write(value);
|
||||
});
|
||||
ipcMain.handle("getSettingValue", async (_ev, settingName: string) => {
|
||||
const setting = Settings[settingName];
|
||||
if (!setting) {
|
||||
throw new Error(`Unknown setting: ${settingName}`);
|
||||
}
|
||||
const value = await setting.read();
|
||||
console.debug(`Reading setting value for: ${settingName} = ${value}`);
|
||||
return value;
|
||||
});
|
||||
|
||||
310
src/store.ts
310
src/store.ts
@@ -16,8 +16,7 @@ limitations under the License.
|
||||
|
||||
import ElectronStore from "electron-store";
|
||||
import keytar from "keytar-forked";
|
||||
import { app, safeStorage, dialog, type SafeStorage } from "electron";
|
||||
import { clearAllUserData, relaunchApp } from "@standardnotes/electron-clear-data";
|
||||
import { app, safeStorage, dialog, type SafeStorage, type Session } from "electron";
|
||||
|
||||
import { _t } from "./language-helper.js";
|
||||
|
||||
@@ -41,28 +40,37 @@ const LEGACY_KEYTAR_SERVICE = "riot.im";
|
||||
* + All other backends are linux-specific and are encrypted using the keychain
|
||||
*/
|
||||
type SafeStorageBackend = ReturnType<SafeStorage["getSelectedStorageBackend"]> | "system" | "plaintext";
|
||||
/**
|
||||
* The "unknown" backend is not a valid backend, so we exclude it from the type.
|
||||
*/
|
||||
type SaneSafeStorageBackend = Exclude<SafeStorageBackend, "unknown">;
|
||||
|
||||
/**
|
||||
* Map of safeStorage backends to their command line arguments.
|
||||
* kwallet6 cannot be specified via command line
|
||||
* https://www.electronjs.org/docs/latest/api/safe-storage#safestoragegetselectedstoragebackend-linux
|
||||
*/
|
||||
const safeStorageBackendMap: Omit<
|
||||
Record<SafeStorageBackend, string>,
|
||||
"unknown" | "kwallet6" | "system" | "plaintext"
|
||||
> = {
|
||||
const safeStorageBackendMap: Omit<Record<SaneSafeStorageBackend, string>, "system" | "plaintext"> = {
|
||||
basic_text: "basic",
|
||||
gnome_libsecret: "gnome-libsecret",
|
||||
kwallet: "kwallet",
|
||||
kwallet5: "kwallet5",
|
||||
kwallet6: "kwallet6",
|
||||
};
|
||||
|
||||
function relaunchApp(): void {
|
||||
console.info("Relaunching app...");
|
||||
app.relaunch();
|
||||
app.exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all data and relaunch the app.
|
||||
*/
|
||||
export async function clearDataAndRelaunch(): Promise<void> {
|
||||
export async function clearDataAndRelaunch(electronSession: Session): Promise<void> {
|
||||
Store.instance?.clear();
|
||||
clearAllUserData();
|
||||
electronSession.flushStorageData();
|
||||
await electronSession.clearStorageData();
|
||||
relaunchApp();
|
||||
}
|
||||
|
||||
@@ -73,6 +81,7 @@ interface StoreData {
|
||||
autoHideMenuBar: boolean;
|
||||
locale?: string | string[];
|
||||
disableHardwareAcceleration: boolean;
|
||||
enableContentProtection: boolean;
|
||||
safeStorage?: Record<string, string>;
|
||||
/** the safeStorage backend used for the safeStorage data as written */
|
||||
safeStorageBackend?: SafeStorageBackend;
|
||||
@@ -85,7 +94,7 @@ interface StoreData {
|
||||
/**
|
||||
* Fallback storage writer for secrets, mainly used for automated tests and systems without any safeStorage support.
|
||||
*/
|
||||
class PlaintextStorageWriter {
|
||||
class StorageWriter {
|
||||
public constructor(protected readonly store: ElectronStore<StoreData>) {}
|
||||
|
||||
public getKey(key: string): `safeStorage.${string}` {
|
||||
@@ -96,7 +105,7 @@ class PlaintextStorageWriter {
|
||||
this.store.set(this.getKey(key), secret);
|
||||
}
|
||||
|
||||
public get(key: string): string | null {
|
||||
public get(key: string): string | undefined {
|
||||
return this.store.get(this.getKey(key));
|
||||
}
|
||||
|
||||
@@ -108,12 +117,12 @@ class PlaintextStorageWriter {
|
||||
/**
|
||||
* Storage writer for secrets using safeStorage.
|
||||
*/
|
||||
class SafeStorageWriter extends PlaintextStorageWriter {
|
||||
class SafeStorageWriter extends StorageWriter {
|
||||
public set(key: string, secret: string): void {
|
||||
this.store.set(this.getKey(key), safeStorage.encryptString(secret).toString("base64"));
|
||||
}
|
||||
|
||||
public get(key: string): string | null {
|
||||
public get(key: string): string | undefined {
|
||||
const ciphertext = this.store.get<string, string | undefined>(this.getKey(key));
|
||||
if (ciphertext) {
|
||||
try {
|
||||
@@ -123,7 +132,7 @@ class SafeStorageWriter extends PlaintextStorageWriter {
|
||||
console.error("...ciphertext:", JSON.stringify(ciphertext));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +166,10 @@ class Store extends ElectronStore<StoreData> {
|
||||
const store = new Store(mode ?? Mode.Encrypted);
|
||||
Store.internalInstance = store;
|
||||
|
||||
if (process.platform === "linux" && store.get("safeStorageBackendOverride")) {
|
||||
if (
|
||||
process.platform === "linux" &&
|
||||
(store.get("safeStorageBackendOverride") || store.get("safeStorageBackendMigrate"))
|
||||
) {
|
||||
const backend = store.get("safeStorageBackend")!;
|
||||
if (backend in safeStorageBackendMap) {
|
||||
// If the safeStorage backend which was used to write the data is one we can specify via the commandLine
|
||||
@@ -174,7 +186,7 @@ class Store extends ElectronStore<StoreData> {
|
||||
|
||||
// Provides "raw" access to the underlying secrets storage,
|
||||
// should be avoided in favour of the getSecret/setSecret/deleteSecret methods.
|
||||
private secrets?: PlaintextStorageWriter | SafeStorageWriter;
|
||||
private secrets?: StorageWriter;
|
||||
|
||||
private constructor(private mode: Mode) {
|
||||
super({
|
||||
@@ -204,6 +216,10 @@ class Store extends ElectronStore<StoreData> {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
enableContentProtection: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
safeStorage: {
|
||||
type: "object",
|
||||
},
|
||||
@@ -220,117 +236,179 @@ class Store extends ElectronStore<StoreData> {
|
||||
});
|
||||
}
|
||||
|
||||
private safeStorageReadyPromise?: Promise<unknown>;
|
||||
private safeStorageReadyPromise?: Promise<boolean>;
|
||||
public async safeStorageReady(): Promise<void> {
|
||||
if (!this.safeStorageReadyPromise) {
|
||||
this.safeStorageReadyPromise = this.prepareSafeStorage();
|
||||
throw new Error("prepareSafeStorage must be called before using storage methods");
|
||||
}
|
||||
await this.safeStorageReadyPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalise the backend to a sane value (exclude `unknown`), respect forcePlaintext mode,
|
||||
* and ensure that if an encrypted backend is picked that encryption is available, falling back to plaintext if not.
|
||||
* @param forcePlaintext - whether to force plaintext mode
|
||||
* @private
|
||||
*/
|
||||
private chooseBackend(forcePlaintext: boolean): SaneSafeStorageBackend {
|
||||
if (forcePlaintext) {
|
||||
return "plaintext";
|
||||
}
|
||||
|
||||
if (process.platform === "linux") {
|
||||
// The following enables plain text encryption if the backend used is basic_text.
|
||||
// It has no significance for any other backend.
|
||||
// We do this early so that in case we end up using the basic_text backend (either because that's the only one available
|
||||
// or as a fallback when the configured backend lacks encryption support), encryption is already turned on.
|
||||
safeStorage.setUsePlainTextEncryption(true);
|
||||
|
||||
// Linux safeStorage support is hellish, the support varies on the Desktop Environment used rather than the store itself.
|
||||
// https://github.com/electron/electron/issues/39789 https://github.com/microsoft/vscode/issues/185212
|
||||
const selectedBackend = safeStorage.getSelectedStorageBackend();
|
||||
|
||||
if (selectedBackend === "unknown" || !safeStorage.isEncryptionAvailable()) {
|
||||
return "plaintext";
|
||||
}
|
||||
return selectedBackend;
|
||||
}
|
||||
|
||||
return safeStorage.isEncryptionAvailable() ? "system" : "plaintext";
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the safeStorage backend for use.
|
||||
* We don't eagerly import from keytar as that would bring in data for all Element profiles and not just the current one,
|
||||
* so we import lazily in getSecret.
|
||||
*
|
||||
* This will relaunch the app in some cases, in which case it will return false and the caller should abort startup.
|
||||
*
|
||||
* @param electronSession - The Electron session to use for storage (will be used to clear storage if necessary).
|
||||
* @returns true if safeStorage was initialised successfully or false if the app will be relaunched
|
||||
*/
|
||||
private async prepareSafeStorage(): Promise<void> {
|
||||
public async prepareSafeStorage(electronSession: Session): Promise<boolean> {
|
||||
this.safeStorageReadyPromise = this.reallyPrepareSafeStorage(electronSession);
|
||||
return this.safeStorageReadyPromise;
|
||||
}
|
||||
|
||||
private async reallyPrepareSafeStorage(electronSession: Session): Promise<boolean> {
|
||||
await app.whenReady();
|
||||
|
||||
let safeStorageBackend = this.get("safeStorageBackend");
|
||||
if (process.platform === "linux") {
|
||||
// Linux safeStorage support is hellish, the support varies on the Desktop Environment used rather than the store itself.
|
||||
// https://github.com/electron/electron/issues/39789 https://github.com/microsoft/vscode/issues/185212
|
||||
const selectedSafeStorageBackend = safeStorage.getSelectedStorageBackend();
|
||||
console.info(
|
||||
`safeStorage backend '${selectedSafeStorageBackend}' selected, '${safeStorageBackend}' in config.`,
|
||||
);
|
||||
// The backend the existing data is written with if any
|
||||
let existingSafeStorageBackend = this.get("safeStorageBackend");
|
||||
// The backend and encryption status of the currently loaded backend
|
||||
const backend = this.chooseBackend(this.mode === Mode.ForcePlaintext);
|
||||
|
||||
if (selectedSafeStorageBackend === "unknown") {
|
||||
// This should never happen but good to be safe
|
||||
await dialog.showMessageBox({
|
||||
title: _t("store|error|unknown_backend_override_title"),
|
||||
message: _t("store|error|unknown_backend_override"),
|
||||
detail: _t("store|error|unknown_backend_override_details"),
|
||||
type: "error",
|
||||
});
|
||||
throw new Error("safeStorage backend unknown");
|
||||
// Handle migrations
|
||||
if (existingSafeStorageBackend) {
|
||||
if (existingSafeStorageBackend === "basic_text" && backend !== "plaintext" && backend !== "basic_text") {
|
||||
this.prepareMigrateBasicTextToPlaintext();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.get("safeStorageBackendMigrate")) {
|
||||
return this.upgradeLinuxBackend2();
|
||||
if (this.get("safeStorageBackendMigrate") && backend === "basic_text") {
|
||||
this.migrateBasicTextToPlaintext();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!safeStorageBackend) {
|
||||
if (selectedSafeStorageBackend === "basic_text" && this.mode === Mode.Encrypted) {
|
||||
const { response } = await dialog.showMessageBox({
|
||||
title: _t("store|error|unsupported_keyring_title"),
|
||||
message: _t("store|error|unsupported_keyring"),
|
||||
detail: _t("store|error|unsupported_keyring_detail", {
|
||||
brand: global.vectorConfig.brand || "Element",
|
||||
link: "https://www.electronjs.org/docs/latest/api/safe-storage#safestoragegetselectedstoragebackend-linux",
|
||||
}),
|
||||
type: "error",
|
||||
buttons: [_t("action|cancel"), _t("store|error|unsupported_keyring_cta")],
|
||||
defaultId: 0,
|
||||
cancelId: 0,
|
||||
});
|
||||
if (response === 0) {
|
||||
throw new Error("safeStorage backend basic_text and user rejected it");
|
||||
}
|
||||
this.mode = Mode.AllowPlaintext;
|
||||
}
|
||||
|
||||
// Store the backend used for the safeStorage data so we can detect if it changes
|
||||
this.recordSafeStorageBackend(selectedSafeStorageBackend);
|
||||
safeStorageBackend = selectedSafeStorageBackend;
|
||||
} else if (safeStorageBackend !== selectedSafeStorageBackend) {
|
||||
console.warn(`safeStorage backend changed from ${safeStorageBackend} to ${selectedSafeStorageBackend}`);
|
||||
|
||||
if (safeStorageBackend === "basic_text") {
|
||||
return this.upgradeLinuxBackend1();
|
||||
} else if (safeStorageBackend === "plaintext") {
|
||||
this.upgradeLinuxBackend3();
|
||||
} else if (safeStorageBackend in safeStorageBackendMap) {
|
||||
this.set("safeStorageBackendOverride", true);
|
||||
relaunchApp();
|
||||
return;
|
||||
} else {
|
||||
// Warn the user that the backend has changed and tell them that we cannot migrate
|
||||
const { response } = await dialog.showMessageBox({
|
||||
title: _t("store|error|backend_changed_title"),
|
||||
message: _t("store|error|backend_changed"),
|
||||
detail: _t("store|error|backend_changed_detail"),
|
||||
type: "question",
|
||||
buttons: [_t("common|no"), _t("common|yes")],
|
||||
defaultId: 0,
|
||||
cancelId: 0,
|
||||
});
|
||||
if (response === 0) {
|
||||
throw new Error("safeStorage backend changed and cannot migrate");
|
||||
}
|
||||
await clearDataAndRelaunch();
|
||||
}
|
||||
if (existingSafeStorageBackend === "plaintext" && backend !== "plaintext") {
|
||||
this.migratePlaintextToEncrypted();
|
||||
// Ensure we update existingSafeStorageBackend so we don't fall into the "backend changed" clause below
|
||||
existingSafeStorageBackend = this.get("safeStorageBackend");
|
||||
}
|
||||
|
||||
// We do not check allowPlaintextStorage here as it was already checked above if the storage is new
|
||||
// and if the storage is existing then we should continue to honour the backend used to write the data
|
||||
if (safeStorageBackend === "basic_text" && selectedSafeStorageBackend === safeStorageBackend) {
|
||||
safeStorage.setUsePlainTextEncryption(true);
|
||||
}
|
||||
} else if (!safeStorageBackend) {
|
||||
safeStorageBackend = this.mode === Mode.Encrypted ? "system" : "plaintext";
|
||||
this.recordSafeStorageBackend(safeStorageBackend);
|
||||
}
|
||||
|
||||
if (this.mode !== Mode.ForcePlaintext && safeStorage.isEncryptionAvailable()) {
|
||||
if (!existingSafeStorageBackend) {
|
||||
// First launch of the app or first launch since the update
|
||||
if (this.mode === Mode.Encrypted && (backend === "plaintext" || backend === "basic_text")) {
|
||||
// Ask the user for consent to use a degraded mode
|
||||
await this.consultUserConsentDegradedMode(backend);
|
||||
}
|
||||
// Store the backend used for the safeStorage data so we can detect if it changes, and we know how the data is encoded
|
||||
this.recordSafeStorageBackend(backend);
|
||||
} else if (existingSafeStorageBackend !== backend) {
|
||||
// We already appear to have started using a backend other than the one that we picked, so
|
||||
// set the override flag and relaunch with the backend we were previously using, unless we
|
||||
// already have the override flag, in which case we must assume the previous backend is no
|
||||
// longer usable, in which case we should fall into the next block and warn the user we can't
|
||||
// migrate.
|
||||
console.warn(`safeStorage backend changed from ${existingSafeStorageBackend} to ${backend}`);
|
||||
|
||||
if (existingSafeStorageBackend in safeStorageBackendMap && !this.get("safeStorageBackendOverride")) {
|
||||
this.set("safeStorageBackendOverride", true);
|
||||
relaunchApp();
|
||||
return false;
|
||||
} else {
|
||||
// This will either relaunch the app or throw an execption
|
||||
await this.consultUserBackendChangedUnableToMigrate(electronSession);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
console.info(`Using storage mode '${this.mode}' with backend '${backend}'`);
|
||||
if (backend !== "plaintext") {
|
||||
this.secrets = new SafeStorageWriter(this);
|
||||
} else if (this.mode !== Mode.Encrypted) {
|
||||
this.secrets = new PlaintextStorageWriter(this);
|
||||
} else {
|
||||
throw new Error(`safeStorage is not available`);
|
||||
this.secrets = new StorageWriter(this);
|
||||
}
|
||||
|
||||
console.info(`Using storage mode '${this.mode}' with backend '${safeStorageBackend}'`);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async consultUserBackendChangedUnableToMigrate(electronSession: Session): Promise<void> {
|
||||
const { response } = await dialog.showMessageBox({
|
||||
title: _t("store|error|backend_changed_title"),
|
||||
message: _t("store|error|backend_changed"),
|
||||
detail: _t("store|error|backend_changed_detail"),
|
||||
type: "question",
|
||||
buttons: [_t("common|no"), _t("common|yes")],
|
||||
defaultId: 0,
|
||||
cancelId: 0,
|
||||
});
|
||||
if (response === 0) {
|
||||
throw new Error("safeStorage backend changed and cannot migrate");
|
||||
}
|
||||
return clearDataAndRelaunch(electronSession);
|
||||
}
|
||||
|
||||
private async consultUserConsentDegradedMode(backend: "plaintext" | "basic_text"): Promise<void> {
|
||||
if (backend === "plaintext") {
|
||||
// Sometimes we may have a working backend that for some reason does not support encryption at the moment.
|
||||
// This may be because electron reported an incorrect backend or because of some known issues with the keyring itself.
|
||||
// Or the environment specified `--storage-mode=force-plaintext`.
|
||||
// In any case, when this happens, we give the user an option to use a weaker form of encryption.
|
||||
const { response } = await dialog.showMessageBox({
|
||||
title: _t("store|error|backend_no_encryption_title"),
|
||||
message: _t("store|error|backend_no_encryption"),
|
||||
detail: _t("store|error|backend_no_encryption_detail", {
|
||||
backend: safeStorage.getSelectedStorageBackend(),
|
||||
brand: global.vectorConfig.brand || "Element",
|
||||
}),
|
||||
type: "error",
|
||||
buttons: [_t("action|cancel"), _t("store|error|unsupported_keyring_use_plaintext")],
|
||||
defaultId: 0,
|
||||
cancelId: 0,
|
||||
});
|
||||
if (response === 0) {
|
||||
throw new Error("isEncryptionAvailable=false and user rejected plaintext");
|
||||
}
|
||||
} else {
|
||||
// Electron did not identify a compatible encrypted backend, ask user for consent to degraded mode
|
||||
const { response } = await dialog.showMessageBox({
|
||||
title: _t("store|error|unsupported_keyring_title"),
|
||||
message: _t("store|error|unsupported_keyring"),
|
||||
detail: _t("store|error|unsupported_keyring_detail", {
|
||||
brand: global.vectorConfig.brand || "Element",
|
||||
link: "https://www.electronjs.org/docs/latest/api/safe-storage#safestoragegetselectedstoragebackend-linux",
|
||||
}),
|
||||
type: "error",
|
||||
buttons: [_t("action|cancel"), _t("store|error|unsupported_keyring_use_basic_text")],
|
||||
defaultId: 0,
|
||||
cancelId: 0,
|
||||
});
|
||||
if (response === 0) {
|
||||
throw new Error("safeStorage backend basic_text and user rejected it");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private recordSafeStorageBackend(backend: SafeStorageBackend): void {
|
||||
@@ -340,35 +418,37 @@ class Store extends ElectronStore<StoreData> {
|
||||
/**
|
||||
* Linux support for upgrading the backend from basic_text to one of the encrypted backends,
|
||||
* this is quite a tricky process as the backend is not known until the app is ready & cannot be changed once it is.
|
||||
* First we restart the app in basic_text backend mode, then decrypt the data & restart back in default backend mode,
|
||||
* and re-encrypt the data.
|
||||
* 1. We restart the app in safeStorageBackendMigrate mode
|
||||
* 2. Now that we are in the mode which our data is written in we decrypt the data, write it back in plaintext
|
||||
* & restart back in default backend mode,
|
||||
* 3. Finally, we load the plaintext data & encrypt it.
|
||||
*/
|
||||
private upgradeLinuxBackend1(): void {
|
||||
private prepareMigrateBasicTextToPlaintext(): void {
|
||||
console.info(`Starting safeStorage migration to ${safeStorage.getSelectedStorageBackend()}`);
|
||||
this.set("safeStorageBackendMigrate", true);
|
||||
relaunchApp();
|
||||
}
|
||||
private upgradeLinuxBackend2(): void {
|
||||
if (!this.secrets) throw new Error("safeStorage not ready");
|
||||
private migrateBasicTextToPlaintext(): void {
|
||||
const secrets = new SafeStorageWriter(this);
|
||||
console.info("Performing safeStorage migration");
|
||||
const data = this.get("safeStorage");
|
||||
if (data) {
|
||||
for (const key in data) {
|
||||
this.set(this.secrets.getKey(key), this.secrets!.get(key));
|
||||
this.set(secrets.getKey(key), secrets.get(key));
|
||||
}
|
||||
this.recordSafeStorageBackend("plaintext");
|
||||
}
|
||||
this.set("safeStorageBackendMigrate", false);
|
||||
this.delete("safeStorageBackendMigrate");
|
||||
relaunchApp();
|
||||
}
|
||||
private upgradeLinuxBackend3(): void {
|
||||
if (!this.secrets) throw new Error("safeStorage not ready");
|
||||
private migratePlaintextToEncrypted(): void {
|
||||
const secrets = new SafeStorageWriter(this);
|
||||
const selectedSafeStorageBackend = safeStorage.getSelectedStorageBackend();
|
||||
console.info(`Finishing safeStorage migration to ${selectedSafeStorageBackend}`);
|
||||
const data = this.get("safeStorage");
|
||||
if (data) {
|
||||
for (const key in data) {
|
||||
this.secrets.set(key, data[key]);
|
||||
secrets.set(key, data[key]);
|
||||
}
|
||||
}
|
||||
this.recordSafeStorageBackend(selectedSafeStorageBackend);
|
||||
@@ -382,7 +462,7 @@ class Store extends ElectronStore<StoreData> {
|
||||
*
|
||||
* @returns A promise for the secret string.
|
||||
*/
|
||||
public async getSecret(key: string): Promise<string | null> {
|
||||
public async getSecret(key: string): Promise<string | undefined> {
|
||||
await this.safeStorageReady();
|
||||
let secret = this.secrets!.get(key);
|
||||
if (secret) return secret;
|
||||
@@ -438,9 +518,11 @@ class Store extends ElectronStore<StoreData> {
|
||||
/**
|
||||
* @deprecated will be removed in the near future
|
||||
*/
|
||||
private async getSecretKeytar(key: string): Promise<string | null> {
|
||||
private async getSecretKeytar(key: string): Promise<string | undefined> {
|
||||
return (
|
||||
(await keytar.getPassword(KEYTAR_SERVICE, key)) ?? (await keytar.getPassword(LEGACY_KEYTAR_SERVICE, key))
|
||||
(await keytar.getPassword(KEYTAR_SERVICE, key)) ??
|
||||
(await keytar.getPassword(LEGACY_KEYTAR_SERVICE, key)) ??
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export async function randomArray(size: number): Promise<string> {
|
||||
|
||||
type JsonValue = null | string | number;
|
||||
type JsonArray = Array<JsonValue | JsonObject | JsonArray>;
|
||||
interface JsonObject {
|
||||
export interface JsonObject {
|
||||
[key: string]: JsonObject | JsonArray | JsonValue;
|
||||
}
|
||||
export type Json = JsonArray | JsonObject;
|
||||
|
||||
Reference in New Issue
Block a user