Compare commits
177 Commits
374-improv
...
v1.14-pre1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5004a69bff | ||
|
|
9628f2a1b8 | ||
|
|
4cfdcb0f95 | ||
|
|
93d1eb9141 | ||
|
|
96f5953ed1 | ||
|
|
48b21b1006 | ||
|
|
c7cdf5afcb | ||
|
|
6d19a09c20 | ||
|
|
079756a2d7 | ||
|
|
9c43136e18 | ||
|
|
38da94e6dd | ||
|
|
da245f3f88 | ||
|
|
1e18763f8d | ||
|
|
82c3b0546c | ||
|
|
f246726ab7 | ||
|
|
331daa7e12 | ||
|
|
9be215c295 | ||
|
|
bb97a023de | ||
|
|
edb4907bf5 | ||
|
|
a718c7ed7e | ||
|
|
4f11789f26 | ||
|
|
eb15861b13 | ||
|
|
8c2c8049c8 | ||
|
|
43c167073e | ||
|
|
1037e3306c | ||
|
|
08e818d4dc | ||
|
|
1946837277 | ||
|
|
2f3761b0a7 | ||
|
|
260bc8adb2 | ||
|
|
87e979635b | ||
|
|
0c9c163755 | ||
|
|
74ceea562b | ||
|
|
4cd91ed228 | ||
|
|
de4a18dfa1 | ||
|
|
c6a5362ecb | ||
|
|
f2c1dc00a1 | ||
|
|
f655a89be0 | ||
|
|
0d6f837578 | ||
|
|
8c61b028b7 | ||
|
|
a3d5273285 | ||
|
|
93cf4f790c | ||
|
|
cd323c0a22 | ||
|
|
99ca8bf953 | ||
|
|
d40b3dc15c | ||
|
|
057a7e2f7a | ||
|
|
1b73c536d5 | ||
|
|
2593a8548f | ||
|
|
13306a9076 | ||
|
|
cfb5098b38 | ||
|
|
d04d455fbd | ||
|
|
b83c4b3772 | ||
|
|
f03c11381e | ||
|
|
8a03ddb7f3 | ||
|
|
913222d7cb | ||
|
|
3e6d86c206 | ||
|
|
d6ce2a32e9 | ||
|
|
21f1c8404c | ||
|
|
16ff81cf81 | ||
|
|
0636f687ac | ||
|
|
60d8900473 | ||
|
|
4b2d2ef768 | ||
|
|
48899ba9a0 | ||
|
|
092b8689b8 | ||
|
|
3d3ba45cb1 | ||
|
|
b380100307 | ||
|
|
4c5ddd59d8 | ||
|
|
5ed183f318 | ||
|
|
9c27fd3e78 | ||
|
|
62c361feb0 | ||
|
|
0ee2495528 | ||
|
|
7dc635a625 | ||
|
|
e15112c3b4 | ||
|
|
1d85fffb18 | ||
|
|
5e2f29e737 | ||
|
|
89a09ea142 | ||
|
|
628c0d2c19 | ||
|
|
584feabe44 | ||
|
|
ae2cfde897 | ||
|
|
42c66670b8 | ||
|
|
288539b902 | ||
|
|
61fd32f121 | ||
|
|
43108ec4a6 | ||
|
|
51089c6b98 | ||
|
|
cf18fcf91c | ||
|
|
da0513c768 | ||
|
|
37f520cdbe | ||
|
|
c98572bee0 | ||
|
|
b1774ffc4b | ||
|
|
57aaa0c4cd | ||
|
|
b961ae1b33 | ||
|
|
5e418e2b1b | ||
|
|
6d22a213f3 | ||
|
|
a76addc43f | ||
|
|
1d96217713 | ||
|
|
d2b8fdcfff | ||
|
|
426fbc2510 | ||
|
|
e89a961c02 | ||
|
|
766c29b7a9 | ||
|
|
507b671448 | ||
|
|
3118ffaeb5 | ||
|
|
0abe29bd77 | ||
|
|
f3a7831390 | ||
|
|
37cd58f7ba | ||
|
|
7dd80a8ef7 | ||
|
|
c78636264b | ||
|
|
035506a5a3 | ||
|
|
4cf46ef062 | ||
|
|
c7b8063171 | ||
|
|
0a8b149c9a | ||
|
|
9240a27791 | ||
|
|
e90d5b903c | ||
|
|
50b4a9f1b9 | ||
|
|
9783c3b5fe | ||
|
|
7a837e3237 | ||
|
|
c8f6714373 | ||
|
|
bc0313aa6a | ||
|
|
0f98668bcd | ||
|
|
c4206e58bf | ||
|
|
630ededf3b | ||
|
|
8d1195ac96 | ||
|
|
df731ac1b3 | ||
|
|
bd784fa13d | ||
|
|
a6bc5e657c | ||
|
|
56c4cdb321 | ||
|
|
42a4a83c7d | ||
|
|
fe3933e154 | ||
|
|
3efe130ee8 | ||
|
|
5808857749 | ||
|
|
a7397c3316 | ||
|
|
d12f936898 | ||
|
|
67f7d74bb9 | ||
|
|
b0d0f06073 | ||
|
|
67ee571c27 | ||
|
|
a360695271 | ||
|
|
149857a516 | ||
|
|
fb8ffb802f | ||
|
|
e957073457 | ||
|
|
da86b0f50b | ||
|
|
0aa78ffd66 | ||
|
|
ce0087af99 | ||
|
|
576bfeecfe | ||
|
|
c0e2f34b79 | ||
|
|
84d0c32610 | ||
|
|
40184dbd55 | ||
|
|
2d17bdde19 | ||
|
|
e2babde1fa | ||
|
|
e1f26fb045 | ||
|
|
adbbfa0ac1 | ||
|
|
37a013135e | ||
|
|
acc6ea7f85 | ||
|
|
62f012713a | ||
|
|
0d726c1789 | ||
|
|
f162e868b9 | ||
|
|
0a1f95653f | ||
|
|
27451825c6 | ||
|
|
bdd6f1033e | ||
|
|
c0413f9b74 | ||
|
|
59d6fc8fdb | ||
|
|
c500245647 | ||
|
|
026a263f10 | ||
|
|
839e6d3cb4 | ||
|
|
735f4caf89 | ||
|
|
11af71ef82 | ||
|
|
e09577d17f | ||
|
|
c1dbf171f5 | ||
|
|
4ca4ec10be | ||
|
|
77fded4964 | ||
|
|
578491b1c7 | ||
|
|
eee3ffd861 | ||
|
|
89696d7f0d | ||
|
|
a202c76bf0 | ||
|
|
c9936ab76b | ||
|
|
7ac6f7ed51 | ||
|
|
ba7b02cd1e | ||
|
|
aec9441de4 | ||
|
|
5edf42254d | ||
|
|
a51bfb102f |
45
.github/workflows/build.yml
vendored
45
.github/workflows/build.yml
vendored
@@ -78,7 +78,7 @@ jobs:
|
|||||||
|
|
||||||
# - name: Build keepass2android (net)
|
# - name: Build keepass2android (net)
|
||||||
# run: |
|
# run: |
|
||||||
# make msbuild Flavor=Net
|
# make dotnetbuild Flavor=Net
|
||||||
|
|
||||||
# - name: Build APK (net)
|
# - name: Build APK (net)
|
||||||
# run: |
|
# run: |
|
||||||
@@ -96,7 +96,7 @@ jobs:
|
|||||||
|
|
||||||
# - name: Build keepass2android (nonet)
|
# - name: Build keepass2android (nonet)
|
||||||
# run: |
|
# run: |
|
||||||
# make msbuild Flavor=NoNet
|
# make dotnetbuild Flavor=NoNet
|
||||||
|
|
||||||
# - name: Build APK (nonet)
|
# - name: Build APK (nonet)
|
||||||
# run: |
|
# run: |
|
||||||
@@ -212,7 +212,7 @@ jobs:
|
|||||||
|
|
||||||
# - name: Build keepass2android (net)
|
# - name: Build keepass2android (net)
|
||||||
# run: |
|
# run: |
|
||||||
# make msbuild Flavor=Net
|
# make dotnetbuild Flavor=Net
|
||||||
|
|
||||||
# - name: Build APK (net)
|
# - name: Build APK (net)
|
||||||
# run: |
|
# run: |
|
||||||
@@ -230,7 +230,7 @@ jobs:
|
|||||||
|
|
||||||
# - name: Build keepass2android (nonet)
|
# - name: Build keepass2android (nonet)
|
||||||
# run: |
|
# run: |
|
||||||
# make msbuild Flavor=NoNet
|
# make dotnetbuild Flavor=NoNet
|
||||||
|
|
||||||
# - name: Build APK (nonet)
|
# - name: Build APK (nonet)
|
||||||
# run: |
|
# run: |
|
||||||
@@ -279,7 +279,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
minimum-size: 8GB
|
minimum-size: 8GB
|
||||||
|
|
||||||
- name: Add msbuild to PATH
|
- name: Add dotnetbuild to PATH
|
||||||
uses: microsoft/setup-msbuild@v2
|
uses: microsoft/setup-msbuild@v2
|
||||||
# If we want to also have nmake, use this instead
|
# If we want to also have nmake, use this instead
|
||||||
#uses: ilammy/msvc-dev-cmd@v1
|
#uses: ilammy/msvc-dev-cmd@v1
|
||||||
@@ -309,30 +309,49 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
make java
|
make java
|
||||||
|
|
||||||
|
- name: Update dotnet workloads
|
||||||
|
run: |
|
||||||
|
dotnet workload update
|
||||||
|
|
||||||
|
- name: Select the manifest
|
||||||
|
run: |
|
||||||
|
make manifestlink Flavor=Net
|
||||||
|
|
||||||
- name: Install NuGet dependencies (net)
|
- name: Install NuGet dependencies (net)
|
||||||
run: make nuget Flavor=Net
|
run: make nuget Flavor=Net
|
||||||
|
|
||||||
- name: Build keepass2android (net)
|
- name: Build keepass2android (net)
|
||||||
run: |
|
run: |
|
||||||
make msbuild Flavor=Net
|
make dotnetbuild Flavor=Net
|
||||||
|
|
||||||
- name: Build APK (net)
|
- name: Build APK (net)
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
env:
|
||||||
|
DropboxAppKey: ${{ secrets.DROPBOX_APP_KEY }}
|
||||||
|
DropboxAppSecret: ${{ secrets.DROPBOX_APP_SECRET }}
|
||||||
|
DropboxAppFolderAppKey: ${{ secrets.DROPBOX_APP_FOLDER_APP_KEY }}
|
||||||
|
DropboxAppFolderAppSecret: ${{ secrets.DROPBOX_APP_FOLDER_APP_SECRET }}
|
||||||
run: |
|
run: |
|
||||||
make apk Flavor=Net
|
make apk Configuration=Release Flavor=Net
|
||||||
|
|
||||||
- name: Archive production artifacts (net)
|
- name: Archive production artifacts (net)
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: signed APK ('net' built on ${{ github.job }})
|
name: archive APK ('net' built on ${{ github.job }})
|
||||||
path: |
|
path: |
|
||||||
src/keepass2android/bin/*/*-Signed.apk
|
src/keepass2android-app/bin/Release/net9.0-android/publish/*.apk
|
||||||
|
|
||||||
|
- name: Select the manifest
|
||||||
|
run: |
|
||||||
|
make manifestlink Flavor=NoNet
|
||||||
|
|
||||||
- name: Install NuGet dependencies (nonet)
|
- name: Install NuGet dependencies (nonet)
|
||||||
run: make nuget Flavor=NoNet
|
run: make nuget Flavor=NoNet
|
||||||
|
|
||||||
- name: Build keepass2android (nonet)
|
- name: Build keepass2android (nonet)
|
||||||
run: |
|
run: |
|
||||||
make msbuild Flavor=NoNet
|
make dotnetbuild Flavor=NoNet
|
||||||
|
|
||||||
- name: Test Autofill
|
- name: Test Autofill
|
||||||
working-directory: ./src/Kp2aAutofillParser.Tests
|
working-directory: ./src/Kp2aAutofillParser.Tests
|
||||||
run: dotnet test
|
run: dotnet test
|
||||||
@@ -344,9 +363,7 @@ jobs:
|
|||||||
- name: Archive production artifacts (nonet)
|
- name: Archive production artifacts (nonet)
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: signed APK ('nonet' built on ${{ github.job }})
|
name: archive APK ('nonet' built on ${{ github.job }})
|
||||||
path: |
|
path: |
|
||||||
src/keepass2android/bin/*/*-Signed.apk
|
src/keepass2android-app/bin/Release/net9.0-android/publish/*.apk
|
||||||
|
|
||||||
- name: Perform "make distclean"
|
|
||||||
run: make distclean
|
|
||||||
|
|||||||
162
.github/workflows/release.yml
vendored
Normal file
162
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
name: Create keepass2android release
|
||||||
|
env:
|
||||||
|
NAME: 'Release'
|
||||||
|
|
||||||
|
on:
|
||||||
|
# the workflow is always triggered manually. This allows to test the apks
|
||||||
|
# before publishing the release and not having a broken tag in the repo if that test fails.
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build-release:
|
||||||
|
|
||||||
|
runs-on: windows-2022
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
flavor: [Net, NoNet]
|
||||||
|
target: [apk, apk_split]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Extract key store
|
||||||
|
env:
|
||||||
|
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
|
||||||
|
KeyStore: "${{ github.workspace }}/kp2a.keystore"
|
||||||
|
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo $KeyStore
|
||||||
|
echo $KEYSTORE_BASE64 | base64 --decode > $KeyStore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v3
|
||||||
|
|
||||||
|
- name: Cache NuGet packages
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.nuget/packages
|
||||||
|
key: ${{ runner.os }}-nuget-${{ hashFiles('src/**/*.csproj', 'src/**/packages.config') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-nuget-
|
||||||
|
|
||||||
|
# Workaround an issue when building on windows-2022. Error was
|
||||||
|
# D8 : OpenJDK 64-Bit Server VM warning : INFO: os::commit_memory(0x00000000ae400000, 330301440, 0) failed; error='The paging file is too small for this operation to complete' (DOS error/errno=1455) [D:\a\keepass2android\keepass2android\src\keepass2android\keepass2android-app.csproj]
|
||||||
|
# C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Xamarin\Android\Xamarin.Android.D8.targets(81,5): error MSB6006: "java.exe" exited with code 1. [D:\a\keepass2android\keepass2android\src\keepass2android\keepass2android-app.csproj]
|
||||||
|
- name: Configure Pagefile
|
||||||
|
uses: al-cheb/configure-pagefile-action@a3b6ebd6b634da88790d9c58d4b37a7f4a7b8708 # v1.4
|
||||||
|
with:
|
||||||
|
minimum-size: 8GB
|
||||||
|
|
||||||
|
- name: Add msbuild/dotnet to PATH
|
||||||
|
uses: microsoft/setup-msbuild@v2
|
||||||
|
# If we want to also have nmake, use this instead
|
||||||
|
#uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
|
||||||
|
- name: Switch to JDK-17
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: '17'
|
||||||
|
distribution: 'temurin'
|
||||||
|
|
||||||
|
- name: Display java version
|
||||||
|
run: java -version
|
||||||
|
|
||||||
|
- name: Build native dependencies
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
make native
|
||||||
|
|
||||||
|
- name: Build java dependencies
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
make java
|
||||||
|
|
||||||
|
- name: List apks
|
||||||
|
run: find . -type f -name "*.apk"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Update dotnet workloads
|
||||||
|
run: |
|
||||||
|
dotnet workload update
|
||||||
|
|
||||||
|
- name: List apks
|
||||||
|
run: find . -type f -name "*.apk"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
|
||||||
|
- name: Select the manifest
|
||||||
|
run: |
|
||||||
|
make manifestlink Flavor=${{ matrix.flavor }}
|
||||||
|
|
||||||
|
- name: List apks
|
||||||
|
run: find . -type f -name "*.apk"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
|
||||||
|
- name: Install NuGet dependencies
|
||||||
|
run: make nuget Flavor=${{ matrix.flavor }}
|
||||||
|
|
||||||
|
- name: List apks
|
||||||
|
run: find . -type f -name "*.apk"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Build APK (net)
|
||||||
|
env:
|
||||||
|
KeyStore: "${{ github.workspace }}/kp2a.keystore"
|
||||||
|
MyAndroidSigningStorePass: ${{ secrets.KEY_STORE_PASSWORD }}
|
||||||
|
MyAndroidSigningKeyPass: ${{ secrets.KEY_PASSWORD }}
|
||||||
|
DropboxAppKey: ${{ secrets.DROPBOX_APP_KEY }}
|
||||||
|
DropboxAppSecret: ${{ secrets.DROPBOX_APP_SECRET }}
|
||||||
|
DropboxAppFolderAppKey: ${{ secrets.DROPBOX_APP_FOLDER_APP_KEY }}
|
||||||
|
DropboxAppFolderAppSecret: ${{ secrets.DROPBOX_APP_FOLDER_APP_SECRET }}
|
||||||
|
|
||||||
|
run: |
|
||||||
|
make ${{ matrix.target }} Configuration=Release Flavor=${{ matrix.flavor }}
|
||||||
|
|
||||||
|
- name: List apks
|
||||||
|
run: find . -type f -name "*.apk"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Archive production artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: keepass2android_${{ matrix.target }}_${{ matrix.flavor }}
|
||||||
|
# the first line is for "apk" target, the second line is for "apk_split" target
|
||||||
|
path: |
|
||||||
|
src/keepass2android-app/bin/Release/net9.0-android/publish/*.apk
|
||||||
|
src/keepass2android-app/bin/Release/net9.0-android/*/publish/*.apk
|
||||||
|
|
||||||
|
- name: List apks
|
||||||
|
run: find . -type f -name "*.apk"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Rename apks
|
||||||
|
# after updating to .net9, the naming scheme of the output apks has changed. rename them to the old scheme
|
||||||
|
# for consistancy with previous releases
|
||||||
|
run: |
|
||||||
|
for apk in src/keepass2android-app/bin/Release/net9.0-android/android-*/publish/*-Signed.apk; do
|
||||||
|
[ -e "$apk" ] || continue # if glob above doesn't return anything, the loop is still executed once
|
||||||
|
arch=$(basename "$(dirname "$(dirname "$apk")")") # e.g. "android-arm64"
|
||||||
|
base=$(basename "$apk" .apk) # e.g. "keepass2android.keepass2android_nonet-Signed"
|
||||||
|
mv "$apk" "$(dirname "$apk")/${base}-${arch#android-}.apk"
|
||||||
|
done
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: List apks
|
||||||
|
run: find . -type f -name "*.apk"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Upload APK to GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
files: |
|
||||||
|
src/keepass2android-app/bin/Release/net9.0-android/publish/*.apk
|
||||||
|
src/keepass2android-app/bin/Release/net9.0-android/*/publish/*.apk
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -64,7 +64,7 @@ Thumbs.db
|
|||||||
/src/java/android-filechooser/code/projectzip/project.zip
|
/src/java/android-filechooser/code/projectzip/project.zip
|
||||||
/src/java/android-filechooser/code/unused.txt
|
/src/java/android-filechooser/code/unused.txt
|
||||||
|
|
||||||
/src/Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs
|
/src/Kp2aBusinessLogic/Io/DropboxFileStorage.g.cs
|
||||||
|
|
||||||
/src/java/workspace/DriveTest
|
/src/java/workspace/DriveTest
|
||||||
|
|
||||||
|
|||||||
89
Makefile
89
Makefile
@@ -4,10 +4,10 @@
|
|||||||
# This Makefile can be used on both unix-like (use make) & windows (with GNU make)
|
# This Makefile can be used on both unix-like (use make) & windows (with GNU make)
|
||||||
#
|
#
|
||||||
# append the Configuration variable to 'make' call with value to use in '/p:Configuration='
|
# append the Configuration variable to 'make' call with value to use in '/p:Configuration='
|
||||||
# of msbuild command.
|
# of dotnetbuild command.
|
||||||
#
|
#
|
||||||
# append the Flavor variable to 'make' call with value to use in '/p:Flavor='
|
# append the Flavor variable to 'make' call with value to use in '/p:Flavor='
|
||||||
# of msbuild command.
|
# of dotnetbuild command.
|
||||||
#
|
#
|
||||||
# Example:
|
# Example:
|
||||||
# make Configuration=Release Flavor=NoNet
|
# make Configuration=Release Flavor=NoNet
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
# - native: build the native libs
|
# - native: build the native libs
|
||||||
# - java: build the java libs
|
# - java: build the java libs
|
||||||
# - nuget: restore NuGet packages
|
# - nuget: restore NuGet packages
|
||||||
# - msbuild: build the project
|
# - dotnetbuild: build the project
|
||||||
# - apk: same as all
|
# - apk: same as all
|
||||||
# - manifestlink: creates a symlink (to be used in building) to the AndroidManifest corresponding to the selected Flavor
|
# - manifestlink: creates a symlink (to be used in building) to the AndroidManifest corresponding to the selected Flavor
|
||||||
#
|
#
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
# - clean_native: clean native lib
|
# - clean_native: clean native lib
|
||||||
# - clean_java: call clean target of java libs
|
# - clean_java: call clean target of java libs
|
||||||
# - clean_nuget: cleanup the 'nuget restore'
|
# - clean_nuget: cleanup the 'nuget restore'
|
||||||
# - clean_msbuild: call clean target of msbuild
|
# - clean_dotnet: call clean target of dotnetbuild
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -60,45 +60,23 @@ $(info MAKESHELL: $(MAKESHELL))
|
|||||||
$(info SHELL: $(SHELL))
|
$(info SHELL: $(SHELL))
|
||||||
$(info )
|
$(info )
|
||||||
|
|
||||||
# On linux use xabuild, on Windows use MSBuild.exe, otherwise (macos?) use msbuild.
|
|
||||||
ifeq ($(detected_OS),Linux)
|
ifeq ($(detected_OS),Linux)
|
||||||
MSBUILD_binary := xabuild
|
DOTNET_binary := dotnet
|
||||||
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary))
|
DOTNET := $(shell $(WHICH) $(DOTNET_binary))
|
||||||
else ifeq ($(detected_OS),Windows)
|
else ifeq ($(detected_OS),Windows)
|
||||||
MSBUILD_binary := MSBuild.exe
|
DOTNET_binary := dotnet
|
||||||
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary) 2> nul)
|
DOTNET := $(shell $(WHICH) $(DOTNET_binary) 2> nul)
|
||||||
ifeq ($(MSBUILD),)
|
|
||||||
# Additional heuristic to find MSBUILD_BINARY on Windows
|
|
||||||
VSWHERE := "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
|
|
||||||
VSWHERE_CHECK := $(shell @echo off & $(VSWHERE) 2> nul || echo VSWHERE_NOT_FOUND)
|
|
||||||
ifneq ($(VSWHERE_CHECK),VSWHERE_NOT_FOUND)
|
|
||||||
MSBUILD := $(shell @echo off & $(VSWHERE) -latest -prerelease -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe)
|
|
||||||
VS_INSTALL_PATH := $(shell @echo off & $(VSWHERE) -property installationPath)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
else
|
else
|
||||||
MSBUILD_binary := msbuild
|
DOTNET_binary := dotnet
|
||||||
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary))
|
DOTNET := $(shell $(WHICH) $(DOTNET_binary))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(MSBUILD),)
|
ifeq ($(DOTNET),)
|
||||||
$(info )
|
$(info )
|
||||||
$(info '$(MSBUILD_binary)' binary could not be found. Check it is in your PATH.)
|
$(info '$(DOTNET_binary)' binary could not be found. Check it is in your PATH.)
|
||||||
ifeq ($(detected_OS),Windows)
|
|
||||||
ifneq ($(VSWHERE_CHECK),VSWHERE_NOT_FOUND)
|
|
||||||
$(info )
|
|
||||||
$(info You may retry after running in the command prompt:)
|
|
||||||
$(info )
|
|
||||||
$(info "$(VS_INSTALL_PATH)\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64)
|
|
||||||
$(info )
|
|
||||||
$(info If this doesn't work, install/find the location of vcvarsall.bat)
|
|
||||||
$(info or install and add msbuild.exe to your PATH)
|
|
||||||
$(info )
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
$(error )
|
$(error )
|
||||||
endif
|
endif
|
||||||
$(info MSBUILD: $(MSBUILD))
|
$(info DOTNET: $(DOTNET))
|
||||||
$(info )
|
$(info )
|
||||||
|
|
||||||
ifeq ($(ANDROID_SDK_ROOT),)
|
ifeq ($(ANDROID_SDK_ROOT),)
|
||||||
@@ -117,7 +95,7 @@ endif
|
|||||||
$(info ANDROID_NDK_ROOT: $(ANDROID_NDK_ROOT))
|
$(info ANDROID_NDK_ROOT: $(ANDROID_NDK_ROOT))
|
||||||
|
|
||||||
ifneq ($(Configuration),)
|
ifneq ($(Configuration),)
|
||||||
MSBUILD_PARAM = -p:Configuration="$(Configuration)"
|
DOTNET_PARAM = -p:Configuration="$(Configuration)"
|
||||||
else
|
else
|
||||||
$(warning Configuration environment variable not set.)
|
$(warning Configuration environment variable not set.)
|
||||||
endif
|
endif
|
||||||
@@ -127,7 +105,7 @@ CREATE_MANIFEST_LINK :=
|
|||||||
|
|
||||||
MANIFEST_FILE :=
|
MANIFEST_FILE :=
|
||||||
ifneq ($(Flavor),)
|
ifneq ($(Flavor),)
|
||||||
MSBUILD_PARAM += -p:Flavor="$(Flavor)"
|
DOTNET_PARAM += -p:Flavor="$(Flavor)"
|
||||||
ifneq ($(Flavor),)
|
ifneq ($(Flavor),)
|
||||||
ifeq ($(Flavor),Debug)
|
ifeq ($(Flavor),Debug)
|
||||||
MANIFEST_FILE := AndroidManifest_debug.xml
|
MANIFEST_FILE := AndroidManifest_debug.xml
|
||||||
@@ -152,7 +130,7 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(KeyStore),)
|
ifneq ($(KeyStore),)
|
||||||
MSBUILD_PARAM += -p:AndroidKeyStore=True -p:AndroidSigningKeyStore="$(KeyStore)" -p:AndroidSigningStorePass=env:MyAndroidSigningStorePass -p:AndroidSigningKeyPass=env:MyAndroidSigningKeyPass -p:AndroidSigningKeyAlias="kp2a"
|
DOTNET_PARAM += -p:AndroidKeyStore=True -p:AndroidSigningKeyStore="$(KeyStore)" -p:AndroidSigningStorePass=env:MyAndroidSigningStorePass -p:AndroidSigningKeyPass=env:MyAndroidSigningKeyPass -p:AndroidSigningKeyAlias="kp2a"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(detected_OS),Windows)
|
ifeq ($(detected_OS),Windows)
|
||||||
@@ -176,7 +154,7 @@ endif
|
|||||||
# Recursive wildcard: https://stackoverflow.com/a/18258352
|
# Recursive wildcard: https://stackoverflow.com/a/18258352
|
||||||
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
|
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
|
||||||
|
|
||||||
$(info MSBUILD_PARAM: $(MSBUILD_PARAM))
|
$(info DOTNET_PARAM: $(DOTNET_PARAM))
|
||||||
$(info nuget path: $(shell $(WHICH) nuget))
|
$(info nuget path: $(shell $(WHICH) nuget))
|
||||||
$(info )
|
$(info )
|
||||||
|
|
||||||
@@ -254,7 +232,7 @@ OUTPUT_PluginQR = src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/Keepa
|
|||||||
.PHONY: native $(NATIVE_COMPONENTS) clean_native $(NATIVE_CLEAN_TARGETS) \
|
.PHONY: native $(NATIVE_COMPONENTS) clean_native $(NATIVE_CLEAN_TARGETS) \
|
||||||
java $(JAVA_COMPONENTS) clean_java $(JAVA_CLEAN_TARGETS) \
|
java $(JAVA_COMPONENTS) clean_java $(JAVA_CLEAN_TARGETS) \
|
||||||
nuget clean_nuget \
|
nuget clean_nuget \
|
||||||
msbuild clean_msbuild \
|
dotnetbuild clean_dotnet \
|
||||||
apk all clean
|
apk all clean
|
||||||
|
|
||||||
all: apk
|
all: apk
|
||||||
@@ -303,7 +281,7 @@ ifeq ($(shell $(WHICH) nuget),)
|
|||||||
endif
|
endif
|
||||||
$(RMFILE) stamp.nuget_*
|
$(RMFILE) stamp.nuget_*
|
||||||
nuget restore src/KeePass.sln
|
nuget restore src/KeePass.sln
|
||||||
$(MSBUILD) src/KeePass.sln -t:restore $(MSBUILD_PARAM) -p:RestorePackagesConfig=true
|
$(DOTNET) restore src/KeePass.sln $(DOTNET_PARAM) -p:RestorePackagesConfig=true
|
||||||
@echo "" > stamp.nuget_$(Flavor)
|
@echo "" > stamp.nuget_$(Flavor)
|
||||||
|
|
||||||
manifestlink:
|
manifestlink:
|
||||||
@@ -312,20 +290,21 @@ manifestlink:
|
|||||||
$(CREATE_MANIFEST_LINK)
|
$(CREATE_MANIFEST_LINK)
|
||||||
|
|
||||||
#####
|
#####
|
||||||
src/Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs:
|
|
||||||
ifeq ($(detected_OS),Windows)
|
|
||||||
$(CP) src\Kp2aBusinessLogic\Io\DropboxFileStorageKeysDummy.cs src\Kp2aBusinessLogic\Io\DropboxFileStorageKeys.cs
|
|
||||||
else
|
|
||||||
$(CP) src/Kp2aBusinessLogic/Io/DropboxFileStorageKeysDummy.cs $@
|
|
||||||
endif
|
|
||||||
|
|
||||||
msbuild: manifestlink native java nuget src/Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs
|
dotnetbuild: manifestlink native java nuget
|
||||||
$(MSBUILD) src/KeePass.sln -target:keepass2android-app -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -p:BuildProjectReferences=true $(MSBUILD_PARAM) -p:Platform="Any CPU" -m
|
$(DOTNET) build src/KeePass.sln -target:keepass2android-app -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -p:BuildProjectReferences=true $(DOTNET_PARAM) -p:Platform="Any CPU" -m
|
||||||
|
|
||||||
apk: msbuild
|
apk: manifestlink native java nuget
|
||||||
$(MSBUILD) src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(MSBUILD_PARAM) -p:Platform=AnyCPU -m
|
$(DOTNET) publish src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(DOTNET_PARAM) -p:Platform=AnyCPU -m
|
||||||
|
|
||||||
build_all: msbuild
|
apk_split: manifestlink native java nuget
|
||||||
|
$(DOTNET) publish src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(DOTNET_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-arm
|
||||||
|
$(DOTNET) publish src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(DOTNET_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-arm64
|
||||||
|
$(DOTNET) publish src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(DOTNET_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-x86
|
||||||
|
$(DOTNET) publish src/keepass2android-app/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(DOTNET_PARAM) -p:Platform=AnyCPU -m -p:RuntimeIdentifier=android-x64
|
||||||
|
src/build-scripts/rename-output-apks.sh src/keepass2android-app/bin/Release/net8.0-android/
|
||||||
|
|
||||||
|
build_all: dotnetbuild
|
||||||
|
|
||||||
##### Cleanup targets
|
##### Cleanup targets
|
||||||
|
|
||||||
@@ -369,10 +348,10 @@ else
|
|||||||
endif
|
endif
|
||||||
$(RMFILE) stamp.nuget_*
|
$(RMFILE) stamp.nuget_*
|
||||||
|
|
||||||
clean_msbuild:
|
clean_dotnet:
|
||||||
$(MSBUILD) src/KeePass.sln -target:clean $(MSBUILD_PARAM)
|
$(DOTNET) clean src/KeePass.sln $(DOTNET_PARAM)
|
||||||
|
|
||||||
clean: clean_native clean_java clean_nuget clean_msbuild
|
clean: clean_native clean_java clean_nuget clean_dotnet
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
ifneq ("$(wildcard ./allow_git_clean)","")
|
ifneq ("$(wildcard ./allow_git_clean)","")
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ Regular stable releases of Keepass2Android are available on [Google Play](https:
|
|||||||
Beta-releases can be obtained by opting in to the [Beta testing channel](https://play.google.com/apps/testing/keepass2android.keepass2android) or [Beta testing channel for Keepass2Android Offline](https://play.google.com/apps/testing/keepass2android.keepass2android_nonet).
|
Beta-releases can be obtained by opting in to the [Beta testing channel](https://play.google.com/apps/testing/keepass2android.keepass2android) or [Beta testing channel for Keepass2Android Offline](https://play.google.com/apps/testing/keepass2android.keepass2android_nonet).
|
||||||
|
|
||||||
# How can I contribute?
|
# How can I contribute?
|
||||||
* Help to translate Keepass2Android into your language or improve translations at [our Crowdin page](http://crowdin.net/project/keepass2android)
|
* Help to translate Keepass2Android into your language or improve translations at [our Crowdin page](https://crowdin.net/project/keepass2android)
|
||||||
* Add features by [creating a plugin](How-to-create-a-plug-in_.md) or creating a pull request. You might want to contact me before you start working so I can coordinate efforts.
|
* Add features by [creating a plugin](How-to-create-a-plug-in_.md) or creating a pull request. You might want to contact me before you start working so I can coordinate efforts.
|
||||||
* [Become a GitHub sponsor to boost 🚀 development](https://github.com/sponsors/PhilippC)
|
* [Become a GitHub sponsor to boost 🚀 development](https://github.com/sponsors/PhilippC)
|
||||||
* [Make a donation](http://philipp.crocoll.net/donate.php)
|
* [Make a donation](https://philipp.crocoll.net/donate.php)
|
||||||
|
|
||||||
# How do I learn more?
|
# How do I learn more?
|
||||||
Please see the [wiki](https://github.com/PhilippC/keepass2android/wiki/Documentation) for further information.
|
Please see the [wiki](https://github.com/PhilippC/keepass2android/wiki/Documentation) for further information.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
@@ -34,5 +34,6 @@
|
|||||||
<AndroidLibrary Bind="False" Update="okhttp-digest-3.1.0.jar" />
|
<AndroidLibrary Bind="False" Update="okhttp-digest-3.1.0.jar" />
|
||||||
<AndroidLibrary Bind="False" Update="okio-3.6.0.jar" />
|
<AndroidLibrary Bind="False" Update="okio-3.6.0.jar" />
|
||||||
<AndroidLibrary Bind="False" Update="okio-jvm-3.6.0.jar" />
|
<AndroidLibrary Bind="False" Update="okio-jvm-3.6.0.jar" />
|
||||||
|
<AndroidLibrary Bind="False" Update="jsch-2.27.2.jar" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
BIN
src/JavaFileStorageBindings/jsch-2.27.2.jar
Normal file
BIN
src/JavaFileStorageBindings/jsch-2.27.2.jar
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
KeePass Password Safe - The Open-Source Password Manager
|
KeePass Password Safe - The Open-Source Password Manager
|
||||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -29,197 +29,226 @@ using KeePassLib.Utility;
|
|||||||
|
|
||||||
namespace KeePassLib.Cryptography
|
namespace KeePassLib.Cryptography
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Algorithms supported by <c>CryptoRandomStream</c>.
|
/// Algorithms supported by <c>CryptoRandomStream</c>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum CrsAlgorithm
|
public enum CrsAlgorithm
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Not supported.
|
/// Not supported.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Null = 0,
|
Null = 0,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A variant of the ARCFour algorithm (RC4 incompatible).
|
/// A variant of the ArcFour algorithm (RC4 incompatible).
|
||||||
/// </summary>
|
/// Insecure; for backward compatibility only.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ArcFourVariant = 1,
|
ArcFourVariant = 1,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Salsa20 stream cipher algorithm.
|
/// Salsa20 stream cipher algorithm.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Salsa20 = 2,
|
Salsa20 = 2,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ChaCha20 stream cipher algorithm.
|
/// ChaCha20 stream cipher algorithm.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ChaCha20 = 3,
|
ChaCha20 = 3,
|
||||||
|
|
||||||
Count = 4
|
Count = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A random stream class. The class is initialized using random
|
/// A random stream class. The class is initialized using random
|
||||||
/// bytes provided by the caller. The produced stream has random
|
/// bytes provided by the caller. The produced stream has random
|
||||||
/// properties, but for the same seed always the same stream
|
/// properties, but for the same seed always the same stream
|
||||||
/// is produced, i.e. this class can be used as stream cipher.
|
/// is produced, i.e. this class can be used as stream cipher.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class CryptoRandomStream : IDisposable
|
public sealed class CryptoRandomStream : IDisposable
|
||||||
{
|
{
|
||||||
private readonly CrsAlgorithm m_crsAlgorithm;
|
private readonly CrsAlgorithm m_alg;
|
||||||
|
private bool m_bDisposed = false;
|
||||||
|
|
||||||
private byte[] m_pbState = null;
|
private readonly byte[] m_pbKey = null;
|
||||||
private byte m_i = 0;
|
private readonly byte[] m_pbIV = null;
|
||||||
private byte m_j = 0;
|
|
||||||
|
|
||||||
private Salsa20Cipher m_salsa20 = null;
|
private readonly ChaCha20Cipher m_chacha20 = null;
|
||||||
private ChaCha20Cipher m_chacha20 = null;
|
private readonly Salsa20Cipher m_salsa20 = null;
|
||||||
|
|
||||||
/// <summary>
|
private readonly byte[] m_pbState = null;
|
||||||
/// Construct a new cryptographically secure random stream object.
|
private byte m_i = 0;
|
||||||
/// </summary>
|
private byte m_j = 0;
|
||||||
/// <param name="genAlgorithm">Algorithm to use.</param>
|
|
||||||
/// <param name="pbKey">Initialization key. Must not be <c>null</c> and
|
|
||||||
/// must contain at least 1 byte.</param>
|
|
||||||
public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
|
|
||||||
{
|
|
||||||
if(pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
|
|
||||||
/// <exception cref="System.ArgumentNullException">Thrown if the
|
|
||||||
int cbKey = pbKey.Length;
|
|
||||||
if(cbKey <= 0)
|
|
||||||
{
|
|
||||||
Debug.Assert(false); // Need at least one byte
|
|
||||||
throw new ArgumentOutOfRangeException("pbKey");
|
|
||||||
}
|
|
||||||
/// <paramref name="pbKey" /> parameter is <c>null</c>.</exception>
|
|
||||||
m_crsAlgorithm = a;
|
|
||||||
/// <exception cref="System.ArgumentException">Thrown if the
|
|
||||||
if(a == CrsAlgorithm.ChaCha20)
|
|
||||||
{
|
|
||||||
byte[] pbKey32 = new byte[32];
|
|
||||||
byte[] pbIV12 = new byte[12];
|
|
||||||
/// <paramref name="pbKey" /> parameter contains no bytes or the
|
|
||||||
using(SHA512Managed h = new SHA512Managed())
|
|
||||||
{
|
|
||||||
byte[] pbHash = h.ComputeHash(pbKey);
|
|
||||||
Array.Copy(pbHash, pbKey32, 32);
|
|
||||||
Array.Copy(pbHash, 32, pbIV12, 0, 12);
|
|
||||||
MemUtil.ZeroByteArray(pbHash);
|
|
||||||
}
|
|
||||||
/// algorithm is unknown.</exception>
|
|
||||||
m_chacha20 = new ChaCha20Cipher(pbKey32, pbIV12, true);
|
|
||||||
}
|
|
||||||
else if(a == CrsAlgorithm.Salsa20)
|
|
||||||
{
|
|
||||||
byte[] pbKey32 = CryptoUtil.HashSha256(pbKey);
|
|
||||||
byte[] pbIV8 = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
|
|
||||||
0x97, 0x20, 0x5D, 0x2A }; // Unique constant
|
|
||||||
|
|
||||||
m_salsa20 = new Salsa20Cipher(pbKey32, pbIV8);
|
/// <summary>
|
||||||
}
|
/// Construct a new cryptographically secure random stream object.
|
||||||
else if(a == CrsAlgorithm.ArcFourVariant)
|
/// </summary>
|
||||||
{
|
/// <param name="a">Algorithm to use.</param>
|
||||||
// Fill the state linearly
|
/// <param name="pbKey">Initialization key. Must not be <c>null</c>
|
||||||
m_pbState = new byte[256];
|
/// and must contain at least 1 byte.</param>
|
||||||
for(int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
|
public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey)
|
||||||
|
{
|
||||||
|
if (pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); }
|
||||||
|
|
||||||
unchecked
|
int cbKey = pbKey.Length;
|
||||||
{
|
if (cbKey <= 0)
|
||||||
byte j = 0, t;
|
{
|
||||||
int inxKey = 0;
|
Debug.Assert(false); // Need at least one byte
|
||||||
for(int w = 0; w < 256; ++w) // Key setup
|
throw new ArgumentOutOfRangeException("pbKey");
|
||||||
{
|
}
|
||||||
j += (byte)(m_pbState[w] + pbKey[inxKey]);
|
|
||||||
|
|
||||||
t = m_pbState[0]; // Swap entries
|
m_alg = a;
|
||||||
m_pbState[0] = m_pbState[j];
|
|
||||||
m_pbState[j] = t;
|
|
||||||
|
|
||||||
++inxKey;
|
if (a == CrsAlgorithm.ChaCha20)
|
||||||
if(inxKey >= cbKey) inxKey = 0;
|
{
|
||||||
}
|
m_pbKey = new byte[32];
|
||||||
}
|
m_pbIV = new byte[12];
|
||||||
|
|
||||||
GetRandomBytes(512); // Increases security, see cryptanalysis
|
using (SHA512Managed h = new SHA512Managed())
|
||||||
}
|
{
|
||||||
else // Unknown algorithm
|
byte[] pbHash = h.ComputeHash(pbKey);
|
||||||
{
|
Array.Copy(pbHash, m_pbKey, 32);
|
||||||
Debug.Assert(false);
|
Array.Copy(pbHash, 32, m_pbIV, 0, 12);
|
||||||
throw new ArgumentOutOfRangeException("a");
|
MemUtil.ZeroByteArray(pbHash);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
m_chacha20 = new ChaCha20Cipher(m_pbKey, m_pbIV, true);
|
||||||
{
|
}
|
||||||
Dispose(true);
|
else if (a == CrsAlgorithm.Salsa20)
|
||||||
GC.SuppressFinalize(this);
|
{
|
||||||
}
|
m_pbKey = CryptoUtil.HashSha256(pbKey);
|
||||||
|
m_pbIV = new byte[8] { 0xE8, 0x30, 0x09, 0x4B,
|
||||||
|
0x97, 0x20, 0x5D, 0x2A }; // Unique constant
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
m_salsa20 = new Salsa20Cipher(m_pbKey, m_pbIV);
|
||||||
{
|
}
|
||||||
if(disposing)
|
else if (a == CrsAlgorithm.ArcFourVariant)
|
||||||
{
|
{
|
||||||
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
|
// Fill the state linearly
|
||||||
m_chacha20.Dispose();
|
m_pbState = new byte[256];
|
||||||
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
|
for (int w = 0; w < 256; ++w) m_pbState[w] = (byte)w;
|
||||||
m_salsa20.Dispose();
|
|
||||||
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
|
|
||||||
{
|
|
||||||
MemUtil.ZeroByteArray(m_pbState);
|
|
||||||
m_i = 0;
|
|
||||||
m_j = 0;
|
|
||||||
}
|
|
||||||
else { Debug.Assert(false); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
unchecked
|
||||||
/// Get <paramref name="uRequestedCount" /> random bytes.
|
{
|
||||||
/// </summary>
|
byte j = 0, t;
|
||||||
/// <param name="uRequestedCount">Number of random bytes to retrieve.</param>
|
int inxKey = 0;
|
||||||
/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
|
for (int w = 0; w < 256; ++w) // Key setup
|
||||||
public byte[] GetRandomBytes(uint uRequestedCount)
|
{
|
||||||
{
|
j += (byte)(m_pbState[w] + pbKey[inxKey]);
|
||||||
if(uRequestedCount == 0) return MemUtil.EmptyByteArray;
|
|
||||||
|
|
||||||
if(uRequestedCount > (uint)int.MaxValue)
|
t = m_pbState[0]; // Swap entries
|
||||||
throw new ArgumentOutOfRangeException("uRequestedCount");
|
m_pbState[0] = m_pbState[j];
|
||||||
int cb = (int)uRequestedCount;
|
m_pbState[j] = t;
|
||||||
|
|
||||||
byte[] pbRet = new byte[cb];
|
++inxKey;
|
||||||
|
if (inxKey >= cbKey) inxKey = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(m_crsAlgorithm == CrsAlgorithm.ChaCha20)
|
GetRandomBytes(512); // Increases security, see cryptanalysis
|
||||||
m_chacha20.Encrypt(pbRet, 0, cb);
|
}
|
||||||
else if(m_crsAlgorithm == CrsAlgorithm.Salsa20)
|
else // Unknown algorithm
|
||||||
m_salsa20.Encrypt(pbRet, 0, cb);
|
{
|
||||||
else if(m_crsAlgorithm == CrsAlgorithm.ArcFourVariant)
|
Debug.Assert(false);
|
||||||
{
|
throw new ArgumentOutOfRangeException("a");
|
||||||
unchecked
|
}
|
||||||
{
|
}
|
||||||
for(int w = 0; w < cb; ++w)
|
|
||||||
{
|
|
||||||
++m_i;
|
|
||||||
m_j += m_pbState[m_i];
|
|
||||||
|
|
||||||
byte t = m_pbState[m_i]; // Swap entries
|
public void Dispose()
|
||||||
m_pbState[m_i] = m_pbState[m_j];
|
{
|
||||||
m_pbState[m_j] = t;
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
t = (byte)(m_pbState[m_i] + m_pbState[m_j]);
|
private void Dispose(bool disposing)
|
||||||
pbRet[w] = m_pbState[t];
|
{
|
||||||
}
|
if (disposing)
|
||||||
}
|
{
|
||||||
}
|
if (m_alg == CrsAlgorithm.ChaCha20)
|
||||||
else { Debug.Assert(false); }
|
m_chacha20.Dispose();
|
||||||
|
else if (m_alg == CrsAlgorithm.Salsa20)
|
||||||
|
m_salsa20.Dispose();
|
||||||
|
else if (m_alg == CrsAlgorithm.ArcFourVariant)
|
||||||
|
{
|
||||||
|
MemUtil.ZeroByteArray(m_pbState);
|
||||||
|
m_i = 0;
|
||||||
|
m_j = 0;
|
||||||
|
}
|
||||||
|
else { Debug.Assert(false); }
|
||||||
|
|
||||||
return pbRet;
|
if (m_pbKey != null) MemUtil.ZeroByteArray(m_pbKey);
|
||||||
}
|
if (m_pbIV != null) MemUtil.ZeroByteArray(m_pbIV);
|
||||||
|
|
||||||
public ulong GetRandomUInt64()
|
m_bDisposed = true;
|
||||||
{
|
}
|
||||||
byte[] pb = GetRandomBytes(8);
|
}
|
||||||
return MemUtil.BytesToUInt64(pb);
|
|
||||||
}
|
/// <summary>
|
||||||
|
/// Get <paramref name="uRequestedCount" /> random bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uRequestedCount">Number of random bytes to retrieve.</param>
|
||||||
|
/// <returns>Returns <paramref name="uRequestedCount" /> random bytes.</returns>
|
||||||
|
public byte[] GetRandomBytes(uint uRequestedCount)
|
||||||
|
{
|
||||||
|
if (m_bDisposed) throw new ObjectDisposedException(null);
|
||||||
|
|
||||||
|
if (uRequestedCount == 0) return MemUtil.EmptyByteArray;
|
||||||
|
if (uRequestedCount > (uint)int.MaxValue)
|
||||||
|
throw new ArgumentOutOfRangeException("uRequestedCount");
|
||||||
|
int cb = (int)uRequestedCount;
|
||||||
|
|
||||||
|
byte[] pbRet = new byte[cb];
|
||||||
|
|
||||||
|
if (m_alg == CrsAlgorithm.ChaCha20)
|
||||||
|
m_chacha20.Encrypt(pbRet, 0, cb);
|
||||||
|
else if (m_alg == CrsAlgorithm.Salsa20)
|
||||||
|
m_salsa20.Encrypt(pbRet, 0, cb);
|
||||||
|
else if (m_alg == CrsAlgorithm.ArcFourVariant)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
for (int w = 0; w < cb; ++w)
|
||||||
|
{
|
||||||
|
++m_i;
|
||||||
|
m_j += m_pbState[m_i];
|
||||||
|
|
||||||
|
byte t = m_pbState[m_i]; // Swap entries
|
||||||
|
m_pbState[m_i] = m_pbState[m_j];
|
||||||
|
m_pbState[m_j] = t;
|
||||||
|
|
||||||
|
t = (byte)(m_pbState[m_i] + m_pbState[m_j]);
|
||||||
|
pbRet[w] = m_pbState[t];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { Debug.Assert(false); }
|
||||||
|
|
||||||
|
return pbRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong GetRandomUInt64()
|
||||||
|
{
|
||||||
|
byte[] pb = GetRandomBytes(8);
|
||||||
|
return MemUtil.BytesToUInt64(pb);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong GetRandomUInt64(ulong uMaxExcl)
|
||||||
|
{
|
||||||
|
if (uMaxExcl == 0) { Debug.Assert(false); throw new ArgumentOutOfRangeException("uMaxExcl"); }
|
||||||
|
|
||||||
|
ulong uGen, uRem;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
uGen = GetRandomUInt64();
|
||||||
|
uRem = uGen % uMaxExcl;
|
||||||
|
}
|
||||||
|
while ((uGen - uRem) > (ulong.MaxValue - (uMaxExcl - 1UL)));
|
||||||
|
// This ensures that the last number of the block (i.e.
|
||||||
|
// (uGen - uRem) + (uMaxExcl - 1)) is generatable;
|
||||||
|
// for signed longs, overflow to negative number:
|
||||||
|
// while((uGen - uRem) + (uMaxExcl - 1) < 0);
|
||||||
|
|
||||||
|
return uRem;
|
||||||
|
}
|
||||||
|
|
||||||
#if CRSBENCHMARK
|
#if CRSBENCHMARK
|
||||||
public static string Benchmark()
|
public static string Benchmark()
|
||||||
@@ -237,22 +266,21 @@ namespace KeePassLib.Cryptography
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int BenchTime(CrsAlgorithm cra, int nRounds, int nDataSize)
|
private static int BenchTime(CrsAlgorithm a, int nRounds, int cbData)
|
||||||
{
|
{
|
||||||
byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 };
|
byte[] pbKey = new byte[4] { 0x00, 0x01, 0x02, 0x03 };
|
||||||
|
|
||||||
int nStart = Environment.TickCount;
|
int tStart = Environment.TickCount;
|
||||||
for(int i = 0; i < nRounds; ++i)
|
for(int i = 0; i < nRounds; ++i)
|
||||||
{
|
{
|
||||||
using(CryptoRandomStream c = new CryptoRandomStream(cra, pbKey))
|
using(CryptoRandomStream crs = new CryptoRandomStream(a, pbKey))
|
||||||
{
|
{
|
||||||
c.GetRandomBytes((uint)nDataSize);
|
crs.GetRandomBytes((uint)cbData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int nEnd = Environment.TickCount;
|
|
||||||
|
|
||||||
return (nEnd - nStart);
|
return (Environment.TickCount - tStart);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
KeePass Password Safe - The Open-Source Password Manager
|
|
||||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
using KeePassLib.Security;
|
|
||||||
using KeePassLib.Utility;
|
|
||||||
|
|
||||||
namespace KeePassLib.Cryptography.PasswordGenerator
|
|
||||||
{
|
|
||||||
internal static class CharSetBasedGenerator
|
|
||||||
{
|
|
||||||
internal static PwgError Generate(out ProtectedString psOut,
|
|
||||||
PwProfile pwProfile, CryptoRandomStream crsRandomSource)
|
|
||||||
{
|
|
||||||
psOut = ProtectedString.Empty;
|
|
||||||
if(pwProfile.Length == 0) return PwgError.Success;
|
|
||||||
|
|
||||||
PwCharSet pcs = new PwCharSet(pwProfile.CharSet.ToString());
|
|
||||||
char[] vGenerated = new char[pwProfile.Length];
|
|
||||||
|
|
||||||
PwGenerator.PrepareCharSet(pcs, pwProfile);
|
|
||||||
|
|
||||||
for(int nIndex = 0; nIndex < (int)pwProfile.Length; ++nIndex)
|
|
||||||
{
|
|
||||||
char ch = PwGenerator.GenerateCharacter(pwProfile, pcs,
|
|
||||||
crsRandomSource);
|
|
||||||
|
|
||||||
if(ch == char.MinValue)
|
|
||||||
{
|
|
||||||
MemUtil.ZeroArray<char>(vGenerated);
|
|
||||||
return PwgError.TooFewCharacters;
|
|
||||||
}
|
|
||||||
|
|
||||||
vGenerated[nIndex] = ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vGenerated);
|
|
||||||
psOut = new ProtectedString(true, pbUtf8);
|
|
||||||
MemUtil.ZeroByteArray(pbUtf8);
|
|
||||||
MemUtil.ZeroArray<char>(vGenerated);
|
|
||||||
|
|
||||||
return PwgError.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
/*
|
|
||||||
KeePass Password Safe - The Open-Source Password Manager
|
|
||||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
using KeePassLib.Security;
|
|
||||||
using KeePassLib.Utility;
|
|
||||||
|
|
||||||
namespace KeePassLib.Cryptography.PasswordGenerator
|
|
||||||
{
|
|
||||||
internal static class PatternBasedGenerator
|
|
||||||
{
|
|
||||||
internal static PwgError Generate(out ProtectedString psOut,
|
|
||||||
PwProfile pwProfile, CryptoRandomStream crsRandomSource)
|
|
||||||
{
|
|
||||||
psOut = ProtectedString.Empty;
|
|
||||||
LinkedList<char> vGenerated = new LinkedList<char>();
|
|
||||||
PwCharSet pcsCurrent = new PwCharSet();
|
|
||||||
PwCharSet pcsCustom = new PwCharSet();
|
|
||||||
PwCharSet pcsUsed = new PwCharSet();
|
|
||||||
bool bInCharSetDef = false;
|
|
||||||
|
|
||||||
string strPattern = ExpandPattern(pwProfile.Pattern);
|
|
||||||
if(strPattern.Length == 0) return PwgError.Success;
|
|
||||||
|
|
||||||
CharStream csStream = new CharStream(strPattern);
|
|
||||||
char ch = csStream.ReadChar();
|
|
||||||
|
|
||||||
while(ch != char.MinValue)
|
|
||||||
{
|
|
||||||
pcsCurrent.Clear();
|
|
||||||
|
|
||||||
bool bGenerateChar = false;
|
|
||||||
|
|
||||||
if(ch == '\\')
|
|
||||||
{
|
|
||||||
ch = csStream.ReadChar();
|
|
||||||
if(ch == char.MinValue) // Backslash at the end
|
|
||||||
{
|
|
||||||
vGenerated.AddLast('\\');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(bInCharSetDef) pcsCustom.Add(ch);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vGenerated.AddLast(ch);
|
|
||||||
pcsUsed.Add(ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(ch == '[')
|
|
||||||
{
|
|
||||||
pcsCustom.Clear();
|
|
||||||
bInCharSetDef = true;
|
|
||||||
}
|
|
||||||
else if(ch == ']')
|
|
||||||
{
|
|
||||||
pcsCurrent.Add(pcsCustom.ToString());
|
|
||||||
|
|
||||||
bInCharSetDef = false;
|
|
||||||
bGenerateChar = true;
|
|
||||||
}
|
|
||||||
else if(bInCharSetDef)
|
|
||||||
{
|
|
||||||
if(pcsCustom.AddCharSet(ch) == false)
|
|
||||||
pcsCustom.Add(ch);
|
|
||||||
}
|
|
||||||
else if(pcsCurrent.AddCharSet(ch) == false)
|
|
||||||
{
|
|
||||||
vGenerated.AddLast(ch);
|
|
||||||
pcsUsed.Add(ch);
|
|
||||||
}
|
|
||||||
else bGenerateChar = true;
|
|
||||||
|
|
||||||
if(bGenerateChar)
|
|
||||||
{
|
|
||||||
PwGenerator.PrepareCharSet(pcsCurrent, pwProfile);
|
|
||||||
|
|
||||||
if(pwProfile.NoRepeatingCharacters)
|
|
||||||
pcsCurrent.Remove(pcsUsed.ToString());
|
|
||||||
|
|
||||||
char chGen = PwGenerator.GenerateCharacter(pwProfile,
|
|
||||||
pcsCurrent, crsRandomSource);
|
|
||||||
|
|
||||||
if(chGen == char.MinValue) return PwgError.TooFewCharacters;
|
|
||||||
|
|
||||||
vGenerated.AddLast(chGen);
|
|
||||||
pcsUsed.Add(chGen);
|
|
||||||
}
|
|
||||||
|
|
||||||
ch = csStream.ReadChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(vGenerated.Count == 0) return PwgError.Success;
|
|
||||||
|
|
||||||
char[] vArray = new char[vGenerated.Count];
|
|
||||||
vGenerated.CopyTo(vArray, 0);
|
|
||||||
|
|
||||||
if(pwProfile.PatternPermutePassword)
|
|
||||||
PwGenerator.ShufflePassword(vArray, crsRandomSource);
|
|
||||||
|
|
||||||
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(vArray);
|
|
||||||
psOut = new ProtectedString(true, pbUtf8);
|
|
||||||
MemUtil.ZeroByteArray(pbUtf8);
|
|
||||||
MemUtil.ZeroArray<char>(vArray);
|
|
||||||
vGenerated.Clear();
|
|
||||||
|
|
||||||
return PwgError.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ExpandPattern(string strPattern)
|
|
||||||
{
|
|
||||||
Debug.Assert(strPattern != null); if(strPattern == null) return string.Empty;
|
|
||||||
string str = strPattern;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
int nOpen = FindFirstUnescapedChar(str, '{');
|
|
||||||
int nClose = FindFirstUnescapedChar(str, '}');
|
|
||||||
|
|
||||||
if((nOpen >= 0) && (nOpen < nClose))
|
|
||||||
{
|
|
||||||
string strCount = str.Substring(nOpen + 1, nClose - nOpen - 1);
|
|
||||||
str = str.Remove(nOpen, nClose - nOpen + 1);
|
|
||||||
|
|
||||||
uint uRepeat;
|
|
||||||
if(StrUtil.TryParseUInt(strCount, out uRepeat) && (nOpen >= 1))
|
|
||||||
{
|
|
||||||
if(uRepeat == 0)
|
|
||||||
str = str.Remove(nOpen - 1, 1);
|
|
||||||
else
|
|
||||||
str = str.Insert(nOpen, new string(str[nOpen - 1], (int)uRepeat - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int FindFirstUnescapedChar(string str, char ch)
|
|
||||||
{
|
|
||||||
for(int i = 0; i < str.Length; ++i)
|
|
||||||
{
|
|
||||||
char chCur = str[i];
|
|
||||||
|
|
||||||
if(chCur == '\\') ++i; // Next is escaped, skip it
|
|
||||||
else if(chCur == ch) return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
KeePass Password Safe - The Open-Source Password Manager
|
KeePass Password Safe - The Open-Source Password Manager
|
||||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,333 +19,311 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using KeePassLib.Utility;
|
||||||
|
|
||||||
namespace KeePassLib.Cryptography.PasswordGenerator
|
namespace KeePassLib.Cryptography.PasswordGenerator
|
||||||
{
|
{
|
||||||
public sealed class PwCharSet
|
public sealed class PwCharSet : IEquatable<PwCharSet>
|
||||||
{
|
{
|
||||||
public const string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
public static readonly string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
public const string LowerCase = "abcdefghijklmnopqrstuvwxyz";
|
public static readonly string LowerCase = "abcdefghijklmnopqrstuvwxyz";
|
||||||
public const string Digits = "0123456789";
|
public static readonly string Digits = "0123456789";
|
||||||
|
|
||||||
public const string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ";
|
public static readonly string UpperConsonants = "BCDFGHJKLMNPQRSTVWXYZ";
|
||||||
public const string LowerConsonants = "bcdfghjklmnpqrstvwxyz";
|
public static readonly string LowerConsonants = "bcdfghjklmnpqrstvwxyz";
|
||||||
public const string UpperVowels = "AEIOU";
|
public static readonly string UpperVowels = "AEIOU";
|
||||||
public const string LowerVowels = "aeiou";
|
public static readonly string LowerVowels = "aeiou";
|
||||||
|
|
||||||
public const string Punctuation = @",.;:";
|
public static readonly string Punctuation = ",.;:";
|
||||||
public const string Brackets = @"[]{}()<>";
|
public static readonly string Brackets = @"[]{}()<>";
|
||||||
|
|
||||||
public const string PrintableAsciiSpecial = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
public static readonly string Special = "!\"#$%&'*+,./:;=?@\\^`|~";
|
||||||
|
public static readonly string PrintableAsciiSpecial = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
||||||
|
|
||||||
public const string UpperHex = "0123456789ABCDEF";
|
public static readonly string UpperHex = "0123456789ABCDEF";
|
||||||
public const string LowerHex = "0123456789abcdef";
|
public static readonly string LowerHex = "0123456789abcdef";
|
||||||
|
|
||||||
public const string Invalid = "\t\r\n";
|
public static readonly string LookAlike = "O0Il1|";
|
||||||
public const string LookAlike = @"O0l1I|";
|
|
||||||
|
|
||||||
internal const string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits;
|
/// <summary>
|
||||||
|
/// Latin-1 Supplement except U+00A0 (NBSP) and U+00AD (SHY).
|
||||||
|
/// </summary>
|
||||||
|
public static readonly string Latin1S =
|
||||||
|
"\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7" +
|
||||||
|
"\u00A8\u00A9\u00AA\u00AB\u00AC\u00AE\u00AF" +
|
||||||
|
"\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7" +
|
||||||
|
"\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF" +
|
||||||
|
"\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7" +
|
||||||
|
"\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF" +
|
||||||
|
"\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7" +
|
||||||
|
"\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF" +
|
||||||
|
"\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7" +
|
||||||
|
"\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF" +
|
||||||
|
"\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7" +
|
||||||
|
"\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF";
|
||||||
|
|
||||||
private const int CharTabSize = (0x10000 / 8);
|
// internal static readonly string MenuAccels = PwCharSet.LowerCase + PwCharSet.Digits;
|
||||||
|
|
||||||
private List<char> m_vChars = new List<char>();
|
[Obsolete]
|
||||||
private byte[] m_vTab = new byte[CharTabSize];
|
public static string SpecialChars { get { return PwCharSet.Special; } }
|
||||||
|
[Obsolete]
|
||||||
|
public static string HighAnsiChars { get { return PwCharSet.Latin1S; } }
|
||||||
|
|
||||||
private static string m_strHighAnsi = null;
|
private readonly List<char> m_lChars = new List<char>();
|
||||||
public static string HighAnsiChars
|
private readonly byte[] m_vTab = new byte[0x10000 / 8];
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if(m_strHighAnsi == null) { new PwCharSet(); } // Create string
|
|
||||||
Debug.Assert(m_strHighAnsi != null);
|
|
||||||
return m_strHighAnsi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string m_strSpecial = null;
|
/// <summary>
|
||||||
public static string SpecialChars
|
/// Create a new, empty character set.
|
||||||
{
|
/// </summary>
|
||||||
get
|
public PwCharSet()
|
||||||
{
|
{
|
||||||
if(m_strSpecial == null) { new PwCharSet(); } // Create string
|
Debug.Assert(PwCharSet.Latin1S.Length == (16 * 6 - 2));
|
||||||
Debug.Assert(m_strSpecial != null);
|
}
|
||||||
return m_strSpecial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public PwCharSet(string strCharSet)
|
||||||
/// Create a new, empty character set collection object.
|
{
|
||||||
/// </summary>
|
Add(strCharSet);
|
||||||
public PwCharSet()
|
}
|
||||||
{
|
|
||||||
Initialize(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PwCharSet(string strCharSet)
|
/// <summary>
|
||||||
{
|
/// Number of characters in this set.
|
||||||
Initialize(true);
|
/// </summary>
|
||||||
Add(strCharSet);
|
public uint Size
|
||||||
}
|
{
|
||||||
|
get { return (uint)m_lChars.Count; }
|
||||||
|
}
|
||||||
|
|
||||||
private PwCharSet(bool bFullInitialize)
|
/// <summary>
|
||||||
{
|
/// Get a character of the set using an index.
|
||||||
Initialize(bFullInitialize);
|
/// </summary>
|
||||||
}
|
/// <param name="uPos">Index of the character to get.</param>
|
||||||
|
/// <returns>Character at the specified position. If the index is invalid,
|
||||||
|
/// an <c>ArgumentOutOfRangeException</c> is thrown.</returns>
|
||||||
|
public char this[uint uPos]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (uPos >= (uint)m_lChars.Count)
|
||||||
|
throw new ArgumentOutOfRangeException("uPos");
|
||||||
|
|
||||||
private void Initialize(bool bFullInitialize)
|
return m_lChars[(int)uPos];
|
||||||
{
|
}
|
||||||
Clear();
|
}
|
||||||
|
|
||||||
if(!bFullInitialize) return;
|
public bool Equals(PwCharSet other)
|
||||||
|
{
|
||||||
|
if (object.ReferenceEquals(other, this)) return true;
|
||||||
|
if (object.ReferenceEquals(other, null)) return false;
|
||||||
|
|
||||||
if(m_strHighAnsi == null)
|
if (m_lChars.Count != other.m_lChars.Count) return false;
|
||||||
{
|
|
||||||
StringBuilder sbHighAnsi = new StringBuilder();
|
|
||||||
// [U+0080, U+009F] are C1 control characters,
|
|
||||||
// U+00A0 is non-breaking space
|
|
||||||
for(char ch = '\u00A1'; ch <= '\u00AC'; ++ch)
|
|
||||||
sbHighAnsi.Append(ch);
|
|
||||||
// U+00AD is soft hyphen (format character)
|
|
||||||
for(char ch = '\u00AE'; ch < '\u00FF'; ++ch)
|
|
||||||
sbHighAnsi.Append(ch);
|
|
||||||
sbHighAnsi.Append('\u00FF');
|
|
||||||
|
|
||||||
m_strHighAnsi = sbHighAnsi.ToString();
|
return MemUtil.ArraysEqual(m_vTab, other.m_vTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_strSpecial == null)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
PwCharSet pcs = new PwCharSet(false);
|
return Equals(obj as PwCharSet);
|
||||||
pcs.AddRange('!', '/');
|
}
|
||||||
pcs.AddRange(':', '@');
|
|
||||||
pcs.AddRange('[', '`');
|
|
||||||
pcs.Add(@"|~");
|
|
||||||
pcs.Remove(@"-_ ");
|
|
||||||
pcs.Remove(PwCharSet.Brackets);
|
|
||||||
|
|
||||||
m_strSpecial = pcs.ToString();
|
public override int GetHashCode()
|
||||||
}
|
{
|
||||||
}
|
return (int)MemUtil.Hash32(m_vTab, 0, m_vTab.Length);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of characters in this set.
|
/// Remove all characters from this set.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint Size
|
public void Clear()
|
||||||
{
|
{
|
||||||
get { return (uint)m_vChars.Count; }
|
m_lChars.Clear();
|
||||||
}
|
Array.Clear(m_vTab, 0, m_vTab.Length);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
public bool Contains(char ch)
|
||||||
/// Get a character of the set using an index.
|
{
|
||||||
/// </summary>
|
return (((m_vTab[ch / 8] >> (ch % 8)) & 1) != char.MinValue);
|
||||||
/// <param name="uPos">Index of the character to get.</param>
|
}
|
||||||
/// <returns>Character at the specified position. If the index is invalid,
|
|
||||||
/// an <c>ArgumentOutOfRangeException</c> is thrown.</returns>
|
|
||||||
public char this[uint uPos]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if(uPos >= (uint)m_vChars.Count)
|
|
||||||
throw new ArgumentOutOfRangeException("uPos");
|
|
||||||
|
|
||||||
return m_vChars[(int)uPos];
|
public bool Contains(string strCharacters)
|
||||||
}
|
{
|
||||||
}
|
Debug.Assert(strCharacters != null);
|
||||||
|
if (strCharacters == null) throw new ArgumentNullException("strCharacters");
|
||||||
|
|
||||||
/// <summary>
|
foreach (char ch in strCharacters)
|
||||||
/// Remove all characters from this set.
|
{
|
||||||
/// </summary>
|
if (!Contains(ch)) return false;
|
||||||
public void Clear()
|
}
|
||||||
{
|
|
||||||
m_vChars.Clear();
|
|
||||||
Array.Clear(m_vTab, 0, m_vTab.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(char ch)
|
return true;
|
||||||
{
|
}
|
||||||
return (((m_vTab[ch / 8] >> (ch % 8)) & 1) != char.MinValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(string strCharacters)
|
/// <summary>
|
||||||
{
|
/// Add characters to the set.
|
||||||
Debug.Assert(strCharacters != null);
|
/// </summary>
|
||||||
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
|
/// <param name="ch">Character to add.</param>
|
||||||
|
public void Add(char ch)
|
||||||
|
{
|
||||||
|
if (ch == char.MinValue) { Debug.Assert(false); return; }
|
||||||
|
|
||||||
foreach(char ch in strCharacters)
|
if (!Contains(ch))
|
||||||
{
|
{
|
||||||
if(!Contains(ch)) return false;
|
m_lChars.Add(ch);
|
||||||
}
|
m_vTab[ch / 8] |= (byte)(1 << (ch % 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
/// <summary>
|
||||||
}
|
/// Add characters to the set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strCharSet">String containing characters to add.</param>
|
||||||
|
public void Add(string strCharSet)
|
||||||
|
{
|
||||||
|
Debug.Assert(strCharSet != null);
|
||||||
|
if (strCharSet == null) throw new ArgumentNullException("strCharSet");
|
||||||
|
|
||||||
/// <summary>
|
foreach (char ch in strCharSet)
|
||||||
/// Add characters to the set.
|
Add(ch);
|
||||||
/// </summary>
|
}
|
||||||
/// <param name="ch">Character to add.</param>
|
|
||||||
public void Add(char ch)
|
|
||||||
{
|
|
||||||
if(ch == char.MinValue) { Debug.Assert(false); return; }
|
|
||||||
|
|
||||||
if(!Contains(ch))
|
public void Add(string strCharSet1, string strCharSet2)
|
||||||
{
|
{
|
||||||
m_vChars.Add(ch);
|
Add(strCharSet1);
|
||||||
m_vTab[ch / 8] |= (byte)(1 << (ch % 8));
|
Add(strCharSet2);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public void Add(string strCharSet1, string strCharSet2, string strCharSet3)
|
||||||
/// Add characters to the set.
|
{
|
||||||
/// </summary>
|
Add(strCharSet1);
|
||||||
/// <param name="strCharSet">String containing characters to add.</param>
|
Add(strCharSet2);
|
||||||
public void Add(string strCharSet)
|
Add(strCharSet3);
|
||||||
{
|
}
|
||||||
Debug.Assert(strCharSet != null);
|
|
||||||
if(strCharSet == null) throw new ArgumentNullException("strCharSet");
|
|
||||||
|
|
||||||
m_vChars.Capacity = m_vChars.Count + strCharSet.Length;
|
public void AddRange(char chMin, char chMax)
|
||||||
|
{
|
||||||
|
for (char ch = chMin; ch < chMax; ++ch)
|
||||||
|
Add(ch);
|
||||||
|
|
||||||
foreach(char ch in strCharSet)
|
Add(chMax);
|
||||||
Add(ch);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(string strCharSet1, string strCharSet2)
|
public bool AddCharSet(char chCharSetIdentifier)
|
||||||
{
|
{
|
||||||
Add(strCharSet1);
|
bool bResult = true;
|
||||||
Add(strCharSet2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(string strCharSet1, string strCharSet2, string strCharSet3)
|
switch (chCharSetIdentifier)
|
||||||
{
|
{
|
||||||
Add(strCharSet1);
|
case 'a': Add(PwCharSet.LowerCase, PwCharSet.Digits); break;
|
||||||
Add(strCharSet2);
|
case 'A':
|
||||||
Add(strCharSet3);
|
Add(PwCharSet.LowerCase, PwCharSet.UpperCase,
|
||||||
}
|
PwCharSet.Digits); break;
|
||||||
|
case 'U': Add(PwCharSet.UpperCase, PwCharSet.Digits); break;
|
||||||
|
case 'c': Add(PwCharSet.LowerConsonants); break;
|
||||||
|
case 'C':
|
||||||
|
Add(PwCharSet.LowerConsonants,
|
||||||
|
PwCharSet.UpperConsonants); break;
|
||||||
|
case 'z': Add(PwCharSet.UpperConsonants); break;
|
||||||
|
case 'd': Add(PwCharSet.Digits); break; // Digit
|
||||||
|
case 'h': Add(PwCharSet.LowerHex); break;
|
||||||
|
case 'H': Add(PwCharSet.UpperHex); break;
|
||||||
|
case 'l': Add(PwCharSet.LowerCase); break;
|
||||||
|
case 'L': Add(PwCharSet.LowerCase, PwCharSet.UpperCase); break;
|
||||||
|
case 'u': Add(PwCharSet.UpperCase); break;
|
||||||
|
case 'p': Add(PwCharSet.Punctuation); break;
|
||||||
|
case 'b': Add(PwCharSet.Brackets); break;
|
||||||
|
case 's': Add(PwCharSet.PrintableAsciiSpecial); break;
|
||||||
|
case 'S':
|
||||||
|
Add(PwCharSet.UpperCase, PwCharSet.LowerCase);
|
||||||
|
Add(PwCharSet.Digits, PwCharSet.PrintableAsciiSpecial); break;
|
||||||
|
case 'v': Add(PwCharSet.LowerVowels); break;
|
||||||
|
case 'V': Add(PwCharSet.LowerVowels, PwCharSet.UpperVowels); break;
|
||||||
|
case 'Z': Add(PwCharSet.UpperVowels); break;
|
||||||
|
case 'x': Add(PwCharSet.Latin1S); break;
|
||||||
|
default: bResult = false; break;
|
||||||
|
}
|
||||||
|
|
||||||
public void AddRange(char chMin, char chMax)
|
return bResult;
|
||||||
{
|
}
|
||||||
m_vChars.Capacity = m_vChars.Count + (chMax - chMin) + 1;
|
|
||||||
|
|
||||||
for(char ch = chMin; ch < chMax; ++ch)
|
public bool Remove(char ch)
|
||||||
Add(ch);
|
{
|
||||||
|
m_vTab[ch / 8] &= (byte)(~(1 << (ch % 8)));
|
||||||
|
return m_lChars.Remove(ch);
|
||||||
|
}
|
||||||
|
|
||||||
Add(chMax);
|
public bool Remove(string strCharacters)
|
||||||
}
|
{
|
||||||
|
Debug.Assert(strCharacters != null);
|
||||||
|
if (strCharacters == null) throw new ArgumentNullException("strCharacters");
|
||||||
|
|
||||||
public bool AddCharSet(char chCharSetIdentifier)
|
bool bResult = true;
|
||||||
{
|
foreach (char ch in strCharacters)
|
||||||
bool bResult = true;
|
{
|
||||||
|
if (!Remove(ch)) bResult = false;
|
||||||
|
}
|
||||||
|
|
||||||
switch(chCharSetIdentifier)
|
return bResult;
|
||||||
{
|
}
|
||||||
case 'a': Add(PwCharSet.LowerCase, PwCharSet.Digits); break;
|
|
||||||
case 'A': Add(PwCharSet.LowerCase, PwCharSet.UpperCase,
|
|
||||||
PwCharSet.Digits); break;
|
|
||||||
case 'U': Add(PwCharSet.UpperCase, PwCharSet.Digits); break;
|
|
||||||
case 'c': Add(PwCharSet.LowerConsonants); break;
|
|
||||||
case 'C': Add(PwCharSet.LowerConsonants,
|
|
||||||
PwCharSet.UpperConsonants); break;
|
|
||||||
case 'z': Add(PwCharSet.UpperConsonants); break;
|
|
||||||
case 'd': Add(PwCharSet.Digits); break; // Digit
|
|
||||||
case 'h': Add(PwCharSet.LowerHex); break;
|
|
||||||
case 'H': Add(PwCharSet.UpperHex); break;
|
|
||||||
case 'l': Add(PwCharSet.LowerCase); break;
|
|
||||||
case 'L': Add(PwCharSet.LowerCase, PwCharSet.UpperCase); break;
|
|
||||||
case 'u': Add(PwCharSet.UpperCase); break;
|
|
||||||
case 'p': Add(PwCharSet.Punctuation); break;
|
|
||||||
case 'b': Add(PwCharSet.Brackets); break;
|
|
||||||
case 's': Add(PwCharSet.PrintableAsciiSpecial); break;
|
|
||||||
case 'S': Add(PwCharSet.UpperCase, PwCharSet.LowerCase);
|
|
||||||
Add(PwCharSet.Digits, PwCharSet.PrintableAsciiSpecial); break;
|
|
||||||
case 'v': Add(PwCharSet.LowerVowels); break;
|
|
||||||
case 'V': Add(PwCharSet.LowerVowels, PwCharSet.UpperVowels); break;
|
|
||||||
case 'Z': Add(PwCharSet.UpperVowels); break;
|
|
||||||
case 'x': Add(m_strHighAnsi); break;
|
|
||||||
default: bResult = false; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bResult;
|
public bool RemoveIfAllExist(string strCharacters)
|
||||||
}
|
{
|
||||||
|
Debug.Assert(strCharacters != null);
|
||||||
|
if (strCharacters == null) throw new ArgumentNullException("strCharacters");
|
||||||
|
|
||||||
public bool Remove(char ch)
|
if (!Contains(strCharacters))
|
||||||
{
|
return false;
|
||||||
m_vTab[ch / 8] &= (byte)(~(1 << (ch % 8)));
|
|
||||||
return m_vChars.Remove(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Remove(string strCharacters)
|
return Remove(strCharacters);
|
||||||
{
|
}
|
||||||
Debug.Assert(strCharacters != null);
|
|
||||||
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
|
|
||||||
|
|
||||||
bool bResult = true;
|
/// <summary>
|
||||||
foreach(char ch in strCharacters)
|
/// Convert the character set to a string containing all its characters.
|
||||||
{
|
/// </summary>
|
||||||
if(!Remove(ch)) bResult = false;
|
/// <returns>String containing all character set characters.</returns>
|
||||||
}
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder(m_lChars.Count);
|
||||||
|
foreach (char ch in m_lChars)
|
||||||
|
sb.Append(ch);
|
||||||
|
|
||||||
return bResult;
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RemoveIfAllExist(string strCharacters)
|
public string PackAndRemoveCharRanges()
|
||||||
{
|
{
|
||||||
Debug.Assert(strCharacters != null);
|
StringBuilder sb = new StringBuilder();
|
||||||
if(strCharacters == null) throw new ArgumentNullException("strCharacters");
|
|
||||||
|
|
||||||
if(!Contains(strCharacters))
|
sb.Append(RemoveIfAllExist(PwCharSet.UpperCase) ? 'U' : '_');
|
||||||
return false;
|
sb.Append(RemoveIfAllExist(PwCharSet.LowerCase) ? 'L' : '_');
|
||||||
|
sb.Append(RemoveIfAllExist(PwCharSet.Digits) ? 'D' : '_');
|
||||||
|
sb.Append(RemoveIfAllExist(PwCharSet.Special) ? 'S' : '_');
|
||||||
|
sb.Append(RemoveIfAllExist(PwCharSet.Punctuation) ? 'P' : '_');
|
||||||
|
sb.Append(RemoveIfAllExist("-") ? 'm' : '_');
|
||||||
|
sb.Append(RemoveIfAllExist("_") ? 'u' : '_');
|
||||||
|
sb.Append(RemoveIfAllExist(" ") ? 's' : '_');
|
||||||
|
sb.Append(RemoveIfAllExist(PwCharSet.Brackets) ? 'B' : '_');
|
||||||
|
sb.Append(RemoveIfAllExist(PwCharSet.Latin1S) ? 'H' : '_');
|
||||||
|
|
||||||
return Remove(strCharacters);
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public void UnpackCharRanges(string strRanges)
|
||||||
/// Convert the character set to a string containing all its characters.
|
{
|
||||||
/// </summary>
|
if (strRanges == null) { Debug.Assert(false); return; }
|
||||||
/// <returns>String containing all character set characters.</returns>
|
if (strRanges.Length < 10) { Debug.Assert(false); return; }
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
foreach(char ch in m_vChars)
|
|
||||||
sb.Append(ch);
|
|
||||||
|
|
||||||
return sb.ToString();
|
if (strRanges[0] != '_') Add(PwCharSet.UpperCase);
|
||||||
}
|
if (strRanges[1] != '_') Add(PwCharSet.LowerCase);
|
||||||
|
if (strRanges[2] != '_') Add(PwCharSet.Digits);
|
||||||
public string PackAndRemoveCharRanges()
|
if (strRanges[3] != '_') Add(PwCharSet.Special);
|
||||||
{
|
if (strRanges[4] != '_') Add(PwCharSet.Punctuation);
|
||||||
StringBuilder sb = new StringBuilder();
|
if (strRanges[5] != '_') Add('-');
|
||||||
|
if (strRanges[6] != '_') Add('_');
|
||||||
sb.Append(RemoveIfAllExist(PwCharSet.UpperCase) ? 'U' : '_');
|
if (strRanges[7] != '_') Add(' ');
|
||||||
sb.Append(RemoveIfAllExist(PwCharSet.LowerCase) ? 'L' : '_');
|
if (strRanges[8] != '_') Add(PwCharSet.Brackets);
|
||||||
sb.Append(RemoveIfAllExist(PwCharSet.Digits) ? 'D' : '_');
|
if (strRanges[9] != '_') Add(PwCharSet.Latin1S);
|
||||||
sb.Append(RemoveIfAllExist(m_strSpecial) ? 'S' : '_');
|
}
|
||||||
sb.Append(RemoveIfAllExist(PwCharSet.Punctuation) ? 'P' : '_');
|
}
|
||||||
sb.Append(RemoveIfAllExist(@"-") ? 'm' : '_');
|
|
||||||
sb.Append(RemoveIfAllExist(@"_") ? 'u' : '_');
|
|
||||||
sb.Append(RemoveIfAllExist(@" ") ? 's' : '_');
|
|
||||||
sb.Append(RemoveIfAllExist(PwCharSet.Brackets) ? 'B' : '_');
|
|
||||||
sb.Append(RemoveIfAllExist(m_strHighAnsi) ? 'H' : '_');
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnpackCharRanges(string strRanges)
|
|
||||||
{
|
|
||||||
if(strRanges == null) { Debug.Assert(false); return; }
|
|
||||||
if(strRanges.Length < 10) { Debug.Assert(false); return; }
|
|
||||||
|
|
||||||
if(strRanges[0] != '_') Add(PwCharSet.UpperCase);
|
|
||||||
if(strRanges[1] != '_') Add(PwCharSet.LowerCase);
|
|
||||||
if(strRanges[2] != '_') Add(PwCharSet.Digits);
|
|
||||||
if(strRanges[3] != '_') Add(m_strSpecial);
|
|
||||||
if(strRanges[4] != '_') Add(PwCharSet.Punctuation);
|
|
||||||
if(strRanges[5] != '_') Add('-');
|
|
||||||
if(strRanges[6] != '_') Add('_');
|
|
||||||
if(strRanges[7] != '_') Add(' ');
|
|
||||||
if(strRanges[8] != '_') Add(PwCharSet.Brackets);
|
|
||||||
if(strRanges[9] != '_') Add(m_strHighAnsi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
KeePass Password Safe - The Open-Source Password Manager
|
KeePass Password Safe - The Open-Source Password Manager
|
||||||
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
|
Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,133 +20,172 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
#if !KeePassUAP
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using KeePassLib.Resources;
|
||||||
using KeePassLib.Security;
|
using KeePassLib.Security;
|
||||||
using KeePassLib.Utility;
|
using KeePassLib.Utility;
|
||||||
|
|
||||||
namespace KeePassLib.Cryptography.PasswordGenerator
|
namespace KeePassLib.Cryptography.PasswordGenerator
|
||||||
{
|
{
|
||||||
public enum PwgError
|
public enum PwgError
|
||||||
{
|
{
|
||||||
Success = 0,
|
Success = 0,
|
||||||
Unknown = 1,
|
Unknown = 1,
|
||||||
TooFewCharacters = 2,
|
TooFewCharacters = 2,
|
||||||
UnknownAlgorithm = 3
|
UnknownAlgorithm = 3,
|
||||||
}
|
InvalidCharSet = 4,
|
||||||
|
InvalidPattern = 5
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Utility functions for generating random passwords.
|
/// Password generator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class PwGenerator
|
public static class PwGenerator
|
||||||
{
|
{
|
||||||
public static PwgError Generate(out ProtectedString psOut,
|
|
||||||
PwProfile pwProfile, byte[] pbUserEntropy,
|
|
||||||
CustomPwGeneratorPool pwAlgorithmPool)
|
|
||||||
{
|
|
||||||
Debug.Assert(pwProfile != null);
|
|
||||||
if (pwProfile == null) throw new ArgumentNullException("pwProfile");
|
|
||||||
|
|
||||||
CryptoRandomStream crs = CreateCryptoStream(pbUserEntropy);
|
private static CryptoRandomStream CreateRandomStream(byte[] pbAdditionalEntropy,
|
||||||
PwgError e = PwgError.Unknown;
|
out byte[] pbKey)
|
||||||
|
{
|
||||||
|
pbKey = CryptoRandom.Instance.GetRandomBytes(128);
|
||||||
|
|
||||||
if (pwProfile.GeneratorType == PasswordGeneratorType.CharSet)
|
// Mix in additional entropy
|
||||||
e = CharSetBasedGenerator.Generate(out psOut, pwProfile, crs);
|
Debug.Assert(pbKey.Length >= 64);
|
||||||
else if (pwProfile.GeneratorType == PasswordGeneratorType.Pattern)
|
if ((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length != 0))
|
||||||
e = PatternBasedGenerator.Generate(out psOut, pwProfile, crs);
|
{
|
||||||
else if (pwProfile.GeneratorType == PasswordGeneratorType.Custom)
|
using (SHA512Managed h = new SHA512Managed())
|
||||||
e = GenerateCustom(out psOut, pwProfile, crs, pwAlgorithmPool);
|
{
|
||||||
else { Debug.Assert(false); psOut = ProtectedString.Empty; }
|
byte[] pbHash = h.ComputeHash(pbAdditionalEntropy);
|
||||||
|
MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length);
|
||||||
|
MemUtil.ZeroByteArray(pbHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return e;
|
return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CryptoRandomStream CreateCryptoStream(byte[] pbAdditionalEntropy)
|
internal static char GenerateCharacter(PwCharSet pwCharSet,
|
||||||
{
|
CryptoRandomStream crsRandomSource)
|
||||||
byte[] pbKey = CryptoRandom.Instance.GetRandomBytes(128);
|
{
|
||||||
|
uint cc = pwCharSet.Size;
|
||||||
|
if (cc == 0) return char.MinValue;
|
||||||
|
|
||||||
// Mix in additional entropy
|
uint i = (uint)crsRandomSource.GetRandomUInt64(cc);
|
||||||
Debug.Assert(pbKey.Length >= 64);
|
return pwCharSet[i];
|
||||||
if ((pbAdditionalEntropy != null) && (pbAdditionalEntropy.Length > 0))
|
}
|
||||||
{
|
|
||||||
using (SHA512Managed h = new SHA512Managed())
|
|
||||||
{
|
|
||||||
byte[] pbHash = h.ComputeHash(pbAdditionalEntropy);
|
|
||||||
MemUtil.XorArray(pbHash, 0, pbKey, 0, pbHash.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CryptoRandomStream(CrsAlgorithm.ChaCha20, pbKey);
|
internal static bool PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile)
|
||||||
}
|
{
|
||||||
|
uint cc = pwCharSet.Size;
|
||||||
|
for (uint i = 0; i < cc; ++i)
|
||||||
|
{
|
||||||
|
char ch = pwCharSet[i];
|
||||||
|
if ((ch == char.MinValue) || (ch == '\t') || (ch == '\r') ||
|
||||||
|
(ch == '\n') || char.IsSurrogate(ch))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
internal static char GenerateCharacter(PwProfile pwProfile,
|
if (pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike);
|
||||||
PwCharSet pwCharSet, CryptoRandomStream crsRandomSource)
|
|
||||||
{
|
|
||||||
if (pwCharSet.Size == 0) return char.MinValue;
|
|
||||||
|
|
||||||
ulong uIndex = crsRandomSource.GetRandomUInt64();
|
if (!string.IsNullOrEmpty(pwProfile.ExcludeCharacters))
|
||||||
uIndex %= (ulong)pwCharSet.Size;
|
pwCharSet.Remove(pwProfile.ExcludeCharacters);
|
||||||
|
|
||||||
char ch = pwCharSet[(uint)uIndex];
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (pwProfile.NoRepeatingCharacters)
|
internal static void Shuffle(char[] v, CryptoRandomStream crsRandomSource)
|
||||||
pwCharSet.Remove(ch);
|
{
|
||||||
|
if (v == null) { Debug.Assert(false); return; }
|
||||||
|
if (crsRandomSource == null) { Debug.Assert(false); return; }
|
||||||
|
|
||||||
return ch;
|
for (int i = v.Length - 1; i >= 1; --i)
|
||||||
}
|
{
|
||||||
|
int j = (int)crsRandomSource.GetRandomUInt64((ulong)(i + 1));
|
||||||
|
|
||||||
internal static void PrepareCharSet(PwCharSet pwCharSet, PwProfile pwProfile)
|
char t = v[i];
|
||||||
{
|
v[i] = v[j];
|
||||||
pwCharSet.Remove(PwCharSet.Invalid);
|
v[j] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pwProfile.ExcludeLookAlike) pwCharSet.Remove(PwCharSet.LookAlike);
|
private static PwgError GenerateCustom(out ProtectedString psOut,
|
||||||
|
PwProfile pwProfile, CryptoRandomStream crs,
|
||||||
|
CustomPwGeneratorPool pwAlgorithmPool)
|
||||||
|
{
|
||||||
|
psOut = ProtectedString.Empty;
|
||||||
|
|
||||||
if (pwProfile.ExcludeCharacters.Length > 0)
|
Debug.Assert(pwProfile.GeneratorType == PasswordGeneratorType.Custom);
|
||||||
pwCharSet.Remove(pwProfile.ExcludeCharacters);
|
if (pwAlgorithmPool == null) return PwgError.UnknownAlgorithm;
|
||||||
}
|
|
||||||
|
|
||||||
internal static void ShufflePassword(char[] pPassword,
|
string strID = pwProfile.CustomAlgorithmUuid;
|
||||||
CryptoRandomStream crsRandomSource)
|
if (string.IsNullOrEmpty(strID)) return PwgError.UnknownAlgorithm;
|
||||||
{
|
|
||||||
Debug.Assert(pPassword != null); if (pPassword == null) return;
|
|
||||||
Debug.Assert(crsRandomSource != null); if (crsRandomSource == null) return;
|
|
||||||
|
|
||||||
if (pPassword.Length <= 1) return; // Nothing to shuffle
|
byte[] pbUuid = Convert.FromBase64String(strID);
|
||||||
|
PwUuid uuid = new PwUuid(pbUuid);
|
||||||
|
CustomPwGenerator pwg = pwAlgorithmPool.Find(uuid);
|
||||||
|
if (pwg == null) { Debug.Assert(false); return PwgError.UnknownAlgorithm; }
|
||||||
|
|
||||||
for (int nSelect = 0; nSelect < pPassword.Length; ++nSelect)
|
ProtectedString pwd = pwg.Generate(pwProfile.CloneDeep(), crs);
|
||||||
{
|
if (pwd == null) return PwgError.Unknown;
|
||||||
ulong uRandomIndex = crsRandomSource.GetRandomUInt64();
|
|
||||||
uRandomIndex %= (ulong)(pPassword.Length - nSelect);
|
|
||||||
|
|
||||||
char chTemp = pPassword[nSelect];
|
psOut = pwd;
|
||||||
pPassword[nSelect] = pPassword[nSelect + (int)uRandomIndex];
|
return PwgError.Success;
|
||||||
pPassword[nSelect + (int)uRandomIndex] = chTemp;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PwgError GenerateCustom(out ProtectedString psOut,
|
internal static string ErrorToString(PwgError e, bool bHeader)
|
||||||
PwProfile pwProfile, CryptoRandomStream crs,
|
{
|
||||||
CustomPwGeneratorPool pwAlgorithmPool)
|
if (e == PwgError.Success) { Debug.Assert(false); return string.Empty; }
|
||||||
{
|
if ((e == PwgError.Unknown) && bHeader) return KLRes.PwGenFailed;
|
||||||
psOut = ProtectedString.Empty;
|
|
||||||
|
|
||||||
Debug.Assert(pwProfile.GeneratorType == PasswordGeneratorType.Custom);
|
string str = KLRes.UnknownError;
|
||||||
if (pwAlgorithmPool == null) return PwgError.UnknownAlgorithm;
|
switch (e)
|
||||||
|
{
|
||||||
|
// case PwgError.Success:
|
||||||
|
// break;
|
||||||
|
|
||||||
string strID = pwProfile.CustomAlgorithmUuid;
|
case PwgError.Unknown:
|
||||||
if (string.IsNullOrEmpty(strID)) { Debug.Assert(false); return PwgError.UnknownAlgorithm; }
|
break;
|
||||||
|
|
||||||
byte[] pbUuid = Convert.FromBase64String(strID);
|
case PwgError.TooFewCharacters:
|
||||||
PwUuid uuid = new PwUuid(pbUuid);
|
str = KLRes.CharSetTooFewChars;
|
||||||
CustomPwGenerator pwg = pwAlgorithmPool.Find(uuid);
|
break;
|
||||||
if (pwg == null) { Debug.Assert(false); return PwgError.UnknownAlgorithm; }
|
|
||||||
|
|
||||||
ProtectedString pwd = pwg.Generate(pwProfile.CloneDeep(), crs);
|
case PwgError.UnknownAlgorithm:
|
||||||
if (pwd == null) return PwgError.Unknown;
|
str = KLRes.AlgorithmUnknown;
|
||||||
|
break;
|
||||||
|
|
||||||
psOut = pwd;
|
case PwgError.InvalidCharSet:
|
||||||
return PwgError.Success;
|
str = KLRes.CharSetInvalid;
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
|
case PwgError.InvalidPattern:
|
||||||
|
str = KLRes.PatternInvalid;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Debug.Assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bHeader)
|
||||||
|
str = KLRes.PwGenFailed + MessageService.NewParagraph + str;
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string ErrorToString(Exception ex, bool bHeader)
|
||||||
|
{
|
||||||
|
string str = ((ex == null) ? KLRes.UnknownError :
|
||||||
|
StrUtil.FormatException(ex));
|
||||||
|
|
||||||
|
if (bHeader)
|
||||||
|
str = KLRes.PwGenFailed + MessageService.NewParagraph + str;
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
KeePass Password Safe - The Open-Source Password Manager
|
KeePass Password Safe - The Open-Source Password Manager
|
||||||
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
|
Copyright (C) 2003-2025 Dominik Reichl <dominik.reichl@t-online.de>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,114 +19,115 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
using KeePassLib.Utility;
|
using KeePassLib.Utility;
|
||||||
|
|
||||||
namespace KeePassLib.Cryptography
|
namespace KeePassLib.Cryptography
|
||||||
{
|
{
|
||||||
public static class PopularPasswords
|
public static class PopularPasswords
|
||||||
{
|
{
|
||||||
private static Dictionary<int, Dictionary<string, bool>> m_dicts =
|
private static readonly Dictionary<int, Dictionary<char[], bool>> g_dicts =
|
||||||
new Dictionary<int, Dictionary<string, bool>>();
|
new Dictionary<int, Dictionary<char[], bool>>();
|
||||||
|
|
||||||
internal static int MaxLength
|
internal static int MaxLength
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
int iMaxLen = 0;
|
Debug.Assert(g_dicts.Count > 0); // Should be initialized
|
||||||
foreach(int iLen in m_dicts.Keys)
|
|
||||||
{
|
|
||||||
if(iLen > iMaxLen) iMaxLen = iLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
return iMaxLen;
|
int iMaxLen = 0;
|
||||||
}
|
foreach (int iLen in g_dicts.Keys)
|
||||||
}
|
{
|
||||||
|
if (iLen > iMaxLen) iMaxLen = iLen;
|
||||||
|
}
|
||||||
|
|
||||||
internal static bool ContainsLength(int nLength)
|
return iMaxLen;
|
||||||
{
|
}
|
||||||
Dictionary<string, bool> dDummy;
|
}
|
||||||
return m_dicts.TryGetValue(nLength, out dDummy);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsPopularPassword(char[] vPassword)
|
internal static bool ContainsLength(int nLength)
|
||||||
{
|
{
|
||||||
ulong uDummy;
|
Dictionary<char[], bool> dDummy;
|
||||||
return IsPopularPassword(vPassword, out uDummy);
|
return g_dicts.TryGetValue(nLength, out dDummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsPopularPassword(char[] vPassword, out ulong uDictSize)
|
public static bool IsPopularPassword(char[] vPassword)
|
||||||
{
|
{
|
||||||
if(vPassword == null) throw new ArgumentNullException("vPassword");
|
ulong uDummy;
|
||||||
if(vPassword.Length == 0) { uDictSize = 0; return false; }
|
return IsPopularPassword(vPassword, out uDummy);
|
||||||
|
}
|
||||||
|
|
||||||
string str = new string(vPassword);
|
public static bool IsPopularPassword(char[] vPassword, out ulong uDictSize)
|
||||||
|
{
|
||||||
|
if (vPassword == null) throw new ArgumentNullException("vPassword");
|
||||||
|
if (vPassword.Length == 0) { uDictSize = 0; return false; }
|
||||||
|
|
||||||
try { return IsPopularPasswordPriv(str, out uDictSize); }
|
#if DEBUG
|
||||||
catch(Exception) { Debug.Assert(false); }
|
Array.ForEach(vPassword, ch => Debug.Assert(ch == char.ToLower(ch)));
|
||||||
|
#endif
|
||||||
|
|
||||||
uDictSize = 0;
|
try { return IsPopularPasswordPriv(vPassword, out uDictSize); }
|
||||||
return false;
|
catch (Exception) { Debug.Assert(false); }
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsPopularPasswordPriv(string str, out ulong uDictSize)
|
uDictSize = 0;
|
||||||
{
|
return false;
|
||||||
Debug.Assert(m_dicts.Count > 0); // Should be initialized with data
|
}
|
||||||
|
|
||||||
Dictionary<string, bool> d;
|
private static bool IsPopularPasswordPriv(char[] vPassword, out ulong uDictSize)
|
||||||
if(!m_dicts.TryGetValue(str.Length, out d))
|
{
|
||||||
{
|
Debug.Assert(g_dicts.Count > 0); // Should be initialized with data
|
||||||
uDictSize = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uDictSize = (ulong)d.Count;
|
Dictionary<char[], bool> d;
|
||||||
return d.ContainsKey(str);
|
if (!g_dicts.TryGetValue(vPassword.Length, out d))
|
||||||
}
|
{
|
||||||
|
uDictSize = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Add(byte[] pbData, bool bGZipped)
|
uDictSize = (ulong)d.Count;
|
||||||
{
|
return d.ContainsKey(vPassword);
|
||||||
try
|
}
|
||||||
{
|
|
||||||
if(bGZipped)
|
|
||||||
pbData = MemUtil.Decompress(pbData);
|
|
||||||
|
|
||||||
string strData = StrUtil.Utf8.GetString(pbData, 0, pbData.Length);
|
public static void Add(byte[] pbData, bool bGZipped)
|
||||||
if(string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; }
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (bGZipped)
|
||||||
|
pbData = MemUtil.Decompress(pbData);
|
||||||
|
|
||||||
if(!char.IsWhiteSpace(strData[strData.Length - 1]))
|
string strData = StrUtil.Utf8.GetString(pbData, 0, pbData.Length);
|
||||||
strData += "\n";
|
if (string.IsNullOrEmpty(strData)) { Debug.Assert(false); return; }
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for(int i = 0; i < strData.Length; ++i)
|
for (int i = 0; i <= strData.Length; ++i)
|
||||||
{
|
{
|
||||||
char ch = strData[i];
|
char ch = ((i == strData.Length) ? ' ' : strData[i]);
|
||||||
|
|
||||||
if(char.IsWhiteSpace(ch))
|
if (char.IsWhiteSpace(ch))
|
||||||
{
|
{
|
||||||
int cc = sb.Length;
|
int cc = sb.Length;
|
||||||
if(cc > 0)
|
if (cc > 0)
|
||||||
{
|
{
|
||||||
string strWord = sb.ToString();
|
char[] vWord = new char[cc];
|
||||||
Debug.Assert(strWord.Length == cc);
|
sb.CopyTo(0, vWord, 0, cc);
|
||||||
|
|
||||||
Dictionary<string, bool> d;
|
Dictionary<char[], bool> d;
|
||||||
if(!m_dicts.TryGetValue(cc, out d))
|
if (!g_dicts.TryGetValue(cc, out d))
|
||||||
{
|
{
|
||||||
d = new Dictionary<string, bool>();
|
d = new Dictionary<char[], bool>(MemUtil.ArrayHelperExOfChar);
|
||||||
m_dicts[cc] = d;
|
g_dicts[cc] = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
d[strWord] = true;
|
d[vWord] = true;
|
||||||
sb.Remove(0, cc);
|
sb.Remove(0, cc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else sb.Append(char.ToLower(ch));
|
else sb.Append(char.ToLower(ch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception) { Debug.Assert(false); }
|
catch (Exception) { Debug.Assert(false); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -46,4 +46,12 @@ namespace KeePassLib.Delegates
|
|||||||
public delegate void VoidDelegate();
|
public delegate void VoidDelegate();
|
||||||
|
|
||||||
public delegate string StrPwEntryDelegate(string str, PwEntry pe);
|
public delegate string StrPwEntryDelegate(string str, PwEntry pe);
|
||||||
|
|
||||||
|
public delegate TResult GFunc<TResult>();
|
||||||
|
public delegate TResult GFunc<T, TResult>(T o);
|
||||||
|
public delegate TResult GFunc<T1, T2, TResult>(T1 o1, T2 o2);
|
||||||
|
public delegate TResult GFunc<T1, T2, T3, TResult>(T1 o1, T2 o2, T3 o3);
|
||||||
|
public delegate TResult GFunc<T1, T2, T3, T4, TResult>(T1 o1, T2 o2, T3 o3, T4 o4);
|
||||||
|
public delegate TResult GFunc<T1, T2, T3, T4, T5, TResult>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5);
|
||||||
|
public delegate TResult GFunc<T1, T2, T3, T4, T5, T6, TResult>(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5, T6 o6);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,17 +86,12 @@ namespace KeePassLib.Interfaces
|
|||||||
/// the current work.</returns>
|
/// the current work.</returns>
|
||||||
bool SetText(string strNewText, LogStatusType lsType);
|
bool SetText(string strNewText, LogStatusType lsType);
|
||||||
|
|
||||||
void UpdateMessage(String message);
|
/// <summary>
|
||||||
|
/// Check if the user cancelled the current work.
|
||||||
|
/// </summary>
|
||||||
void UpdateSubMessage(String submessage);
|
/// <returns>Returns <c>true</c> if the caller should continue
|
||||||
|
/// the current work.</returns>
|
||||||
/// <summary>
|
bool ContinueWork();
|
||||||
/// Check if the user cancelled the current work.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Returns <c>true</c> if the caller should continue
|
|
||||||
/// the current work.</returns>
|
|
||||||
bool ContinueWork();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class NullStatusLogger : IStatusLogger
|
public sealed class NullStatusLogger : IStatusLogger
|
||||||
@@ -105,12 +100,6 @@ namespace KeePassLib.Interfaces
|
|||||||
public void EndLogging() { }
|
public void EndLogging() { }
|
||||||
public bool SetProgress(uint uPercent) { return true; }
|
public bool SetProgress(uint uPercent) { return true; }
|
||||||
public bool SetText(string strNewText, LogStatusType lsType) { return true; }
|
public bool SetText(string strNewText, LogStatusType lsType) { return true; }
|
||||||
public void UpdateMessage(string message){
|
public bool ContinueWork() { return true; }
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateSubMessage(string submessage){
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ContinueWork() { return true; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ using System.IO;
|
|||||||
using Android;
|
using Android;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
|
||||||
using Android.Preferences;
|
using Android.Preferences;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
|
|
||||||
@@ -35,17 +34,9 @@ namespace keepass2android
|
|||||||
|
|
||||||
public static void Log(string message)
|
public static void Log(string message)
|
||||||
{
|
{
|
||||||
if (message != null)
|
if (message != null)
|
||||||
{
|
Android.Util.Log.Debug("KP2A", message);
|
||||||
message += Thread.CurrentThread.ManagedThreadId != 0
|
if (LogToFile)
|
||||||
? " (Thread ID: " + Thread.CurrentThread.ManagedThreadId + ")"
|
|
||||||
: "";
|
|
||||||
if (Looper.MainLooper == Looper.MyLooper())
|
|
||||||
message += " (Main Looper)";
|
|
||||||
Android.Util.Log.Debug("KP2A", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LogToFile)
|
|
||||||
{
|
{
|
||||||
lock (_fileLocker)
|
lock (_fileLocker)
|
||||||
{
|
{
|
||||||
@@ -125,12 +116,23 @@ namespace keepass2android
|
|||||||
|
|
||||||
Intent sendIntent = new Intent();
|
Intent sendIntent = new Intent();
|
||||||
sendIntent.SetAction(Intent.ActionSend);
|
sendIntent.SetAction(Intent.ActionSend);
|
||||||
sendIntent.PutExtra(Intent.ExtraText, File.ReadAllText(LogFilename));
|
string logText = File.ReadAllText(LogFilename);
|
||||||
|
|
||||||
|
sendIntent.PutExtra(Intent.ExtraText, logText);
|
||||||
sendIntent.PutExtra(Intent.ExtraEmail, "crocoapps@gmail.com");
|
sendIntent.PutExtra(Intent.ExtraEmail, "crocoapps@gmail.com");
|
||||||
sendIntent.PutExtra(Intent.ExtraSubject, "Keepass2Android log");
|
sendIntent.PutExtra(Intent.ExtraSubject, "Keepass2Android log");
|
||||||
sendIntent.SetType("text/plain");
|
sendIntent.SetType("text/plain");
|
||||||
ctx.StartActivity(Intent.CreateChooser(sendIntent, "Send log to..."));
|
try
|
||||||
}
|
{
|
||||||
|
ctx.StartActivity(Intent.CreateChooser(sendIntent, "Send log to..."));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Toast.MakeText(ctx, $"Error sending log of length {logText.Length} bytes: " + e.Message, ToastLength.Long)?.Show();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static void LogTask(object task, string activityName)
|
public static void LogTask(object task, string activityName)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ namespace KeePassLib.Serialization
|
|||||||
if (!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) &&
|
if (!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) &&
|
||||||
!m_bRepairMode)
|
!m_bRepairMode)
|
||||||
{
|
{
|
||||||
// Debug.Assert(m_uFileVersion < FileVersion32_4);
|
Debug.Assert(m_uFileVersion < FileVersion32_4);
|
||||||
|
|
||||||
byte[] pbHash = Convert.FromBase64String(strHash);
|
byte[] pbHash = Convert.FromBase64String(strHash);
|
||||||
if (!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader))
|
if (!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader))
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ namespace KeePassLib.Serialization
|
|||||||
if (m_bUsedOnce)
|
if (m_bUsedOnce)
|
||||||
throw new InvalidOperationException("Do not reuse KdbxFile objects!");
|
throw new InvalidOperationException("Do not reuse KdbxFile objects!");
|
||||||
m_bUsedOnce = true;
|
m_bUsedOnce = true;
|
||||||
|
Kp2aLog.Log("Starting to load KDBX file...");
|
||||||
|
|
||||||
#if KDBX_BENCHMARK
|
#if KDBX_BENCHMARK
|
||||||
Stopwatch swTime = Stopwatch.StartNew();
|
Stopwatch swTime = Stopwatch.StartNew();
|
||||||
@@ -257,7 +258,8 @@ namespace KeePassLib.Serialization
|
|||||||
MessageService.ShowInfo("Loading KDBX took " +
|
MessageService.ShowInfo("Loading KDBX took " +
|
||||||
swTime.ElapsedMilliseconds.ToString() + " ms.");
|
swTime.ElapsedMilliseconds.ToString() + " ms.");
|
||||||
#endif
|
#endif
|
||||||
}
|
Kp2aLog.Log("Finished loading KDBX file.");
|
||||||
|
}
|
||||||
|
|
||||||
private void CommonCleanUpRead(List<Stream> lStreams, HashingStreamEx sHashing)
|
private void CommonCleanUpRead(List<Stream> lStreams, HashingStreamEx sHashing)
|
||||||
{
|
{
|
||||||
@@ -488,7 +490,7 @@ namespace KeePassLib.Serialization
|
|||||||
|
|
||||||
ProtectedBinary pb = new ProtectedBinary(bProt, pbData,
|
ProtectedBinary pb = new ProtectedBinary(bProt, pbData,
|
||||||
1, pbData.Length - 1);
|
1, pbData.Length - 1);
|
||||||
//Debug.Assert(m_pbsBinaries.Find(pb) < 0); // No deduplication?
|
Debug.Assert(m_pbsBinaries.Find(pb) < 0); // No deduplication?
|
||||||
m_pbsBinaries.Add(pb);
|
m_pbsBinaries.Add(pb);
|
||||||
|
|
||||||
if (bProt) MemUtil.ZeroByteArray(pbData);
|
if (bProt) MemUtil.ZeroByteArray(pbData);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -95,7 +95,7 @@ namespace Kp2aAutofillParserTest
|
|||||||
StructureParserBase<TestInputField> parser =
|
StructureParserBase<TestInputField> parser =
|
||||||
new StructureParserBase<TestInputField>(new TestLogger(), new TestDalSourceTrustAll());
|
new StructureParserBase<TestInputField>(new TestLogger(), new TestDalSourceTrustAll());
|
||||||
|
|
||||||
var result = parser.ParseForFill(false, autofillView);
|
var result = parser.ParseForFill(autofillView);
|
||||||
if (expectedPackageName != null)
|
if (expectedPackageName != null)
|
||||||
Assert.Equal(expectedPackageName, result.PackageName);
|
Assert.Equal(expectedPackageName, result.PackageName);
|
||||||
if (expectedWebDomain != null)
|
if (expectedWebDomain != null)
|
||||||
|
|||||||
@@ -58,7 +58,8 @@
|
|||||||
"IsFocused": false,
|
"IsFocused": false,
|
||||||
"InputType": 97,
|
"InputType": 97,
|
||||||
"HtmlInfoTag": null,
|
"HtmlInfoTag": null,
|
||||||
"HtmlInfoTypeAttribute": null
|
"HtmlInfoTypeAttribute": null,
|
||||||
|
"ExpectedAssignedHints": [ "username" ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdEntry": "password_text_input_layout",
|
"IdEntry": "password_text_input_layout",
|
||||||
@@ -81,6 +82,7 @@
|
|||||||
"InputType": 129,
|
"InputType": 129,
|
||||||
"HtmlInfoTag": null,
|
"HtmlInfoTag": null,
|
||||||
"HtmlInfoTypeAttribute": null,
|
"HtmlInfoTypeAttribute": null,
|
||||||
|
"ExpectedAssignedHints": [ "password" ]
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -476,8 +476,16 @@ namespace Kp2aAutofillParser
|
|||||||
|
|
||||||
foreach (var field in autofillFields.HintMap.Values.Distinct())
|
foreach (var field in autofillFields.HintMap.Values.Distinct())
|
||||||
{
|
{
|
||||||
|
if (field == null || field.AutofillHints == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
foreach (var hint in field.AutofillHints)
|
foreach (var hint in field.AutofillHints)
|
||||||
{
|
{
|
||||||
|
if (hint == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (GetPartitionIndex(hint) == partitionIndex)
|
if (GetPartitionIndex(hint) == partitionIndex)
|
||||||
{
|
{
|
||||||
filteredCollection.Add(field);
|
filteredCollection.Add(field);
|
||||||
@@ -793,14 +801,14 @@ namespace Kp2aAutofillParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AutofillTargetId ParseForFill(bool isManual, AutofillView<FieldT> autofillView)
|
public AutofillTargetId ParseForFill(AutofillView<FieldT> autofillView)
|
||||||
{
|
{
|
||||||
return Parse(true, isManual, autofillView);
|
return Parse(true, autofillView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AutofillTargetId ParseForSave(AutofillView<FieldT> autofillView)
|
public AutofillTargetId ParseForSave(AutofillView<FieldT> autofillView)
|
||||||
{
|
{
|
||||||
return Parse(false, true, autofillView);
|
return Parse(false, autofillView);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -808,8 +816,7 @@ namespace Kp2aAutofillParser
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The parse.</returns>
|
/// <returns>The parse.</returns>
|
||||||
/// <param name="forFill">If set to <c>true</c> for fill.</param>
|
/// <param name="forFill">If set to <c>true</c> for fill.</param>
|
||||||
/// <param name="isManualRequest"></param>
|
protected virtual AutofillTargetId Parse(bool forFill, AutofillView<FieldT> autofillView)
|
||||||
protected virtual AutofillTargetId Parse(bool forFill, bool isManualRequest, AutofillView<FieldT> autofillView)
|
|
||||||
{
|
{
|
||||||
AutofillTargetId result = new AutofillTargetId()
|
AutofillTargetId result = new AutofillTargetId()
|
||||||
{
|
{
|
||||||
@@ -876,8 +883,9 @@ namespace Kp2aAutofillParser
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//for "heuristic determination" we demand that one of the filled fields is focused:
|
//for "heuristic determination" we demand that there is a password field or one of the username fields is focused:
|
||||||
if (passwordFields.Concat(usernameFields).Any(f => f.IsFocused))
|
//Note that "IsFocused" might be false even when tapping the field. It might require long-press to autofill.
|
||||||
|
if (passwordFields.Any() || usernameFields.Any(f => f.IsFocused))
|
||||||
{
|
{
|
||||||
foreach (var uf in usernameFields)
|
foreach (var uf in usernameFields)
|
||||||
AddFieldToHintMap(uf, new string[] { AutofillHintsHelper.AutofillHintUsername });
|
AddFieldToHintMap(uf, new string[] { AutofillHintsHelper.AutofillHintUsername });
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
|
|
||||||
|
|
||||||
Keepass2Android is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Keepass2Android is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using Android.OS;
|
|
||||||
using Java.Lang;
|
|
||||||
using Java.Security;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace keepass2android
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Class to run a task while a progress dialog is shown
|
|
||||||
/// </summary>
|
|
||||||
public class BlockingOperationStarter
|
|
||||||
{
|
|
||||||
|
|
||||||
private readonly OperationWithFinishHandler _task;
|
|
||||||
private readonly IKp2aApp _app;
|
|
||||||
|
|
||||||
public BlockingOperationStarter(IKp2aApp app, OperationWithFinishHandler task)
|
|
||||||
{
|
|
||||||
_task = task;
|
|
||||||
_app = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Run()
|
|
||||||
{
|
|
||||||
_app.CancelBackgroundOperations();
|
|
||||||
OperationRunner.Instance.Run(_app, _task, true);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ namespace keepass2android
|
|||||||
/// Interface through which Activities and the logic layer can access some app specific functionalities and Application static data
|
/// Interface through which Activities and the logic layer can access some app specific functionalities and Application static data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// This also contains methods which are UI specific and should be replacable for testing.
|
/// This also contains methods which are UI specific and should be replacable for testing.
|
||||||
public interface IKp2aApp : ICertificateValidationHandler, IActiveContextProvider
|
public interface IKp2aApp : ICertificateValidationHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Locks all currently open databases, quicklocking if available (unless false is passed for allowQuickUnlock)
|
/// Locks all currently open databases, quicklocking if available (unless false is passed for allowQuickUnlock)
|
||||||
@@ -52,9 +52,7 @@ namespace keepass2android
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the specified data as the currently open database, as unlocked.
|
/// Loads the specified data as the currently open database, as unlocked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Database LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey,
|
Database LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey, ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat, bool makeCurrent);
|
||||||
IKp2aStatusLogger statusLogger, IDatabaseFormat databaseFormat, bool makeCurrent,
|
|
||||||
IDatabaseModificationWatcher modificationWatcher);
|
|
||||||
|
|
||||||
|
|
||||||
HashSet<PwGroup> DirtyGroups { get; }
|
HashSet<PwGroup> DirtyGroups { get; }
|
||||||
@@ -98,6 +96,7 @@ namespace keepass2android
|
|||||||
EventHandler<DialogClickEventArgs> yesHandler,
|
EventHandler<DialogClickEventArgs> yesHandler,
|
||||||
EventHandler<DialogClickEventArgs> noHandler,
|
EventHandler<DialogClickEventArgs> noHandler,
|
||||||
EventHandler<DialogClickEventArgs> cancelHandler,
|
EventHandler<DialogClickEventArgs> cancelHandler,
|
||||||
|
Context ctx,
|
||||||
string messageSuffix = "");
|
string messageSuffix = "");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -108,6 +107,7 @@ namespace keepass2android
|
|||||||
EventHandler<DialogClickEventArgs> yesHandler,
|
EventHandler<DialogClickEventArgs> yesHandler,
|
||||||
EventHandler<DialogClickEventArgs> noHandler,
|
EventHandler<DialogClickEventArgs> noHandler,
|
||||||
EventHandler<DialogClickEventArgs> cancelHandler,
|
EventHandler<DialogClickEventArgs> cancelHandler,
|
||||||
|
Context ctx,
|
||||||
string messageSuffix = "");
|
string messageSuffix = "");
|
||||||
|
|
||||||
void ShowMessage(Context ctx, int resourceId, MessageSeverity severity);
|
void ShowMessage(Context ctx, int resourceId, MessageSeverity severity);
|
||||||
@@ -136,17 +136,14 @@ namespace keepass2android
|
|||||||
bool CheckForDuplicateUuids { get; }
|
bool CheckForDuplicateUuids { get; }
|
||||||
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
|
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
|
||||||
ICertificateErrorHandler CertificateErrorHandler { get; }
|
ICertificateErrorHandler CertificateErrorHandler { get; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
int WebDavChunkedUploadSize
|
||||||
bool SyncInBackgroundPreference { get; set; }
|
{
|
||||||
void StartBackgroundSyncService();
|
get;
|
||||||
|
}
|
||||||
ReaderWriterLockSlim DatabasesBackgroundModificationLock { get; }
|
|
||||||
bool CancelBackgroundOperations();
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers an action that should be executed when the context instance with the given id has been resumed.
|
|
||||||
/// </summary>
|
|
||||||
void RegisterPendingActionForContextInstance(int contextInstanceId, ActionOnOperationFinished actionToPerformWhenContextIsResumed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -186,11 +186,8 @@ namespace keepass2android.Io
|
|||||||
Kp2aLog.Log("couldn't open from remote " + ioc.Path);
|
Kp2aLog.Log("couldn't open from remote " + ioc.Path);
|
||||||
#endif
|
#endif
|
||||||
Kp2aLog.Log(ex.ToString());
|
Kp2aLog.Log(ex.ToString());
|
||||||
if (TriggerWarningWhenFallingBackToCache)
|
|
||||||
{
|
_cacheSupervisor.CouldntOpenFromRemote(ioc, ex);
|
||||||
_cacheSupervisor.CouldntOpenFromRemote(ioc, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return File.OpenRead(cachedFilePath);
|
return File.OpenRead(cachedFilePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,10 +327,7 @@ namespace keepass2android.Io
|
|||||||
Kp2aLog.Log("couldn't save to remote " + ioc.Path);
|
Kp2aLog.Log("couldn't save to remote " + ioc.Path);
|
||||||
Kp2aLog.Log(e.ToString());
|
Kp2aLog.Log(e.ToString());
|
||||||
//notify the supervisor so it might display a warning or schedule a retry
|
//notify the supervisor so it might display a warning or schedule a retry
|
||||||
if (TriggerWarningWhenFallingBackToCache)
|
_cacheSupervisor.CouldntSaveToRemote(ioc, e);
|
||||||
{
|
|
||||||
_cacheSupervisor.CouldntSaveToRemote(ioc, e); }
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -638,9 +632,7 @@ namespace keepass2android.Io
|
|||||||
set { _cachedStorage.IsOffline = value; }
|
set { _cachedStorage.IsOffline = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TriggerWarningWhenFallingBackToCache { get; set; }
|
public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode,
|
||||||
|
|
||||||
public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode,
|
|
||||||
string[] permissions, Permission[] grantResults)
|
string[] permissions, Permission[] grantResults)
|
||||||
{
|
{
|
||||||
_cachedStorage.OnRequestPermissionsResult(fileStorageSetupActivity, requestCode, permissions, grantResults);
|
_cachedStorage.OnRequestPermissionsResult(fileStorageSetupActivity, requestCode, permissions, grantResults);
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ namespace keepass2android.Io
|
|||||||
{
|
{
|
||||||
get { return false; }
|
get { return false; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static public bool IsConfigured => !string.IsNullOrEmpty(AppKey) && !string.IsNullOrEmpty(AppSecret);
|
||||||
|
}
|
||||||
|
|
||||||
public partial class DropboxAppFolderFileStorage: JavaFileStorage
|
public partial class DropboxAppFolderFileStorage: JavaFileStorage
|
||||||
{
|
{
|
||||||
@@ -29,6 +31,7 @@ namespace keepass2android.Io
|
|||||||
get { return false; }
|
get { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public bool IsConfigured => !string.IsNullOrEmpty(AppKey) && !string.IsNullOrEmpty(AppSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace keepass2android.Io
|
|
||||||
{
|
|
||||||
public partial class DropboxFileStorage
|
|
||||||
{
|
|
||||||
private const string AppKey = "dummy";
|
|
||||||
private const string AppSecret = "dummy";
|
|
||||||
}
|
|
||||||
public partial class DropboxAppFolderFileStorage
|
|
||||||
{
|
|
||||||
private const string AppKey = "dummy";
|
|
||||||
private const string AppSecret = "dummy";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
27
src/Kp2aBusinessLogic/Io/GenerateSecrets.targets
Normal file
27
src/Kp2aBusinessLogic/Io/GenerateSecrets.targets
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<Project>
|
||||||
|
<Target Name="GenerateDropboxSecrets" BeforeTargets="BeforeCompile"
|
||||||
|
Inputs="@(DropboxSecretLines)"
|
||||||
|
Outputs="DropboxFileStorage.g.cs">
|
||||||
|
|
||||||
|
<WriteLinesToFile
|
||||||
|
File="Io/DropboxFileStorage.g.cs"
|
||||||
|
Lines="@(DropboxSecretLines->'%(Text)')"
|
||||||
|
Overwrite="true"
|
||||||
|
/>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<DropboxSecretLines Include="GeneratedDropboxSecrets">
|
||||||
|
<Text>namespace keepass2android.Io {
|
||||||
|
public partial class DropboxFileStorage {
|
||||||
|
private const string AppKey = "$(DropboxAppKey)";
|
||||||
|
private const string AppSecret = "$(DropboxAppSecret)";
|
||||||
|
}
|
||||||
|
public partial class DropboxAppFolderFileStorage {
|
||||||
|
private const string AppKey = "$(DropboxAppFolderAppKey)";
|
||||||
|
private const string AppSecret = "$(DropboxAppFolderAppSecret)";
|
||||||
|
}
|
||||||
|
}</Text>
|
||||||
|
</DropboxSecretLines>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -111,11 +111,6 @@ namespace keepass2android.Io
|
|||||||
}
|
}
|
||||||
|
|
||||||
Java.Lang.Exception exception = e as Java.Lang.Exception;
|
Java.Lang.Exception exception = e as Java.Lang.Exception;
|
||||||
|
|
||||||
if ((exception is Java.Lang.InterruptedException) || (exception is Java.IO.InterruptedIOException))
|
|
||||||
{
|
|
||||||
throw new Java.Lang.InterruptedException(exception.Message);
|
|
||||||
}
|
|
||||||
if (exception != null)
|
if (exception != null)
|
||||||
{
|
{
|
||||||
var ex = new Exception(exception.LocalizedMessage ??
|
var ex = new Exception(exception.LocalizedMessage ??
|
||||||
@@ -128,7 +123,7 @@ namespace keepass2android.Io
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
public virtual IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||||
{
|
{
|
||||||
return new JavaFileStorageWriteTransaction(IocToPath(ioc), useFileTransaction, this);
|
return new JavaFileStorageWriteTransaction(IocToPath(ioc), useFileTransaction, this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ namespace keepass2android.Io
|
|||||||
public interface IOfflineSwitchable
|
public interface IOfflineSwitchable
|
||||||
{
|
{
|
||||||
bool IsOffline { get; set; }
|
bool IsOffline { get; set; }
|
||||||
bool TriggerWarningWhenFallingBackToCache { get; set; }
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Encapsulates another IFileStorage. Allows to switch to offline mode by throwing
|
/// Encapsulates another IFileStorage. Allows to switch to offline mode by throwing
|
||||||
@@ -22,9 +21,8 @@ namespace keepass2android.Io
|
|||||||
{
|
{
|
||||||
private readonly IFileStorage _baseStorage;
|
private readonly IFileStorage _baseStorage;
|
||||||
public bool IsOffline { get; set; }
|
public bool IsOffline { get; set; }
|
||||||
public bool TriggerWarningWhenFallingBackToCache { get; set; }
|
|
||||||
|
|
||||||
public OfflineSwitchableFileStorage(IFileStorage baseStorage)
|
public OfflineSwitchableFileStorage(IFileStorage baseStorage)
|
||||||
{
|
{
|
||||||
_baseStorage = baseStorage;
|
_baseStorage = baseStorage;
|
||||||
}
|
}
|
||||||
|
|||||||
617
src/Kp2aBusinessLogic/Io/SmbFileStorage.cs
Normal file
617
src/Kp2aBusinessLogic/Io/SmbFileStorage.cs
Normal file
@@ -0,0 +1,617 @@
|
|||||||
|
#if !NoNet
|
||||||
|
using System.Net;
|
||||||
|
using Android.Content;
|
||||||
|
using keepass2android;
|
||||||
|
using keepass2android.Io;
|
||||||
|
using KeePassLib.Serialization;
|
||||||
|
using SMBLibrary.Client;
|
||||||
|
using SMBLibrary;
|
||||||
|
using FileAttributes = SMBLibrary.FileAttributes;
|
||||||
|
using KeePassLib.Utility;
|
||||||
|
using Java.Nio.FileNio;
|
||||||
|
|
||||||
|
namespace Kp2aBusinessLogic.Io
|
||||||
|
{
|
||||||
|
public class SmbFileStorage : IFileStorage
|
||||||
|
{
|
||||||
|
public IEnumerable<string> SupportedProtocols
|
||||||
|
{
|
||||||
|
get { yield return "smb"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UserShouldBackup
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SmbConnectionInfo
|
||||||
|
{
|
||||||
|
public string Host;
|
||||||
|
public string Username;
|
||||||
|
public string Password;
|
||||||
|
public string? Domain;
|
||||||
|
public string? Share;
|
||||||
|
public string? LocalPath;
|
||||||
|
|
||||||
|
public static SmbConnectionInfo FromUrlAndCredentials(string url, string username, string password, string? domain)
|
||||||
|
{
|
||||||
|
string userDomain = username;
|
||||||
|
if (domain != null)
|
||||||
|
{
|
||||||
|
userDomain = domain + "\\" + username;
|
||||||
|
}
|
||||||
|
if (url.StartsWith("smb://"))
|
||||||
|
{
|
||||||
|
url = url.Substring(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.StartsWith("\\\\"))
|
||||||
|
{
|
||||||
|
url = url.Substring(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
url = url.Replace("\\", "/");
|
||||||
|
|
||||||
|
string fullPath = "smb://" + WebUtility.UrlEncode(userDomain) + ":" + WebUtility.UrlEncode(password) + "@" + url;
|
||||||
|
return new SmbConnectionInfo(new IOConnectionInfo() { Path = fullPath} );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SmbConnectionInfo(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
string fullpath = ioc.Path;
|
||||||
|
if (!fullpath.StartsWith("smb://"))
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid smb path!");
|
||||||
|
}
|
||||||
|
|
||||||
|
fullpath = fullpath.Substring(6);
|
||||||
|
string[] authAndPath = fullpath.Split('@');
|
||||||
|
if (authAndPath.Length != 2)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid smb path!");
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] userAndPwd = authAndPath[0].Split(':');
|
||||||
|
if (userAndPwd.Length != 2)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid smb path!");
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] pathParts = authAndPath[1].Split('/');
|
||||||
|
if (pathParts.Length < 1)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid smb path!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Host = pathParts[0];
|
||||||
|
if (pathParts.Length > 1)
|
||||||
|
{
|
||||||
|
Share = pathParts[1];
|
||||||
|
}
|
||||||
|
LocalPath = String.Join("/", pathParts.Skip(2));
|
||||||
|
if (LocalPath.EndsWith("/"))
|
||||||
|
{
|
||||||
|
LocalPath = LocalPath.Substring(0, LocalPath.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Username = WebUtility.UrlDecode(userAndPwd[0]);
|
||||||
|
if (Username.Contains("\\"))
|
||||||
|
{
|
||||||
|
string[] domainAndUser = Username.Split('\\');
|
||||||
|
Domain = domainAndUser[0];
|
||||||
|
Username = domainAndUser[1];
|
||||||
|
}
|
||||||
|
else Domain = null;
|
||||||
|
|
||||||
|
Password = WebUtility.UrlDecode(userAndPwd[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToPath()
|
||||||
|
{
|
||||||
|
string domainUser = Username;
|
||||||
|
if (Domain != null)
|
||||||
|
{
|
||||||
|
domainUser = Domain + "\\" + Username;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "smb://" + WebUtility.UrlEncode(domainUser) + ":" + WebUtility.UrlEncode(Password) + "@" + Host +
|
||||||
|
"/" + Share + "/" + LocalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetPathWithoutCredentials()
|
||||||
|
{
|
||||||
|
return "smb://" + Host + "/" + Share + "/" + LocalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetLocalSmbPath()
|
||||||
|
{
|
||||||
|
return LocalPath?.Replace("/", "\\") ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmbConnectionInfo GetParent()
|
||||||
|
{
|
||||||
|
SmbConnectionInfo parent = new SmbConnectionInfo
|
||||||
|
{
|
||||||
|
Host = Host,
|
||||||
|
Username = Username,
|
||||||
|
Password = Password,
|
||||||
|
Domain = Domain,
|
||||||
|
Share = Share
|
||||||
|
};
|
||||||
|
string[] pathParts = LocalPath?.Split('/') ?? [];
|
||||||
|
if (pathParts.Length > 0)
|
||||||
|
{
|
||||||
|
parent.LocalPath = string.Join("/", pathParts.Take(pathParts.Length - 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parent.LocalPath = "";
|
||||||
|
parent.Share = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Stem()
|
||||||
|
{
|
||||||
|
return LocalPath?.Split('/').Last() ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SmbConnectionInfo GetChild(string childName)
|
||||||
|
{
|
||||||
|
SmbConnectionInfo child = new SmbConnectionInfo();
|
||||||
|
child.Host = Host;
|
||||||
|
child.Username = Username;
|
||||||
|
child.Password = Password;
|
||||||
|
child.Domain = Domain;
|
||||||
|
if (string.IsNullOrEmpty(Share))
|
||||||
|
{
|
||||||
|
child.Share = childName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
child.Share = Share;
|
||||||
|
var pathPartsList = LocalPath?.Split('/').Where(p => !string.IsNullOrEmpty(p)).ToList() ?? [];
|
||||||
|
pathPartsList.Add(childName);
|
||||||
|
child.LocalPath = string.Join("/", pathPartsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToDisplayString()
|
||||||
|
{
|
||||||
|
return "smb://" + Host + "/" + Share + "/" + LocalPath;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SmbConnection: IDisposable
|
||||||
|
{
|
||||||
|
public SmbConnection(SmbConnectionInfo info)
|
||||||
|
{
|
||||||
|
_isLoggedIn = false;
|
||||||
|
var isConnected = Client.Connect(info.Host, SMBTransportType.DirectTCPTransport);
|
||||||
|
if (!isConnected)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to connect to SMB server {info.Host}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = Client.Login(info.Domain ?? string.Empty, info.Username, info.Password);
|
||||||
|
if (status != NTStatus.STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to login to SMB as {info.Username}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_isLoggedIn = true;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(info.Share))
|
||||||
|
{
|
||||||
|
FileStore = Client.TreeConnect(info.Share, out status);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public readonly SMB2Client Client = new SMB2Client();
|
||||||
|
|
||||||
|
|
||||||
|
public readonly ISMBFileStore? FileStore;
|
||||||
|
private readonly bool _isLoggedIn;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
FileStore?.Disconnect();
|
||||||
|
|
||||||
|
if (_isLoggedIn)
|
||||||
|
Client.Logoff();
|
||||||
|
|
||||||
|
if (!Client.IsConnected) return;
|
||||||
|
Client.Disconnect();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Stream OpenFileForRead(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
|
||||||
|
SmbConnectionInfo info = new SmbConnectionInfo(ioc);
|
||||||
|
using SmbConnection conn = new SmbConnection(info);
|
||||||
|
|
||||||
|
if (conn.FileStore == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to read to {info.GetPathWithoutCredentials()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NTStatus status = conn.FileStore.CreateFile(out var fileHandle, out _, info.GetLocalSmbPath(),
|
||||||
|
AccessMask.GENERIC_READ | AccessMask.SYNCHRONIZE, FileAttributes.Normal, ShareAccess.Read,
|
||||||
|
CreateDisposition.FILE_OPEN,
|
||||||
|
CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, null);
|
||||||
|
|
||||||
|
if (status != NTStatus.STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to open file {info.LocalPath}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var stream = new MemoryStream();
|
||||||
|
long bytesRead = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
status = conn.FileStore.ReadFile(out var data, fileHandle, bytesRead, (int)conn.Client.MaxReadSize);
|
||||||
|
if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.STATUS_END_OF_FILE)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to read from file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == NTStatus.STATUS_END_OF_FILE || data.Length == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesRead += data.Length;
|
||||||
|
stream.Write(data, 0, data.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SmbFileStorageWriteTransaction : IWriteTransaction
|
||||||
|
{
|
||||||
|
private bool UseFileTransaction { get; }
|
||||||
|
private readonly string _path;
|
||||||
|
private readonly string _uploadPath;
|
||||||
|
private readonly SmbFileStorage _fileStorage;
|
||||||
|
private MemoryStream? _memoryStream;
|
||||||
|
|
||||||
|
public SmbFileStorageWriteTransaction(string path, SmbFileStorage fileStorage, bool useFileTransaction)
|
||||||
|
{
|
||||||
|
UseFileTransaction = useFileTransaction;
|
||||||
|
_path = path;
|
||||||
|
if (useFileTransaction)
|
||||||
|
{
|
||||||
|
_uploadPath = _path + Guid.NewGuid().ToString().Substring(0, 8) + ".tmp";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_uploadPath = _path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_fileStorage = fileStorage;
|
||||||
|
_memoryStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_memoryStream?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream OpenFile()
|
||||||
|
{
|
||||||
|
_memoryStream = new MemoryStream();
|
||||||
|
return _memoryStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CommitWrite()
|
||||||
|
{
|
||||||
|
_fileStorage.UploadData(new MemoryStream(_memoryStream!.ToArray()), new SmbConnectionInfo(new IOConnectionInfo() { Path = _uploadPath}));
|
||||||
|
if (UseFileTransaction)
|
||||||
|
{
|
||||||
|
SmbConnectionInfo uploadPath = new SmbConnectionInfo(new IOConnectionInfo() { Path = _uploadPath });
|
||||||
|
SmbConnectionInfo finalPath = new SmbConnectionInfo(new IOConnectionInfo() { Path = _path });
|
||||||
|
_fileStorage.RenameFile(uploadPath, finalPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenameFile(SmbConnectionInfo fromPath, SmbConnectionInfo toPath)
|
||||||
|
{
|
||||||
|
using var connection = new SmbConnection(fromPath);
|
||||||
|
|
||||||
|
// Open existing file
|
||||||
|
var status = connection.FileStore!.CreateFile(out var handle, out _, fromPath.GetLocalSmbPath(), AccessMask.MAXIMUM_ALLOWED, 0, ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
|
||||||
|
if (status != NTStatus.STATUS_SUCCESS)
|
||||||
|
throw new Exception($"Failed to open {fromPath.LocalPath} for renaming!");
|
||||||
|
|
||||||
|
FileRenameInformationType2 renameInfo = new FileRenameInformationType2
|
||||||
|
{
|
||||||
|
FileName = toPath.GetLocalSmbPath(),
|
||||||
|
ReplaceIfExists = true
|
||||||
|
};
|
||||||
|
connection.FileStore.SetFileInformation(handle, renameInfo);
|
||||||
|
connection.FileStore.CloseFile(handle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UploadData(Stream data, SmbConnectionInfo uploadPath)
|
||||||
|
{
|
||||||
|
using var connection = new SmbConnection(uploadPath);
|
||||||
|
var status = connection.FileStore!.CreateFile(out var fileHandle, out _, uploadPath.GetLocalSmbPath(), AccessMask.GENERIC_WRITE | AccessMask.SYNCHRONIZE, FileAttributes.Normal, ShareAccess.None, CreateDisposition.FILE_CREATE, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, null);
|
||||||
|
if (status == NTStatus.STATUS_OBJECT_NAME_COLLISION)
|
||||||
|
status = connection.FileStore!.CreateFile(out fileHandle, out _, uploadPath.GetLocalSmbPath(), AccessMask.GENERIC_WRITE | AccessMask.SYNCHRONIZE, FileAttributes.Normal, ShareAccess.None, CreateDisposition.FILE_OVERWRITE, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, null);
|
||||||
|
if (status != NTStatus.STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to open {uploadPath.LocalPath} for writing!");
|
||||||
|
}
|
||||||
|
|
||||||
|
long writeOffset = 0;
|
||||||
|
while (data.Position < data.Length)
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[(int)connection.Client.MaxWriteSize];
|
||||||
|
int bytesRead = data.Read(buffer, 0, buffer.Length);
|
||||||
|
if (bytesRead < (int)connection.Client.MaxWriteSize)
|
||||||
|
{
|
||||||
|
Array.Resize(ref buffer, bytesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
status = connection.FileStore.WriteFile(out _, fileHandle, writeOffset, buffer);
|
||||||
|
if (status != NTStatus.STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to write to file");
|
||||||
|
}
|
||||||
|
writeOffset += bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||||
|
{
|
||||||
|
return new SmbFileStorageWriteTransaction(ioc.Path, this, useFileTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return UrlUtil.StripExtension(
|
||||||
|
UrlUtil.GetFileName(ioc.Path));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFileExtension(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return UrlUtil.GetExtension(ioc.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RequiresCredentials(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<FileDescription> ListShares(SmbConnection conn, SmbConnectionInfo parent)
|
||||||
|
{
|
||||||
|
foreach (string share in conn.Client.ListShares(out _))
|
||||||
|
{
|
||||||
|
yield return new FileDescription()
|
||||||
|
{
|
||||||
|
CanRead = true,
|
||||||
|
CanWrite = true,
|
||||||
|
DisplayName = share,
|
||||||
|
IsDirectory = true,
|
||||||
|
Path = parent.GetChild(share).ToPath()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
List<FileDescription> result = [];
|
||||||
|
SmbConnectionInfo info = new SmbConnectionInfo(ioc);
|
||||||
|
using SmbConnection conn = new SmbConnection(info);
|
||||||
|
if (string.IsNullOrEmpty(info.Share))
|
||||||
|
{
|
||||||
|
var shares = ListShares(conn, info).ToList();
|
||||||
|
return shares;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTStatus status = conn.FileStore!.CreateFile(out var directoryHandle, out _, info.GetLocalSmbPath(), AccessMask.GENERIC_READ, FileAttributes.Directory, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
|
||||||
|
if (status == NTStatus.STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
conn.FileStore.QueryDirectory(out List<QueryDirectoryFileInformation> fileList, directoryHandle, "*", FileInformationClass.FileDirectoryInformation);
|
||||||
|
foreach (var fi in fileList)
|
||||||
|
{
|
||||||
|
var fileDirectoryInformation = fi as FileDirectoryInformation;
|
||||||
|
if (fileDirectoryInformation == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (fileDirectoryInformation.FileName is "." or "..")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var fileDescription = FileDescriptionConvert(ioc, fileDirectoryInformation);
|
||||||
|
|
||||||
|
result.Add(fileDescription);
|
||||||
|
}
|
||||||
|
conn.FileStore.CloseFile(directoryHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileDescription FileDescriptionConvert(IOConnectionInfo parentIoc,
|
||||||
|
FileDirectoryInformation fileDirectoryInformation)
|
||||||
|
{
|
||||||
|
FileDescription fileDescription = new FileDescription
|
||||||
|
{
|
||||||
|
CanRead = true,
|
||||||
|
CanWrite = true,
|
||||||
|
IsDirectory = (fileDirectoryInformation.FileAttributes & FileAttributes.Directory) != 0,
|
||||||
|
DisplayName = fileDirectoryInformation.FileName
|
||||||
|
};
|
||||||
|
fileDescription.Path = CreateFilePath(parentIoc.Path, fileDescription.DisplayName);
|
||||||
|
fileDescription.LastModified = fileDirectoryInformation.LastWriteTime;
|
||||||
|
|
||||||
|
fileDescription.SizeInBytes = fileDirectoryInformation.EndOfFile;
|
||||||
|
return fileDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileDescription GetFileDescription(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
SmbConnectionInfo info = new SmbConnectionInfo(ioc);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(info.Share))
|
||||||
|
{
|
||||||
|
return new FileDescription
|
||||||
|
{
|
||||||
|
CanRead = true, CanWrite = true,
|
||||||
|
DisplayName = info.Host,
|
||||||
|
IsDirectory = true,
|
||||||
|
Path = info.ToPath()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
using SmbConnection conn = new SmbConnection(info);
|
||||||
|
NTStatus status = conn.FileStore!.CreateFile(out var directoryHandle, out _, info.GetParent().GetLocalSmbPath(), AccessMask.GENERIC_READ, FileAttributes.Directory, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
|
||||||
|
if (status != NTStatus.STATUS_SUCCESS) throw new Exception($"Failed to query details for {info.LocalPath}");
|
||||||
|
conn.FileStore.QueryDirectory(out List<QueryDirectoryFileInformation> fileList, directoryHandle, info.Stem(), FileInformationClass.FileDirectoryInformation);
|
||||||
|
foreach (var fi in fileList)
|
||||||
|
{
|
||||||
|
var fileDirectoryInformation = fi as FileDirectoryInformation;
|
||||||
|
if (fileDirectoryInformation == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (fileDirectoryInformation.FileName is "." or "..")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return FileDescriptionConvert(ioc, fileDirectoryInformation);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
conn.FileStore.CloseFile(directoryHandle);
|
||||||
|
|
||||||
|
throw new Exception($"Failed to query details for {info.LocalPath}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RequiresSetup(IOConnectionInfo ioConnection)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string IocToPath(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return ioc.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
|
||||||
|
{
|
||||||
|
activity.PerformManualFileSelect(isForSave, requestCode, protocolId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
|
||||||
|
bool alwaysReturnSuccess)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent();
|
||||||
|
activity.IocToIntent(intent, ioc);
|
||||||
|
activity.OnImmediateResult(requestCode, (int)FileStorageResults.FileUsagePrepared, intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareFileUsage(Context ctx, IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnResume(IFileStorageSetupActivity activity)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnStart(IFileStorageSetupActivity activity)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetDisplayName(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return new SmbConnectionInfo(ioc).ToDisplayString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateFilePath(string parent, string newFilename)
|
||||||
|
{
|
||||||
|
return new SmbConnectionInfo(new IOConnectionInfo() { Path = parent}).GetChild(newFilename).ToPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
SmbConnectionInfo connectionInfo = new SmbConnectionInfo(ioc);
|
||||||
|
return new IOConnectionInfo() { Path = connectionInfo.GetParent().ToPath() };
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
||||||
|
{
|
||||||
|
return new IOConnectionInfo() { Path = CreateFilePath(folderPath.Path, filename)};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPermanentLocation(IOConnectionInfo ioc)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly(IOConnectionInfo ioc, OptionalOut<UiStringKey> reason = null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -6,10 +6,12 @@ using System.Text;
|
|||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
|
using Android.Preferences;
|
||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
using Android.Views;
|
using Android.Views;
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
|
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
|
||||||
|
|
||||||
using Keepass2android.Javafilestorage;
|
using Keepass2android.Javafilestorage;
|
||||||
#endif
|
#endif
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
@@ -19,9 +21,15 @@ namespace keepass2android.Io
|
|||||||
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
|
#if !NoNet && !EXCLUDE_JAVAFILESTORAGE
|
||||||
public class WebDavFileStorage: JavaFileStorage
|
public class WebDavFileStorage: JavaFileStorage
|
||||||
{
|
{
|
||||||
public WebDavFileStorage(IKp2aApp app) : base(new Keepass2android.Javafilestorage.WebDavStorage(app.CertificateErrorHandler), app)
|
private readonly IKp2aApp _app;
|
||||||
{
|
private readonly WebDavStorage baseWebdavStorage;
|
||||||
}
|
|
||||||
|
public WebDavFileStorage(IKp2aApp app, int chunkSize) : base(new Keepass2android.Javafilestorage.WebDavStorage(app.CertificateErrorHandler, chunkSize), app)
|
||||||
|
{
|
||||||
|
_app = app;
|
||||||
|
baseWebdavStorage = (WebDavStorage)Jfs;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> SupportedProtocols
|
public override IEnumerable<string> SupportedProtocols
|
||||||
{
|
{
|
||||||
@@ -75,6 +83,15 @@ namespace keepass2android.Io
|
|||||||
}
|
}
|
||||||
return base.IocToPath(ioc);
|
return base.IocToPath(ioc);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public override IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||||
|
{
|
||||||
|
baseWebdavStorage.SetUploadChunkSize(_app.WebDavChunkedUploadSize);
|
||||||
|
return base.OpenWriteTransaction(ioc, useFileTransaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -1,33 +1,47 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<DefineConstants Condition="'$(Flavor)'=='NoNet'">NO_QR_SCANNER;EXCLUDE_JAVAFILESTORAGE;NoNet</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Resources\" />
|
<Folder Include="Resources\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentFTP" Version="51.1.0" />
|
<PackageReference Include="FluentFTP" Version="52.1.0" Condition="'$(Flavor)'!='NoNet'" />
|
||||||
<PackageReference Include="MegaApiClient" Version="1.10.4" />
|
<PackageReference Include="MegaApiClient" Version="1.10.4" Condition="'$(Flavor)'!='NoNet'" />
|
||||||
<PackageReference Include="Microsoft.Graph" Version="5.68.0" />
|
<PackageReference Include="Microsoft.Graph" Version="5.68.0" Condition="'$(Flavor)'!='NoNet'" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.1" />
|
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.1" Condition="'$(Flavor)'!='NoNet'" />
|
||||||
|
<PackageReference Include="SMBLibrary" Version="1.5.4" Condition="'$(Flavor)'!='NoNet'" />
|
||||||
<PackageReference Include="Xamarin.AndroidX.Browser" Version="1.8.0" />
|
<PackageReference Include="Xamarin.AndroidX.Browser" Version="1.8.0" />
|
||||||
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.13.1.5" />
|
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.13.1.5" />
|
||||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.11.0.3" />
|
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.11.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AndroidFileChooserBinding\AndroidFileChooserBinding.csproj" />
|
<ProjectReference Include="..\AndroidFileChooserBinding\AndroidFileChooserBinding.csproj" />
|
||||||
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj" />
|
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj" Condition="'$(Flavor)'!='NoNet'" />
|
||||||
<ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj" />
|
<ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj" />
|
||||||
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj" />
|
<ProjectReference Include="..\KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj" />
|
||||||
<ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj" />
|
<ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Io/DropboxFileStorageKeysDummy.cs" />
|
|
||||||
<Compile Remove="Io/DropboxFileStorageKeysDummy.cs" />
|
|
||||||
<Content Remove="Io/DropboxFileStorageKeysDummy.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup Condition="'$(Flavor)'=='NoNet'">
|
||||||
|
<None Remove="Io/OneDrive2FileStorage.cs" />
|
||||||
|
<Compile Remove="Io/OneDrive2FileStorage.cs" />
|
||||||
|
<Content Remove="Io/OneDrive2FileStorage.cs" />
|
||||||
|
<None Remove="Io/MegaFileStorage.cs" />
|
||||||
|
<Compile Remove="Io/MegaFileStorage.cs" />
|
||||||
|
<Content Remove="Io/MegaFileStorage.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Import Project="Io/GenerateSecrets.targets" />
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Io/DropboxFileStorage.g.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using Android.OS;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Thread = Java.Lang.Thread;
|
|
||||||
|
|
||||||
namespace keepass2android;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Allows to run tasks in the background. The UI is not blocked by the task. Tasks continue to run in the BackgroundSyncService if the app goes to background while tasks are active.
|
|
||||||
/// </summary>
|
|
||||||
public class OperationRunner
|
|
||||||
{
|
|
||||||
//singleton instance
|
|
||||||
private static OperationRunner _instance = null;
|
|
||||||
|
|
||||||
public static OperationRunner Instance
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_instance == null)
|
|
||||||
{
|
|
||||||
_instance = new OperationRunner();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Initialize(IKp2aApp app)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct OperationWithMetadata
|
|
||||||
{
|
|
||||||
public OperationWithMetadata()
|
|
||||||
{
|
|
||||||
Operation = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OperationWithFinishHandler Operation { get; set; }
|
|
||||||
public bool RunBlocking { get; set; } = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProgressUiAsStatusLoggerAdapter StatusLogger => _statusLogger;
|
|
||||||
|
|
||||||
private OperationRunner()
|
|
||||||
{
|
|
||||||
//private constructor
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Queue<OperationWithMetadata> _taskQueue = new Queue<OperationWithMetadata>();
|
|
||||||
private readonly object _taskQueueLock = new object();
|
|
||||||
private Java.Lang.Thread? _thread = null;
|
|
||||||
private OperationWithMetadata? _currentlyRunningTask = null;
|
|
||||||
private ProgressUiAsStatusLoggerAdapter _statusLogger = null;
|
|
||||||
private IProgressDialog _progressDialog;
|
|
||||||
private IKp2aApp _app;
|
|
||||||
|
|
||||||
public void Run(IKp2aApp app, OperationWithFinishHandler operation, bool runBlocking = false)
|
|
||||||
{
|
|
||||||
lock (Instance._taskQueueLock)
|
|
||||||
{
|
|
||||||
_taskQueue.Enqueue(new OperationWithMetadata(){ Operation = operation, RunBlocking = runBlocking});
|
|
||||||
operation.SetStatusLogger(_statusLogger);
|
|
||||||
|
|
||||||
// Start thread to run the task (unless it's already running)
|
|
||||||
if (_thread == null)
|
|
||||||
{
|
|
||||||
_statusLogger.StartLogging("", false);
|
|
||||||
_thread = new Java.Lang.Thread(() =>
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
|
|
||||||
lock (_taskQueueLock)
|
|
||||||
{
|
|
||||||
if (!_taskQueue.Any())
|
|
||||||
{
|
|
||||||
_thread = null;
|
|
||||||
_statusLogger.EndLogging();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_currentlyRunningTask = _taskQueue.Dequeue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentlyRunningTask.Value.RunBlocking)
|
|
||||||
{
|
|
||||||
app.UiThreadHandler.Post(
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
TrySetupProgressDialog();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var originalFinishedHandler = _currentlyRunningTask.Value.Operation.operationFinishedHandler;
|
|
||||||
_currentlyRunningTask.Value.Operation.operationFinishedHandler = new ActionOnOperationFinished(app, (
|
|
||||||
(success, message, context) =>
|
|
||||||
{
|
|
||||||
if (_currentlyRunningTask?.RunBlocking == true)
|
|
||||||
{
|
|
||||||
_app.UiThreadHandler.Post(() =>
|
|
||||||
{
|
|
||||||
_progressDialog?.Dismiss();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_currentlyRunningTask = null;
|
|
||||||
|
|
||||||
}), originalFinishedHandler);
|
|
||||||
_currentlyRunningTask.Value.Operation.Run();
|
|
||||||
|
|
||||||
while (_currentlyRunningTask != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Kp2aLog.Log("Thread interrupted.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
_thread.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private bool TrySetupProgressDialog()
|
|
||||||
{
|
|
||||||
string currentMessage = "Initializing...";
|
|
||||||
string currentSubmessage = "";
|
|
||||||
|
|
||||||
if (_statusLogger != null)
|
|
||||||
{
|
|
||||||
currentMessage = _statusLogger.LastMessage;
|
|
||||||
currentSubmessage = _statusLogger.LastSubMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_progressDialog != null)
|
|
||||||
{
|
|
||||||
var pd = _progressDialog;
|
|
||||||
_app.UiThreadHandler.Post(() =>
|
|
||||||
{
|
|
||||||
pd.Dismiss();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show process dialog
|
|
||||||
_progressDialog = _app.CreateProgressDialog(_app.ActiveContext);
|
|
||||||
if (_progressDialog == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var progressUi = new ProgressDialogUi(_app, _app.UiThreadHandler, _progressDialog);
|
|
||||||
_statusLogger.SetNewProgressUi(progressUi);
|
|
||||||
|
|
||||||
_statusLogger.StartLogging("", false);
|
|
||||||
_statusLogger.UpdateMessage(currentMessage);
|
|
||||||
_statusLogger.UpdateSubMessage(currentSubmessage);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetNewActiveContext(IKp2aApp app)
|
|
||||||
{
|
|
||||||
_app = app;
|
|
||||||
Context? context = app.ActiveContext;
|
|
||||||
bool isAppContext = context == null || (context.ApplicationContext == context);
|
|
||||||
lock (_taskQueueLock)
|
|
||||||
{
|
|
||||||
if (isAppContext && _thread != null)
|
|
||||||
{
|
|
||||||
//this will register the service as new active context (see BackgroundSyncService.OnStartCommand())
|
|
||||||
app.StartBackgroundSyncService();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentlyRunningTask?.RunBlocking == true && (context is Activity { IsFinishing: false, IsDestroyed:false}))
|
|
||||||
{
|
|
||||||
app.UiThreadHandler.Post(() =>
|
|
||||||
{
|
|
||||||
TrySetupProgressDialog();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var progressUi = (context as IProgressUiProvider)?.ProgressUi;
|
|
||||||
if (_statusLogger == null)
|
|
||||||
{
|
|
||||||
_statusLogger = new ProgressUiAsStatusLoggerAdapter(progressUi, app);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_statusLogger.SetNewProgressUi(progressUi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var task in _taskQueue.Concat(_currentlyRunningTask == null ?
|
|
||||||
new List<OperationWithMetadata>() : [_currentlyRunningTask.Value])
|
|
||||||
)
|
|
||||||
{
|
|
||||||
task.Operation.SetStatusLogger(_statusLogger);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CancelAll()
|
|
||||||
{
|
|
||||||
lock (_taskQueueLock)
|
|
||||||
{
|
|
||||||
if (_thread != null)
|
|
||||||
{
|
|
||||||
_thread.Interrupt();
|
|
||||||
_thread = null;
|
|
||||||
_statusLogger?.EndLogging();
|
|
||||||
}
|
|
||||||
|
|
||||||
_taskQueue.Clear();
|
|
||||||
_currentlyRunningTask = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,152 +22,116 @@ using KeePassLib.Interfaces;
|
|||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
public interface IKp2aStatusLogger : IStatusLogger
|
/// <summary>
|
||||||
{
|
/// StatusLogger implementation which shows the progress in a progress dialog
|
||||||
void UpdateMessage(UiStringKey stringKey);
|
/// </summary>
|
||||||
string LastMessage { get; }
|
public class ProgressDialogStatusLogger: IStatusLogger {
|
||||||
string LastSubMessage { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IProgressUi
|
|
||||||
{
|
|
||||||
void Show();
|
|
||||||
void Hide();
|
|
||||||
void UpdateMessage(String message);
|
|
||||||
void UpdateSubMessage(String submessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IProgressUiProvider
|
|
||||||
{
|
|
||||||
IProgressUi? ProgressUi { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class Kp2aNullStatusLogger : IKp2aStatusLogger
|
|
||||||
{
|
|
||||||
public void StartLogging(string strOperation, bool bWriteOperationToLog)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndLogging()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SetProgress(uint uPercent)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SetText(string strNewText, LogStatusType lsType)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _lastMessage;
|
|
||||||
private string _lastSubMessage;
|
|
||||||
public void UpdateMessage(string message)
|
|
||||||
{
|
|
||||||
_lastMessage = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateSubMessage(string submessage)
|
|
||||||
{
|
|
||||||
_lastSubMessage = submessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ContinueWork()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateMessage(UiStringKey stringKey)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LastMessage { get { return _lastMessage; } }
|
|
||||||
public string LastSubMessage { get { return _lastSubMessage; } }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// StatusLogger implementation which shows the progress in a progress dialog
|
|
||||||
/// </summary>
|
|
||||||
public class ProgressDialogUi: IProgressUi
|
|
||||||
{
|
|
||||||
private readonly IProgressDialog _progressDialog;
|
private readonly IProgressDialog _progressDialog;
|
||||||
|
readonly IKp2aApp _app;
|
||||||
private readonly Handler _handler;
|
private readonly Handler _handler;
|
||||||
private string _message = "";
|
private string _message = "";
|
||||||
private string _submessage;
|
private string _submessage;
|
||||||
private readonly IKp2aApp _app;
|
|
||||||
|
|
||||||
public String LastSubMessage => _submessage;
|
public String SubMessage => _submessage;
|
||||||
public String LastMessage => _message;
|
public String Message => _message;
|
||||||
|
|
||||||
|
public ProgressDialogStatusLogger() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public ProgressDialogUi(IKp2aApp app, Handler handler, IProgressDialog pd)
|
public ProgressDialogStatusLogger(IKp2aApp app, Handler handler, IProgressDialog pd) {
|
||||||
{
|
_app = app;
|
||||||
_app = app;
|
|
||||||
_progressDialog = pd;
|
_progressDialog = pd;
|
||||||
_handler = handler;
|
_handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateMessage(UiStringKey stringKey) {
|
||||||
|
if (_app != null)
|
||||||
|
UpdateMessage(_app.GetResourceString(stringKey));
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateSubMessage(String submessage)
|
public void UpdateMessage (String message)
|
||||||
{
|
{
|
||||||
Kp2aLog.Log("status submessage: " + submessage);
|
Kp2aLog.Log("status message: " + message);
|
||||||
_submessage = submessage;
|
|
||||||
if (_app != null && _progressDialog != null && _handler != null)
|
|
||||||
{
|
|
||||||
_handler.Post(() =>
|
|
||||||
{
|
|
||||||
if (!String.IsNullOrEmpty(submessage))
|
|
||||||
{
|
|
||||||
_progressDialog.SetMessage(_message + " (" + submessage + ")");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_progressDialog.SetMessage(_message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Show()
|
|
||||||
{
|
|
||||||
_handler.Post(() =>
|
|
||||||
{
|
|
||||||
_progressDialog?.Show();
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Hide()
|
|
||||||
{
|
|
||||||
_handler.Post(() =>
|
|
||||||
{
|
|
||||||
_progressDialog?.Dismiss();
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateMessage(string message)
|
|
||||||
{
|
|
||||||
Kp2aLog.Log("status message: " + message);
|
|
||||||
_message = message;
|
_message = message;
|
||||||
if (_app != null && _progressDialog != null && _handler != null)
|
if ( _app!= null && _progressDialog != null && _handler != null ) {
|
||||||
{
|
_handler.Post(() => {_progressDialog.SetMessage(message); } );
|
||||||
_handler.Post(() =>
|
}
|
||||||
{
|
}
|
||||||
_progressDialog.SetMessage(message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
public void UpdateSubMessage(String submessage)
|
||||||
|
{
|
||||||
|
Kp2aLog.Log("status submessage: " + submessage);
|
||||||
|
_submessage = submessage;
|
||||||
|
if (_app != null && _progressDialog != null && _handler != null)
|
||||||
|
{
|
||||||
|
_handler.Post(() =>
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(submessage))
|
||||||
|
{
|
||||||
|
_progressDialog.SetMessage(_message + " (" + submessage + ")");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_progressDialog.SetMessage(_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IStatusLogger implementation
|
||||||
|
|
||||||
|
public void StartLogging (string strOperation, bool bWriteOperationToLog)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndLogging ()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetProgress (uint uPercent)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetText (string strNewText, LogStatusType lsType)
|
||||||
|
{
|
||||||
|
if (strNewText.StartsWith("KP2AKEY_"))
|
||||||
|
{
|
||||||
|
UiStringKey key;
|
||||||
|
if (Enum.TryParse(strNewText.Substring("KP2AKEY_".Length), true, out key))
|
||||||
|
{
|
||||||
|
UpdateMessage(_app.GetResourceString(key), lsType);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateMessage(strNewText, lsType);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateMessage(string message, LogStatusType lsType)
|
||||||
|
{
|
||||||
|
if (lsType == LogStatusType.AdditionalInfo)
|
||||||
|
{
|
||||||
|
UpdateSubMessage(message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UpdateMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContinueWork ()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
179
src/Kp2aBusinessLogic/ProgressTask.cs
Normal file
179
src/Kp2aBusinessLogic/ProgressTask.cs
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
|
||||||
|
|
||||||
|
Keepass2Android is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Keepass2Android is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
|
using Java.Lang;
|
||||||
|
|
||||||
|
namespace keepass2android
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class to run a task while a progress dialog is shown
|
||||||
|
/// </summary>
|
||||||
|
public class ProgressTask
|
||||||
|
{
|
||||||
|
//for handling Activity recreation situations, we need access to the currently active task. It must hold that there is no more than one active task.
|
||||||
|
private static ProgressTask _currentTask = null;
|
||||||
|
|
||||||
|
public static void SetNewActiveActivity(Activity activeActivity)
|
||||||
|
{
|
||||||
|
if (_currentTask != null)
|
||||||
|
{
|
||||||
|
_currentTask.ActiveActivity = activeActivity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void RemoveActiveActivity(Activity activity)
|
||||||
|
{
|
||||||
|
if ((_currentTask != null) && (_currentTask._activeActivity == activity))
|
||||||
|
_currentTask.ActiveActivity = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Activity ActiveActivity
|
||||||
|
{
|
||||||
|
get { return _activeActivity; }
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (_activeActivity != null && _activeActivity != _previouslyActiveActivity)
|
||||||
|
{
|
||||||
|
_previouslyActiveActivity = _activeActivity;
|
||||||
|
|
||||||
|
}
|
||||||
|
_activeActivity = value;
|
||||||
|
if (_task != null)
|
||||||
|
_task.ActiveActivity = _activeActivity;
|
||||||
|
if (_activeActivity != null)
|
||||||
|
{
|
||||||
|
SetupProgressDialog(_app);
|
||||||
|
_progressDialog.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Activity PreviouslyActiveActivity
|
||||||
|
{
|
||||||
|
get { return _previouslyActiveActivity; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Handler _handler;
|
||||||
|
private readonly RunnableOnFinish _task;
|
||||||
|
private IProgressDialog _progressDialog;
|
||||||
|
private readonly IKp2aApp _app;
|
||||||
|
private Java.Lang.Thread _thread;
|
||||||
|
private Activity _activeActivity, _previouslyActiveActivity;
|
||||||
|
private ProgressDialogStatusLogger _progressDialogStatusLogger;
|
||||||
|
|
||||||
|
public ProgressTask(IKp2aApp app, Activity activity, RunnableOnFinish task)
|
||||||
|
{
|
||||||
|
_activeActivity = activity;
|
||||||
|
_task = task;
|
||||||
|
_handler = app.UiThreadHandler;
|
||||||
|
_app = app;
|
||||||
|
|
||||||
|
SetupProgressDialog(app);
|
||||||
|
|
||||||
|
// Set code to run when this is finished
|
||||||
|
_task.OnFinishToRun = new AfterTask(activity, task.OnFinishToRun, _handler, this);
|
||||||
|
|
||||||
|
_task.SetStatusLogger(_progressDialogStatusLogger);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupProgressDialog(IKp2aApp app)
|
||||||
|
{
|
||||||
|
string currentMessage = "Initializing...";
|
||||||
|
string currentSubmessage = "";
|
||||||
|
|
||||||
|
if (_progressDialogStatusLogger != null)
|
||||||
|
{
|
||||||
|
currentMessage = _progressDialogStatusLogger.Message;
|
||||||
|
currentSubmessage = _progressDialogStatusLogger.SubMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_progressDialog != null)
|
||||||
|
{
|
||||||
|
var pd = _progressDialog;
|
||||||
|
app.UiThreadHandler.Post(() =>
|
||||||
|
{
|
||||||
|
pd.Dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show process dialog
|
||||||
|
_progressDialog = app.CreateProgressDialog(_activeActivity);
|
||||||
|
_progressDialog.SetTitle(_app.GetResourceString(UiStringKey.progress_title));
|
||||||
|
_progressDialogStatusLogger = new ProgressDialogStatusLogger(_app, _handler, _progressDialog);
|
||||||
|
_progressDialogStatusLogger.UpdateMessage(currentMessage);
|
||||||
|
_progressDialogStatusLogger.UpdateSubMessage(currentSubmessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run(bool allowOverwriteCurrentTask = false)
|
||||||
|
{
|
||||||
|
if ((!allowOverwriteCurrentTask) && (_currentTask != null))
|
||||||
|
throw new System.Exception("Cannot start another ProgressTask while ProgressTask is already running! " + _task.GetType().Name + "/" + _currentTask._task.GetType().Name);
|
||||||
|
_currentTask = this;
|
||||||
|
|
||||||
|
// Show process dialog
|
||||||
|
_progressDialog.Show();
|
||||||
|
|
||||||
|
|
||||||
|
// Start Thread to Run task
|
||||||
|
_thread = new Java.Lang.Thread(_task.Run);
|
||||||
|
_thread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void JoinWorkerThread()
|
||||||
|
{
|
||||||
|
_thread.Join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AfterTask : OnFinish {
|
||||||
|
readonly ProgressTask _progressTask;
|
||||||
|
|
||||||
|
public AfterTask (Activity activity, OnFinish finish, Handler handler, ProgressTask pt): base(activity, finish, handler)
|
||||||
|
{
|
||||||
|
_progressTask = pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Run() {
|
||||||
|
base.Run();
|
||||||
|
|
||||||
|
if (Handler != null) //can be null in tests
|
||||||
|
{
|
||||||
|
// Remove the progress dialog
|
||||||
|
Handler.Post(delegate
|
||||||
|
{
|
||||||
|
_progressTask._progressDialog.Dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_progressTask._progressDialog.Dismiss();
|
||||||
|
}
|
||||||
|
_currentTask = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
using KeePassLib.Interfaces;
|
|
||||||
|
|
||||||
namespace keepass2android;
|
|
||||||
|
|
||||||
public class ProgressUiAsStatusLoggerAdapter : IKp2aStatusLogger
|
|
||||||
{
|
|
||||||
private IProgressUi? _progressUi;
|
|
||||||
private readonly IKp2aApp _app;
|
|
||||||
|
|
||||||
private string _lastMessage = "";
|
|
||||||
private string _lastSubMessage = "";
|
|
||||||
private bool _isVisible = false;
|
|
||||||
|
|
||||||
public ProgressUiAsStatusLoggerAdapter(IProgressUi progressUi, IKp2aApp app)
|
|
||||||
{
|
|
||||||
_progressUi = progressUi;
|
|
||||||
_app = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetNewProgressUi(IProgressUi progressUi)
|
|
||||||
{
|
|
||||||
_progressUi?.Hide();
|
|
||||||
_progressUi = progressUi;
|
|
||||||
if (_isVisible)
|
|
||||||
{
|
|
||||||
progressUi?.Show();
|
|
||||||
progressUi?.UpdateMessage(_lastMessage);
|
|
||||||
progressUi?.UpdateSubMessage(_lastSubMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
progressUi?.Hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartLogging(string strOperation, bool bWriteOperationToLog)
|
|
||||||
{
|
|
||||||
_progressUi?.Show();
|
|
||||||
_isVisible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndLogging()
|
|
||||||
{
|
|
||||||
_progressUi?.Hide();
|
|
||||||
_isVisible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SetProgress(uint uPercent)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SetText(string strNewText, LogStatusType lsType)
|
|
||||||
{
|
|
||||||
if (strNewText.StartsWith("KP2AKEY_"))
|
|
||||||
{
|
|
||||||
UiStringKey key;
|
|
||||||
if (Enum.TryParse(strNewText.Substring("KP2AKEY_".Length), true, out key))
|
|
||||||
{
|
|
||||||
UpdateMessage(_app.GetResourceString(key));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UpdateMessage(strNewText);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateMessage(string message)
|
|
||||||
{
|
|
||||||
_progressUi?.UpdateMessage(message);
|
|
||||||
_lastMessage = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateSubMessage(string submessage)
|
|
||||||
{
|
|
||||||
_progressUi?.UpdateSubMessage(submessage);
|
|
||||||
_lastSubMessage = submessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ContinueWork()
|
|
||||||
{
|
|
||||||
return !Java.Lang.Thread.Interrupted();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateMessage(UiStringKey stringKey)
|
|
||||||
{
|
|
||||||
if (_app != null)
|
|
||||||
UpdateMessage(_app.GetResourceString(stringKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LastMessage { get { return _lastMessage; } }
|
|
||||||
public string LastSubMessage { get { return _lastSubMessage; } }
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -16,7 +15,14 @@ namespace KeePass.Util
|
|||||||
string errorMessage = e.Message;
|
string errorMessage = e.Message;
|
||||||
if (e is Java.Lang.Exception javaException)
|
if (e is Java.Lang.Exception javaException)
|
||||||
{
|
{
|
||||||
errorMessage = javaException.Message ?? errorMessage;
|
try
|
||||||
|
{
|
||||||
|
errorMessage = javaException.LocalizedMessage ?? javaException.Message ?? errorMessage;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
|
|||||||
@@ -13,15 +13,16 @@ using keepass2android.Io;
|
|||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
public class CheckDatabaseForChanges: OperationWithFinishHandler
|
public class CheckDatabaseForChanges: RunnableOnFinish
|
||||||
{
|
{
|
||||||
private readonly Context _context;
|
private readonly Context _context;
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
|
|
||||||
|
|
||||||
public CheckDatabaseForChanges(IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler)
|
public CheckDatabaseForChanges(Activity context, IKp2aApp app, OnFinish finish)
|
||||||
: base(app, operationFinishedHandler)
|
: base(context, finish)
|
||||||
{
|
{
|
||||||
|
_context = context;
|
||||||
_app = app;
|
_app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,14 +85,17 @@ namespace keepass2android
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Do not call this method directly. Call App.Kp2a.LoadDatabase instead.
|
/// Do not call this method directly. Call App.Kp2a.LoadDatabase instead.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, CompositeKey compositeKey, IKp2aStatusLogger status, IDatabaseFormat databaseFormat)
|
public void LoadData(IKp2aApp app, IOConnectionInfo iocInfo, MemoryStream databaseData, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseFormat databaseFormat)
|
||||||
{
|
{
|
||||||
PwDatabase pwDatabase = new PwDatabase();
|
PwDatabase pwDatabase = new PwDatabase();
|
||||||
|
|
||||||
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
|
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
|
||||||
Stream s = databaseData ?? fileStorage.OpenFileForRead(iocInfo);
|
Kp2aLog.Log("LoadData: Retrieving stream");
|
||||||
var fileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(iocInfo);
|
Stream s = databaseData ?? fileStorage.OpenFileForRead(iocInfo);
|
||||||
PopulateDatabaseFromStream(pwDatabase, s, iocInfo, compositeKey, status, databaseFormat);
|
Kp2aLog.Log("LoadData: GetCurrentFileVersion");
|
||||||
|
var fileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(iocInfo);
|
||||||
|
Kp2aLog.Log("LoadData: PopulateDatabaseFromStream");
|
||||||
|
PopulateDatabaseFromStream(pwDatabase, s, iocInfo, compositeKey, status, databaseFormat);
|
||||||
LastFileVersion = fileVersion;
|
LastFileVersion = fileVersion;
|
||||||
|
|
||||||
status.UpdateSubMessage("");
|
status.UpdateSubMessage("");
|
||||||
@@ -149,7 +152,7 @@ namespace keepass2android
|
|||||||
get { return GetFingerprintModePrefKey(Ioc); }
|
get { return GetFingerprintModePrefKey(Ioc); }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, IKp2aStatusLogger status, IDatabaseFormat databaseFormat)
|
protected virtual void PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseFormat databaseFormat)
|
||||||
{
|
{
|
||||||
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
|
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);
|
||||||
var filename = fileStorage.GetFilenameWithoutPathAndExt(iocInfo);
|
var filename = fileStorage.GetFilenameWithoutPathAndExt(iocInfo);
|
||||||
@@ -194,9 +197,9 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void SaveData(IFileStorage fileStorage) {
|
public void SaveData() {
|
||||||
|
|
||||||
using (IWriteTransaction trans = fileStorage.OpenWriteTransaction(Ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
|
using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
|
||||||
{
|
{
|
||||||
DatabaseFormat.Save(KpDatabase, trans.OpenFile());
|
DatabaseFormat.Save(KpDatabase, trans.OpenFile());
|
||||||
|
|
||||||
|
|||||||
@@ -396,6 +396,8 @@ namespace keepass2android
|
|||||||
{
|
{
|
||||||
PwGroupV3 toGroup = new PwGroupV3();
|
PwGroupV3 toGroup = new PwGroupV3();
|
||||||
toGroup.Name = fromGroup.Name;
|
toGroup.Name = fromGroup.Name;
|
||||||
|
//todo remove
|
||||||
|
Android.Util.Log.Debug("KP2A", "save kdb: group " + fromGroup.Name);
|
||||||
|
|
||||||
toGroup.TCreation = new PwDate(ConvertTime(fromGroup.CreationTime));
|
toGroup.TCreation = new PwDate(ConvertTime(fromGroup.CreationTime));
|
||||||
toGroup.TLastAccess= new PwDate(ConvertTime(fromGroup.LastAccessTime));
|
toGroup.TLastAccess= new PwDate(ConvertTime(fromGroup.LastAccessTime));
|
||||||
|
|||||||
@@ -4,152 +4,109 @@ using System.IO;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
using keepass2android.Io;
|
using keepass2android.Io;
|
||||||
using KeePass.Util;
|
using KeePass.Util;
|
||||||
using Group.Pals.Android.Lib.UI.Filechooser.Utils;
|
|
||||||
using KeePassLib;
|
|
||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
public class SynchronizeCachedDatabase: OperationWithFinishHandler
|
public class SynchronizeCachedDatabase: RunnableOnFinish
|
||||||
{
|
{
|
||||||
|
private readonly Activity _context;
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
private IDatabaseModificationWatcher _modificationWatcher;
|
private SaveDb _saveDb;
|
||||||
private readonly Database _database;
|
|
||||||
|
|
||||||
|
public SynchronizeCachedDatabase(Activity context, IKp2aApp app, OnFinish finish)
|
||||||
public SynchronizeCachedDatabase(IKp2aApp app, Database database, OnOperationFinishedHandler operationFinishedHandler, IDatabaseModificationWatcher modificationWatcher)
|
: base(context, finish)
|
||||||
: base(app, operationFinishedHandler)
|
{
|
||||||
{
|
_context = context;
|
||||||
_app = app;
|
_app = app;
|
||||||
_database = database;
|
}
|
||||||
_modificationWatcher = modificationWatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run()
|
public override void Run()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IOConnectionInfo ioc = _database.Ioc;
|
IOConnectionInfo ioc = _app.CurrentDb.Ioc;
|
||||||
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
||||||
if (!(fileStorage is CachingFileStorage))
|
if (!(fileStorage is CachingFileStorage))
|
||||||
{
|
{
|
||||||
throw new Exception("Cannot sync a non-cached database!");
|
throw new Exception("Cannot sync a non-cached database!");
|
||||||
}
|
}
|
||||||
|
StatusLogger.UpdateMessage(UiStringKey.SynchronizingCachedDatabase);
|
||||||
|
CachingFileStorage cachingFileStorage = (CachingFileStorage) fileStorage;
|
||||||
|
|
||||||
StatusLogger.UpdateMessage(UiStringKey.SynchronizingCachedDatabase);
|
//download file from remote location and calculate hash:
|
||||||
CachingFileStorage cachingFileStorage = (CachingFileStorage)fileStorage;
|
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.DownloadingRemoteFile));
|
||||||
|
string hash;
|
||||||
//download file from remote location and calculate hash:
|
|
||||||
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.DownloadingRemoteFile));
|
MemoryStream remoteData;
|
||||||
string hash;
|
try
|
||||||
|
{
|
||||||
MemoryStream remoteData;
|
remoteData = cachingFileStorage.GetRemoteDataAndHash(ioc, out hash);
|
||||||
try
|
Kp2aLog.Log("Checking for file change. Current hash = " + hash);
|
||||||
{
|
}
|
||||||
remoteData = cachingFileStorage.GetRemoteDataAndHash(ioc, out hash);
|
catch (FileNotFoundException)
|
||||||
Kp2aLog.Log("Checking for file change. Current hash = " + hash);
|
{
|
||||||
}
|
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.RestoringRemoteFile));
|
||||||
catch (FileNotFoundException)
|
cachingFileStorage.UpdateRemoteFile(ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
|
||||||
{
|
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
||||||
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.RestoringRemoteFile));
|
|
||||||
cachingFileStorage.UpdateRemoteFile(ioc,
|
|
||||||
_app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
|
|
||||||
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
|
||||||
Kp2aLog.Log("Checking for file change: file not found");
|
Kp2aLog.Log("Checking for file change: file not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if remote file was modified:
|
//check if remote file was modified:
|
||||||
var baseVersionHash = cachingFileStorage.GetBaseVersionHash(ioc);
|
var baseVersionHash = cachingFileStorage.GetBaseVersionHash(ioc);
|
||||||
Kp2aLog.Log("Checking for file change. baseVersionHash = " + baseVersionHash);
|
Kp2aLog.Log("Checking for file change. baseVersionHash = " + baseVersionHash);
|
||||||
if (baseVersionHash != hash)
|
if (baseVersionHash != hash)
|
||||||
{
|
{
|
||||||
//remote file is modified
|
//remote file is modified
|
||||||
if (cachingFileStorage.HasLocalChanges(ioc))
|
if (cachingFileStorage.HasLocalChanges(ioc))
|
||||||
{
|
{
|
||||||
//conflict! need to merge
|
//conflict! need to merge
|
||||||
var _saveDb = new SaveDb(_app, new ActionOnOperationFinished(_app,
|
_saveDb = new SaveDb(_context, _app, new ActionOnFinish(ActiveActivity, (success, result, activity) =>
|
||||||
(success, result, activity) =>
|
{
|
||||||
{
|
if (!success)
|
||||||
if (!success)
|
{
|
||||||
{
|
Finish(false, result);
|
||||||
Finish(false, result);
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
||||||
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
}
|
||||||
}
|
_saveDb = null;
|
||||||
}), _database, false, remoteData, _modificationWatcher);
|
}), _app.CurrentDb, false, remoteData);
|
||||||
_saveDb.SetStatusLogger(StatusLogger);
|
_saveDb.Run();
|
||||||
_saveDb.DoNotSetStatusLoggerMessage = true; //Keep "sync db" as main message
|
|
||||||
_saveDb.SyncInBackground = false;
|
|
||||||
_saveDb.Run();
|
|
||||||
|
|
||||||
_database.UpdateGlobals();
|
_app.CurrentDb.UpdateGlobals();
|
||||||
|
|
||||||
_app.MarkAllGroupsAsDirty();
|
_app.MarkAllGroupsAsDirty();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//only the remote file was modified -> reload database.
|
//only the remote file was modified -> reload database.
|
||||||
var onFinished = new ActionOnOperationFinished(_app, (success, result, activity) =>
|
//note: it's best to lock the database and do a complete reload here (also better for UI consistency in case something goes wrong etc.)
|
||||||
{
|
_app.TriggerReload(_context, (bool result) => Finish(result));
|
||||||
if (!success)
|
}
|
||||||
{
|
}
|
||||||
Finish(false, result);
|
else
|
||||||
}
|
{
|
||||||
else
|
//remote file is unmodified
|
||||||
{
|
if (cachingFileStorage.HasLocalChanges(ioc))
|
||||||
new Handler(Looper.MainLooper).Post(() =>
|
{
|
||||||
{
|
//but we have local changes -> upload:
|
||||||
_database.UpdateGlobals();
|
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.UploadingFile));
|
||||||
|
cachingFileStorage.UpdateRemoteFile(ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
|
||||||
_app.MarkAllGroupsAsDirty();
|
StatusLogger.UpdateSubMessage("");
|
||||||
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
||||||
});
|
}
|
||||||
|
else
|
||||||
}
|
{
|
||||||
});
|
//files are in sync: just set the result
|
||||||
var _loadDb = new LoadDb(_app, ioc, Task.FromResult(remoteData),
|
Finish(true, _app.GetResourceString(UiStringKey.FilesInSync));
|
||||||
_database.KpDatabase.MasterKey, null, onFinished, true, false, _modificationWatcher);
|
}
|
||||||
_loadDb.SetStatusLogger(StatusLogger);
|
}
|
||||||
_loadDb.DoNotSetStatusLoggerMessage = true; //Keep "sync db" as main message
|
}
|
||||||
_loadDb.Run();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//remote file is unmodified
|
|
||||||
if (cachingFileStorage.HasLocalChanges(ioc))
|
|
||||||
{
|
|
||||||
//but we have local changes -> upload:
|
|
||||||
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.UploadingFile));
|
|
||||||
cachingFileStorage.UpdateRemoteFile(ioc,
|
|
||||||
_app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
|
|
||||||
StatusLogger.UpdateSubMessage("");
|
|
||||||
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//files are in sync: just set the result
|
|
||||||
Finish(true, _app.GetResourceString(UiStringKey.FilesInSync));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Java.Lang.InterruptedException e)
|
|
||||||
{
|
|
||||||
Kp2aLog.LogUnexpectedError(e);
|
|
||||||
//no Finish()
|
|
||||||
}
|
|
||||||
catch (Java.IO.InterruptedIOException e)
|
|
||||||
{
|
|
||||||
Kp2aLog.LogUnexpectedError(e);
|
|
||||||
//no Finish()
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Kp2aLog.LogUnexpectedError(e);
|
Kp2aLog.LogUnexpectedError(e);
|
||||||
@@ -158,5 +115,10 @@ namespace keepass2android
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void JoinWorkerThread()
|
||||||
|
{
|
||||||
|
if (_saveDb != null)
|
||||||
|
_saveDb.JoinWorkerThread();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
57
src/Kp2aBusinessLogic/database/edit/ActionOnFinish.cs
Normal file
57
src/Kp2aBusinessLogic/database/edit/ActionOnFinish.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll.
|
||||||
|
|
||||||
|
Keepass2Android is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Keepass2Android is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Android.App;
|
||||||
|
using Android.OS;
|
||||||
|
|
||||||
|
namespace keepass2android
|
||||||
|
{
|
||||||
|
public class ActionOnFinish: OnFinish
|
||||||
|
{
|
||||||
|
public delegate void ActionToPerformOnFinsh(bool success, String message, Activity activeActivity);
|
||||||
|
|
||||||
|
readonly ActionToPerformOnFinsh _actionToPerform;
|
||||||
|
|
||||||
|
public ActionOnFinish(Activity activity, ActionToPerformOnFinsh actionToPerform) : base(activity, null, null)
|
||||||
|
{
|
||||||
|
_actionToPerform = actionToPerform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionOnFinish(Activity activity, ActionToPerformOnFinsh actionToPerform, OnFinish finish) : base(activity, finish)
|
||||||
|
{
|
||||||
|
_actionToPerform = actionToPerform;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if set to true, the previously active active will be passed to ActionToPerformOnFinish instead null if no activity is on foreground
|
||||||
|
public bool AllowInactiveActivity { get; set; }
|
||||||
|
|
||||||
|
public override void Run()
|
||||||
|
{
|
||||||
|
if (Message == null)
|
||||||
|
Message = "";
|
||||||
|
if (Handler != null)
|
||||||
|
{
|
||||||
|
Handler.Post(() => {_actionToPerform(Success, Message, ActiveActivity);});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_actionToPerform(Success, Message, AllowInactiveActivity ? (ActiveActivity ?? PreviouslyActiveActivity) : ActiveActivity);
|
||||||
|
base.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll.
|
|
||||||
|
|
||||||
Keepass2Android is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Keepass2Android is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using Android.OS;
|
|
||||||
using keepass2android;
|
|
||||||
|
|
||||||
namespace keepass2android
|
|
||||||
{
|
|
||||||
public class ActionOnOperationFinished: OnOperationFinishedHandler
|
|
||||||
{
|
|
||||||
public delegate void ActionToPerformOnFinsh(bool success, String message, Context activeContext);
|
|
||||||
|
|
||||||
readonly ActionToPerformOnFinsh _actionToPerform;
|
|
||||||
|
|
||||||
public ActionOnOperationFinished(IKp2aApp app, ActionToPerformOnFinsh actionToPerform) : base(app, null, null)
|
|
||||||
{
|
|
||||||
_actionToPerform = actionToPerform;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionOnOperationFinished(IKp2aApp app, ActionToPerformOnFinsh actionToPerform, OnOperationFinishedHandler operationFinishedHandler) : base(app, operationFinishedHandler)
|
|
||||||
{
|
|
||||||
_actionToPerform = actionToPerform;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run()
|
|
||||||
{
|
|
||||||
if (Message == null)
|
|
||||||
Message = "";
|
|
||||||
if (Handler != null)
|
|
||||||
{
|
|
||||||
Handler.Post(() =>
|
|
||||||
{
|
|
||||||
_actionToPerform(Success, Message, ActiveContext);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_actionToPerform(Success, Message, ActiveContext);
|
|
||||||
}
|
|
||||||
base.Run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Action which runs when the contextInstanceId is the active context
|
|
||||||
// otherwise it is registered as pending action for the context instance.
|
|
||||||
public class ActionInContextInstanceOnOperationFinished : ActionOnOperationFinished
|
|
||||||
{
|
|
||||||
private readonly int _contextInstanceId;
|
|
||||||
private IKp2aApp _app;
|
|
||||||
|
|
||||||
public ActionInContextInstanceOnOperationFinished(int contextInstanceId, IKp2aApp app, ActionToPerformOnFinsh actionToPerform) : base(app, actionToPerform)
|
|
||||||
{
|
|
||||||
_contextInstanceId = contextInstanceId;
|
|
||||||
_app = app;
|
|
||||||
}
|
|
||||||
public ActionInContextInstanceOnOperationFinished(int contextInstanceId, IKp2aApp app, ActionToPerformOnFinsh actionToPerform, OnOperationFinishedHandler operationFinishedHandler) : base(app, actionToPerform, operationFinishedHandler)
|
|
||||||
{
|
|
||||||
_contextInstanceId = contextInstanceId;
|
|
||||||
_app = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run()
|
|
||||||
{
|
|
||||||
if ((ActiveContext as IContextInstanceIdProvider)?.ContextInstanceId != _contextInstanceId)
|
|
||||||
{
|
|
||||||
_app.RegisterPendingActionForContextInstance(_contextInstanceId, this);
|
|
||||||
}
|
|
||||||
else _app.UiThreadHandler.Post(() => base.Run());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ using KeePassLib;
|
|||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
public class AddEntry : OperationWithFinishHandler {
|
public class AddEntry : RunnableOnFinish {
|
||||||
protected Database Db
|
protected Database Db
|
||||||
{
|
{
|
||||||
get { return _app.CurrentDb; }
|
get { return _app.CurrentDb; }
|
||||||
@@ -30,20 +30,22 @@ namespace keepass2android
|
|||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
private readonly PwEntry _entry;
|
private readonly PwEntry _entry;
|
||||||
private readonly PwGroup _parentGroup;
|
private readonly PwGroup _parentGroup;
|
||||||
|
private readonly Activity _ctx;
|
||||||
private readonly Database _db;
|
private readonly Database _db;
|
||||||
|
|
||||||
public static AddEntry GetInstance(IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnOperationFinishedHandler operationFinishedHandler, Database db) {
|
public static AddEntry GetInstance(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish, Database db) {
|
||||||
|
|
||||||
return new AddEntry(db, app, entry, parentGroup, operationFinishedHandler);
|
return new AddEntry(ctx, db, app, entry, parentGroup, finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddEntry(Database db, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnOperationFinishedHandler operationFinishedHandler):base(app, operationFinishedHandler) {
|
public AddEntry(Activity ctx, Database db, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
|
||||||
|
_ctx = ctx;
|
||||||
_db = db;
|
_db = db;
|
||||||
_parentGroup = parentGroup;
|
_parentGroup = parentGroup;
|
||||||
_app = app;
|
_app = app;
|
||||||
_entry = entry;
|
_entry = entry;
|
||||||
|
|
||||||
_operationFinishedHandler = new AfterAdd(app.CurrentDb, entry, app,operationFinishedHandler);
|
_onFinishToRun = new AfterAdd(ctx, app.CurrentDb, entry, app,OnFinishToRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -63,17 +65,17 @@ namespace keepass2android
|
|||||||
_db.Elements.Add(_entry);
|
_db.Elements.Add(_entry);
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDb save = new SaveDb(_app, _app.CurrentDb, operationFinishedHandler);
|
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterAdd : OnOperationFinishedHandler {
|
private class AfterAdd : OnFinish {
|
||||||
private readonly Database _db;
|
private readonly Database _db;
|
||||||
private readonly PwEntry _entry;
|
private readonly PwEntry _entry;
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
|
|
||||||
public AfterAdd( Database db, PwEntry entry, IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler):base(app, operationFinishedHandler) {
|
public AfterAdd(Activity activity, Database db, PwEntry entry, IKp2aApp app, OnFinish finish):base(activity, finish) {
|
||||||
_db = db;
|
_db = db;
|
||||||
_entry = entry;
|
_entry = entry;
|
||||||
_app = app;
|
_app = app;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ using KeePassLib;
|
|||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
|
|
||||||
public class AddGroup : OperationWithFinishHandler {
|
public class AddGroup : RunnableOnFinish {
|
||||||
internal Database Db
|
internal Database Db
|
||||||
{
|
{
|
||||||
get { return _app.CurrentDb; }
|
get { return _app.CurrentDb; }
|
||||||
@@ -38,16 +38,18 @@ namespace keepass2android
|
|||||||
public PwGroup Group;
|
public PwGroup Group;
|
||||||
internal PwGroup Parent;
|
internal PwGroup Parent;
|
||||||
protected bool DontSave;
|
protected bool DontSave;
|
||||||
|
readonly Activity _ctx;
|
||||||
|
|
||||||
public static AddGroup GetInstance(IKp2aApp app, string name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnOperationFinishedHandler operationFinishedHandler, bool dontSave) {
|
|
||||||
return new AddGroup(app, name, iconid, groupCustomIconId, parent, operationFinishedHandler, dontSave);
|
public static AddGroup GetInstance(Activity ctx, IKp2aApp app, string name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnFinish finish, bool dontSave) {
|
||||||
|
return new AddGroup(ctx, app, name, iconid, groupCustomIconId, parent, finish, dontSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private AddGroup(IKp2aApp app, String name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnOperationFinishedHandler operationFinishedHandler, bool dontSave)
|
private AddGroup(Activity ctx, IKp2aApp app, String name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnFinish finish, bool dontSave)
|
||||||
: base(app, operationFinishedHandler)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
|
_ctx = ctx;
|
||||||
_name = name;
|
_name = name;
|
||||||
_iconId = iconid;
|
_iconId = iconid;
|
||||||
_groupCustomIconId = groupCustomIconId;
|
_groupCustomIconId = groupCustomIconId;
|
||||||
@@ -55,7 +57,7 @@ namespace keepass2android
|
|||||||
DontSave = dontSave;
|
DontSave = dontSave;
|
||||||
_app = app;
|
_app = app;
|
||||||
|
|
||||||
_operationFinishedHandler = new AfterAdd(_app, this, operationFinishedHandler);
|
_onFinishToRun = new AfterAdd(ctx, this, OnFinishToRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -72,15 +74,15 @@ namespace keepass2android
|
|||||||
_app.CurrentDb.Elements.Add(Group);
|
_app.CurrentDb.Elements.Add(Group);
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDb save = new SaveDb(_app, _app.CurrentDb, operationFinishedHandler, DontSave, null);
|
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, DontSave);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterAdd : OnOperationFinishedHandler {
|
private class AfterAdd : OnFinish {
|
||||||
readonly AddGroup _addGroup;
|
readonly AddGroup _addGroup;
|
||||||
|
|
||||||
public AfterAdd(IKp2aApp app, AddGroup addGroup,OnOperationFinishedHandler operationFinishedHandler): base(app, operationFinishedHandler) {
|
public AfterAdd(Activity activity, AddGroup addGroup,OnFinish finish): base(activity, finish) {
|
||||||
_addGroup = addGroup;
|
_addGroup = addGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ using KeePassLib.Utility;
|
|||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
public class AddTemplateEntries : OperationWithFinishHandler {
|
public class AddTemplateEntries : RunnableOnFinish {
|
||||||
|
|
||||||
public class TemplateEntry
|
public class TemplateEntry
|
||||||
{
|
{
|
||||||
@@ -130,13 +130,15 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
|
private readonly Activity _ctx;
|
||||||
public AddTemplateEntries(IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler)
|
|
||||||
: base(app, operationFinishedHandler)
|
public AddTemplateEntries(Activity ctx, IKp2aApp app, OnFinish finish)
|
||||||
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
_app = app;
|
_ctx = ctx;
|
||||||
|
_app = app;
|
||||||
|
|
||||||
//_operationFinishedHandler = new AfterAdd(this, operationFinishedHandler);
|
//_onFinishToRun = new AfterAdd(this, OnFinishToRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly List<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
|
public static readonly List<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
|
||||||
@@ -311,7 +313,7 @@ namespace keepass2android
|
|||||||
_app.DirtyGroups.Add(templateGroup);
|
_app.DirtyGroups.Add(templateGroup);
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDb save = new SaveDb(_app, _app.CurrentDb, operationFinishedHandler);
|
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
@@ -335,6 +337,7 @@ namespace keepass2android
|
|||||||
_app.DirtyGroups.Add(_app.CurrentDb.KpDatabase.RootGroup);
|
_app.DirtyGroups.Add(_app.CurrentDb.KpDatabase.RootGroup);
|
||||||
_app.CurrentDb.GroupsById[templateGroup.Uuid] = templateGroup;
|
_app.CurrentDb.GroupsById[templateGroup.Uuid] = templateGroup;
|
||||||
_app.CurrentDb.Elements.Add(templateGroup);
|
_app.CurrentDb.Elements.Add(templateGroup);
|
||||||
|
|
||||||
}
|
}
|
||||||
addedEntries = new List<PwEntry>();
|
addedEntries = new List<PwEntry>();
|
||||||
|
|
||||||
@@ -366,11 +369,11 @@ namespace keepass2android
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterAdd : OnOperationFinishedHandler {
|
private class AfterAdd : OnFinish {
|
||||||
private readonly Database _db;
|
private readonly Database _db;
|
||||||
private readonly List<PwEntry> _entries;
|
private readonly List<PwEntry> _entries;
|
||||||
|
|
||||||
public AfterAdd(IKp2aApp app, Database db, List<PwEntry> entries, OnOperationFinishedHandler operationFinishedHandler):base(app, operationFinishedHandler) {
|
public AfterAdd(Activity activity, Database db, List<PwEntry> entries, OnFinish finish):base(activity, finish) {
|
||||||
_db = db;
|
_db = db;
|
||||||
_entries = entries;
|
_entries = entries;
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ namespace keepass2android.database.edit
|
|||||||
{
|
{
|
||||||
public class CopyEntry: AddEntry
|
public class CopyEntry: AddEntry
|
||||||
{
|
{
|
||||||
public CopyEntry(IKp2aApp app, PwEntry entry, OnOperationFinishedHandler operationFinishedHandler, Database db)
|
public CopyEntry(Activity ctx, IKp2aApp app, PwEntry entry, OnFinish finish, Database db)
|
||||||
: base(db, app, CreateCopy(entry, app), entry.ParentGroup, operationFinishedHandler)
|
: base(ctx, db, app, CreateCopy(entry, app), entry.ParentGroup, finish)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,24 +26,27 @@ using KeePassLib.Keys;
|
|||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
|
|
||||||
public class CreateDb : OperationWithFinishHandler {
|
public class CreateDb : RunnableOnFinish {
|
||||||
private readonly IOConnectionInfo _ioc;
|
private readonly IOConnectionInfo _ioc;
|
||||||
private readonly bool _dontSave;
|
private readonly bool _dontSave;
|
||||||
|
private readonly Activity _ctx;
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
private CompositeKey _key;
|
private CompositeKey _key;
|
||||||
private readonly bool _makeCurrent;
|
private readonly bool _makeCurrent;
|
||||||
|
|
||||||
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnOperationFinishedHandler operationFinishedHandler, bool dontSave, bool makeCurrent): base(app, operationFinishedHandler) {
|
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, bool makeCurrent): base(ctx, finish) {
|
||||||
_ioc = ioc;
|
_ctx = ctx;
|
||||||
|
_ioc = ioc;
|
||||||
_dontSave = dontSave;
|
_dontSave = dontSave;
|
||||||
_makeCurrent = makeCurrent;
|
_makeCurrent = makeCurrent;
|
||||||
_app = app;
|
_app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnOperationFinishedHandler operationFinishedHandler, bool dontSave, CompositeKey key, bool makeCurrent)
|
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, CompositeKey key, bool makeCurrent)
|
||||||
: base(app, operationFinishedHandler)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
_ioc = ioc;
|
_ctx = ctx;
|
||||||
|
_ioc = ioc;
|
||||||
_dontSave = dontSave;
|
_dontSave = dontSave;
|
||||||
_app = app;
|
_app = app;
|
||||||
_key = key;
|
_key = key;
|
||||||
@@ -74,19 +77,19 @@ namespace keepass2android
|
|||||||
db.SearchHelper = new SearchDbHelper(_app);
|
db.SearchHelper = new SearchDbHelper(_app);
|
||||||
|
|
||||||
// Add a couple default groups
|
// Add a couple default groups
|
||||||
AddGroup internet = AddGroup.GetInstance(_app, "Internet", 1, null, db.KpDatabase.RootGroup, null, true);
|
AddGroup internet = AddGroup.GetInstance(_ctx, _app, "Internet", 1, null, db.KpDatabase.RootGroup, null, true);
|
||||||
internet.Run();
|
internet.Run();
|
||||||
AddGroup email = AddGroup.GetInstance(_app, "eMail", 19, null, db.KpDatabase.RootGroup, null, true);
|
AddGroup email = AddGroup.GetInstance(_ctx, _app, "eMail", 19, null, db.KpDatabase.RootGroup, null, true);
|
||||||
email.Run();
|
email.Run();
|
||||||
|
|
||||||
List<PwEntry> addedEntries;
|
List<PwEntry> addedEntries;
|
||||||
AddTemplateEntries addTemplates = new AddTemplateEntries(_app, null);
|
AddTemplateEntries addTemplates = new AddTemplateEntries(_ctx, _app, null);
|
||||||
addTemplates.AddTemplates(out addedEntries);
|
addTemplates.AddTemplates(out addedEntries);
|
||||||
|
|
||||||
// Commit changes
|
// Commit changes
|
||||||
SaveDb save = new SaveDb(_app, db, operationFinishedHandler, _dontSave, null);
|
SaveDb save = new SaveDb(_ctx, _app, db, OnFinishToRun, _dontSave);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
_operationFinishedHandler = null;
|
_onFinishToRun = null;
|
||||||
save.Run();
|
save.Run();
|
||||||
|
|
||||||
db.UpdateGlobals();
|
db.UpdateGlobals();
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
using Java.Lang;
|
|
||||||
|
|
||||||
namespace keepass2android;
|
|
||||||
|
|
||||||
public interface IDatabaseModificationWatcher
|
|
||||||
{
|
|
||||||
void BeforeModifyDatabases();
|
|
||||||
void AfterModifyDatabases();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NullDatabaseModificationWatcher : IDatabaseModificationWatcher
|
|
||||||
{
|
|
||||||
public void BeforeModifyDatabases() { }
|
|
||||||
public void AfterModifyDatabases() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BackgroundDatabaseModificationLocker(IKp2aApp app) : IDatabaseModificationWatcher
|
|
||||||
{
|
|
||||||
public void BeforeModifyDatabases()
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (app.DatabasesBackgroundModificationLock.TryEnterWriteLock(TimeSpan.FromSeconds(0.1)))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Java.Lang.Thread.Interrupted())
|
|
||||||
{
|
|
||||||
throw new InterruptedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AfterModifyDatabases()
|
|
||||||
{
|
|
||||||
app.DatabasesBackgroundModificationLock.ExitWriteLock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,8 +29,8 @@ namespace keepass2android
|
|||||||
private readonly PwEntry _entry;
|
private readonly PwEntry _entry;
|
||||||
private UiStringKey _statusMessage;
|
private UiStringKey _statusMessage;
|
||||||
|
|
||||||
public DeleteEntry(IKp2aApp app, PwEntry entry, OnOperationFinishedHandler operationFinishedHandler):base(operationFinishedHandler, app) {
|
public DeleteEntry(Activity activiy, IKp2aApp app, PwEntry entry, OnFinish finish):base(activiy, finish, app) {
|
||||||
|
Ctx = activiy;
|
||||||
Db = app.FindDatabaseForElement(entry);
|
Db = app.FindDatabaseForElement(entry);
|
||||||
_entry = entry;
|
_entry = entry;
|
||||||
|
|
||||||
|
|||||||
@@ -29,25 +29,25 @@ namespace keepass2android
|
|||||||
private PwGroup _group;
|
private PwGroup _group;
|
||||||
protected bool DontSave;
|
protected bool DontSave;
|
||||||
|
|
||||||
public DeleteGroup(Activity activity, IKp2aApp app, PwGroup group, OnOperationFinishedHandler operationFinishedHandler)
|
public DeleteGroup(Activity activity, IKp2aApp app, PwGroup group, OnFinish finish)
|
||||||
: base(operationFinishedHandler, app)
|
: base(activity, finish, app)
|
||||||
{
|
{
|
||||||
SetMembers(app, group, false);
|
SetMembers(activity, app, group, false);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
public DeleteGroup(Context ctx, Database db, PwGroup group, Activity act, OnOperationFinishedHandler operationFinishedHandler, bool dontSave)
|
public DeleteGroup(Context ctx, Database db, PwGroup group, Activity act, OnFinish finish, bool dontSave)
|
||||||
: base(operationFinishedHandler)
|
: base(finish)
|
||||||
{
|
{
|
||||||
SetMembers(ctx, db, group, act, dontSave);
|
SetMembers(ctx, db, group, act, dontSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeleteGroup(Context ctx, Database db, PwGroup group, OnOperationFinishedHandler operationFinishedHandler, bool dontSave):base(operationFinishedHandler) {
|
public DeleteGroup(Context ctx, Database db, PwGroup group, OnFinish finish, bool dontSave):base(finish) {
|
||||||
SetMembers(ctx, db, group, null, dontSave);
|
SetMembers(ctx, db, group, null, dontSave);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
private void SetMembers(IKp2aApp app, PwGroup group, bool dontSave)
|
private void SetMembers(Activity activity, IKp2aApp app, PwGroup group, bool dontSave)
|
||||||
{
|
{
|
||||||
base.SetMembers(app.FindDatabaseForElement(group));
|
base.SetMembers(activity, app.FindDatabaseForElement(group));
|
||||||
|
|
||||||
_group = group;
|
_group = group;
|
||||||
DontSave = dontSave;
|
DontSave = dontSave;
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ namespace keepass2android
|
|||||||
private readonly List<IStructureItem> _elementsToDelete;
|
private readonly List<IStructureItem> _elementsToDelete;
|
||||||
private readonly bool _canRecycle;
|
private readonly bool _canRecycle;
|
||||||
|
|
||||||
public DeleteMultipleItemsFromOneDatabase(Activity activity, Database db, List<IStructureItem> elementsToDelete, OnOperationFinishedHandler operationFinishedHandler, IKp2aApp app)
|
public DeleteMultipleItemsFromOneDatabase(Activity activity, Database db, List<IStructureItem> elementsToDelete, OnFinish finish, IKp2aApp app)
|
||||||
: base(operationFinishedHandler, app)
|
: base(activity, finish, app)
|
||||||
{
|
{
|
||||||
_elementsToDelete = elementsToDelete;
|
_elementsToDelete = elementsToDelete;
|
||||||
SetMembers(db);
|
SetMembers(activity, db);
|
||||||
|
|
||||||
//determine once. The property is queried for each delete operation, but might return false
|
//determine once. The property is queried for each delete operation, but might return false
|
||||||
//after one entry/group is deleted (and thus in recycle bin and thus can't be recycled anymore)
|
//after one entry/group is deleted (and thus in recycle bin and thus can't be recycled anymore)
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ using KeePassLib;
|
|||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
public abstract class DeleteRunnable : OperationWithFinishHandler
|
public abstract class DeleteRunnable : RunnableOnFinish
|
||||||
{
|
{
|
||||||
protected DeleteRunnable(OnOperationFinishedHandler operationFinishedHandler, IKp2aApp app)
|
protected DeleteRunnable(Activity activity, OnFinish finish, IKp2aApp app)
|
||||||
: base(app, operationFinishedHandler)
|
: base(activity, finish)
|
||||||
{
|
{
|
||||||
App = app;
|
App = app;
|
||||||
}
|
}
|
||||||
@@ -18,10 +18,11 @@ namespace keepass2android
|
|||||||
|
|
||||||
protected Database Db;
|
protected Database Db;
|
||||||
|
|
||||||
|
protected Activity Ctx;
|
||||||
|
|
||||||
protected void SetMembers( Database db)
|
protected void SetMembers(Activity activity, Database db)
|
||||||
{
|
{
|
||||||
|
Ctx = activity;
|
||||||
Db = db;
|
Db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,18 +131,18 @@ namespace keepass2android
|
|||||||
(dlgSender, dlgEvt) =>
|
(dlgSender, dlgEvt) =>
|
||||||
{
|
{
|
||||||
DeletePermanently = true;
|
DeletePermanently = true;
|
||||||
BlockingOperationStarter pt = new BlockingOperationStarter(App, this);
|
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||||
pt.Run();
|
pt.Run();
|
||||||
|
|
||||||
},
|
},
|
||||||
(dlgSender, dlgEvt) =>
|
(dlgSender, dlgEvt) =>
|
||||||
{
|
{
|
||||||
DeletePermanently = false;
|
DeletePermanently = false;
|
||||||
BlockingOperationStarter pt = new BlockingOperationStarter(App, this);
|
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||||
pt.Run();
|
pt.Run();
|
||||||
},
|
},
|
||||||
(dlgSender, dlgEvt) => { },
|
(dlgSender, dlgEvt) => { },
|
||||||
messageSuffix);
|
Ctx, messageSuffix);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -152,12 +153,12 @@ namespace keepass2android
|
|||||||
QuestionNoRecycleResourceId,
|
QuestionNoRecycleResourceId,
|
||||||
(dlgSender, dlgEvt) =>
|
(dlgSender, dlgEvt) =>
|
||||||
{
|
{
|
||||||
BlockingOperationStarter pt = new BlockingOperationStarter(App, this);
|
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||||
pt.Run();
|
pt.Run();
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
(dlgSender, dlgEvt) => { },
|
(dlgSender, dlgEvt) => { },
|
||||||
messageSuffix);
|
Ctx, messageSuffix);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -214,7 +215,7 @@ namespace keepass2android
|
|||||||
Android.Util.Log.Debug("KP2A", "Calling PerformDelete..");
|
Android.Util.Log.Debug("KP2A", "Calling PerformDelete..");
|
||||||
PerformDelete(touchedGroups, permanentlyDeletedGroups);
|
PerformDelete(touchedGroups, permanentlyDeletedGroups);
|
||||||
|
|
||||||
_operationFinishedHandler = new ActionOnOperationFinished(App,(success, message, context) =>
|
_onFinishToRun = new ActionOnFinish(ActiveActivity,(success, message, activity) =>
|
||||||
{
|
{
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
@@ -235,10 +236,10 @@ namespace keepass2android
|
|||||||
// Let's not bother recovering from a failure to save. It is too much work.
|
// Let's not bother recovering from a failure to save. It is too much work.
|
||||||
App.Lock(false, false);
|
App.Lock(false, false);
|
||||||
}
|
}
|
||||||
}, operationFinishedHandler);
|
}, OnFinishToRun);
|
||||||
|
|
||||||
// Commit database
|
// Commit database
|
||||||
SaveDb save = new SaveDb( App, Db, operationFinishedHandler, false, null);
|
SaveDb save = new SaveDb(Ctx, App, Db, OnFinishToRun, false);
|
||||||
save.ShowDatabaseIocInStatus = ShowDatabaseIocInStatus;
|
save.ShowDatabaseIocInStatus = ShowDatabaseIocInStatus;
|
||||||
|
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ using KeePassLib;
|
|||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
|
|
||||||
public class EditGroup : OperationWithFinishHandler {
|
public class EditGroup : RunnableOnFinish {
|
||||||
internal Database Db
|
internal Database Db
|
||||||
{
|
{
|
||||||
get { return _app.FindDatabaseForElement(Group); }
|
get { return _app.FindDatabaseForElement(Group); }
|
||||||
@@ -36,17 +36,19 @@ namespace keepass2android
|
|||||||
private readonly PwIcon _iconId;
|
private readonly PwIcon _iconId;
|
||||||
private readonly PwUuid _customIconId;
|
private readonly PwUuid _customIconId;
|
||||||
internal PwGroup Group;
|
internal PwGroup Group;
|
||||||
|
readonly Activity _ctx;
|
||||||
|
|
||||||
public EditGroup(IKp2aApp app, String name, PwIcon iconid, PwUuid customIconId, PwGroup group, OnOperationFinishedHandler operationFinishedHandler)
|
public EditGroup(Activity ctx, IKp2aApp app, String name, PwIcon iconid, PwUuid customIconId, PwGroup group, OnFinish finish)
|
||||||
: base(app, operationFinishedHandler)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
|
_ctx = ctx;
|
||||||
_name = name;
|
_name = name;
|
||||||
_iconId = iconid;
|
_iconId = iconid;
|
||||||
Group = group;
|
Group = group;
|
||||||
_customIconId = customIconId;
|
_customIconId = customIconId;
|
||||||
_app = app;
|
_app = app;
|
||||||
|
|
||||||
_operationFinishedHandler = new AfterEdit(app, this, operationFinishedHandler);
|
_onFinishToRun = new AfterEdit(ctx, this, OnFinishToRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -58,16 +60,16 @@ namespace keepass2android
|
|||||||
Group.Touch(true);
|
Group.Touch(true);
|
||||||
|
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDb save = new SaveDb(_app, Db, operationFinishedHandler);
|
SaveDb save = new SaveDb(_ctx, _app, Db, OnFinishToRun);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterEdit : OnOperationFinishedHandler {
|
private class AfterEdit : OnFinish {
|
||||||
readonly EditGroup _editGroup;
|
readonly EditGroup _editGroup;
|
||||||
|
|
||||||
public AfterEdit(IKp2aApp app, EditGroup editGroup, OnOperationFinishedHandler operationFinishedHandler)
|
public AfterEdit(Activity ctx, EditGroup editGroup, OnFinish finish)
|
||||||
: base(app, operationFinishedHandler)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
_editGroup = editGroup;
|
_editGroup = editGroup;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ using Android.App;
|
|||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
|
|
||||||
public abstract class FileOnFinish : OnOperationFinishedHandler {
|
public abstract class FileOnFinish : OnFinish {
|
||||||
private String _filename = "";
|
private String _filename = "";
|
||||||
|
|
||||||
protected FileOnFinish(IKp2aApp app, FileOnFinish operationFinishedHandler):base(app, operationFinishedHandler) {
|
protected FileOnFinish(Activity activity, FileOnFinish finish):base(activity, finish) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Filename
|
public string Filename
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace keepass2android;
|
|
||||||
|
|
||||||
// A context instance can be the instance of an Activity. Even if the activity is recreated (due to a configuration change, for example), the instance id must remain the same
|
|
||||||
// but it must be different for other activities/services or if the activity is finished and then starts again.
|
|
||||||
// We want to be able to perform actions on a context instance, even though that instance might not live at the time when we want to perform the action.
|
|
||||||
// In that case, we want to be able to register the action such that it is performed when the activity is recreated.
|
|
||||||
public interface IContextInstanceIdProvider
|
|
||||||
{
|
|
||||||
|
|
||||||
int ContextInstanceId { get; }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -21,88 +21,60 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.OS;
|
|
||||||
using KeePass.Util;
|
using KeePass.Util;
|
||||||
using keepass2android.database.edit;
|
using keepass2android.database.edit;
|
||||||
using keepass2android.Io;
|
|
||||||
using KeePassLib;
|
using KeePassLib;
|
||||||
using KeePassLib.Keys;
|
using KeePassLib.Keys;
|
||||||
using KeePassLib.Serialization;
|
using KeePassLib.Serialization;
|
||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
public class LoadDb : OperationWithFinishHandler {
|
public class LoadDb : RunnableOnFinish {
|
||||||
private readonly IOConnectionInfo _ioc;
|
private readonly IOConnectionInfo _ioc;
|
||||||
private readonly Task<MemoryStream> _databaseData;
|
private readonly Task<MemoryStream> _databaseData;
|
||||||
private readonly CompositeKey _compositeKey;
|
private readonly CompositeKey _compositeKey;
|
||||||
private readonly string? _keyfileOrProvider;
|
private readonly string _keyfileOrProvider;
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
private readonly bool _rememberKeyfile;
|
private readonly bool _rememberKeyfile;
|
||||||
IDatabaseFormat _format;
|
IDatabaseFormat _format;
|
||||||
|
|
||||||
public bool DoNotSetStatusLoggerMessage = false;
|
public LoadDb(Activity activity, IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish, bool updateLastUsageTimestamp, bool makeCurrent): base(activity, finish)
|
||||||
|
|
||||||
|
|
||||||
public LoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey,
|
|
||||||
string keyfileOrProvider, OnOperationFinishedHandler operationFinishedHandler,
|
|
||||||
bool updateLastUsageTimestamp, bool makeCurrent, IDatabaseModificationWatcher modificationWatcher = null): base(app, operationFinishedHandler)
|
|
||||||
{
|
{
|
||||||
_modificationWatcher = modificationWatcher ?? new NullDatabaseModificationWatcher();
|
_app = app;
|
||||||
_app = app;
|
|
||||||
_ioc = ioc;
|
_ioc = ioc;
|
||||||
_databaseData = databaseData;
|
_databaseData = databaseData;
|
||||||
_compositeKey = compositeKey;
|
_compositeKey = compositeKey;
|
||||||
_keyfileOrProvider = keyfileOrProvider;
|
_keyfileOrProvider = keyfileOrProvider;
|
||||||
_updateLastUsageTimestamp = updateLastUsageTimestamp;
|
_updateLastUsageTimestamp = updateLastUsageTimestamp;
|
||||||
_makeCurrent = makeCurrent;
|
_makeCurrent = makeCurrent;
|
||||||
_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile);
|
|
||||||
}
|
|
||||||
|
_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile);
|
||||||
|
}
|
||||||
|
|
||||||
protected bool success = false;
|
protected bool success = false;
|
||||||
private bool _updateLastUsageTimestamp;
|
private bool _updateLastUsageTimestamp;
|
||||||
private readonly bool _makeCurrent;
|
private readonly bool _makeCurrent;
|
||||||
private readonly IDatabaseModificationWatcher _modificationWatcher;
|
|
||||||
|
|
||||||
public override void Run()
|
public override void Run()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//make sure the file data is stored in the recent files list even if loading fails
|
//make sure the file data is stored in the recent files list even if loading fails
|
||||||
SaveFileData(_ioc, _keyfileOrProvider);
|
SaveFileData(_ioc, _keyfileOrProvider);
|
||||||
|
|
||||||
|
|
||||||
var fileStorage = _app.GetFileStorage(_ioc);
|
|
||||||
|
|
||||||
RequiresSubsequentSync = false;
|
|
||||||
|
|
||||||
|
|
||||||
if (!DoNotSetStatusLoggerMessage)
|
StatusLogger.UpdateMessage(UiStringKey.loading_database);
|
||||||
{
|
|
||||||
StatusLogger.UpdateMessage(UiStringKey.loading_database);
|
|
||||||
}
|
|
||||||
|
|
||||||
//get the stream data into a single stream variable (databaseStream) regardless whether its preloaded or not:
|
//get the stream data into a single stream variable (databaseStream) regardless whether its preloaded or not:
|
||||||
MemoryStream preloadedMemoryStream = _databaseData == null ? null : _databaseData.Result;
|
MemoryStream preloadedMemoryStream = _databaseData == null ? null : _databaseData.Result;
|
||||||
MemoryStream databaseStream;
|
MemoryStream databaseStream;
|
||||||
if (preloadedMemoryStream != null)
|
if (preloadedMemoryStream != null)
|
||||||
{
|
databaseStream = preloadedMemoryStream;
|
||||||
//note: if the stream has been loaded already, we don't need to trigger another sync later on
|
else
|
||||||
databaseStream = preloadedMemoryStream;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (_app.SyncInBackgroundPreference && fileStorage is CachingFileStorage cachingFileStorage &&
|
using (Stream s = _app.GetFileStorage(_ioc).OpenFileForRead(_ioc))
|
||||||
cachingFileStorage.IsCached(_ioc))
|
|
||||||
{
|
|
||||||
cachingFileStorage.IsOffline = true;
|
|
||||||
//no warning. We'll trigger a sync later.
|
|
||||||
cachingFileStorage.TriggerWarningWhenFallingBackToCache = false;
|
|
||||||
RequiresSubsequentSync = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
using (Stream s = fileStorage.OpenFileForRead(_ioc))
|
|
||||||
{
|
{
|
||||||
databaseStream = new MemoryStream();
|
databaseStream = new MemoryStream();
|
||||||
s.CopyTo(databaseStream);
|
s.CopyTo(databaseStream);
|
||||||
@@ -110,13 +82,8 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!StatusLogger.ContinueWork())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//ok, try to load the database. Let's start with Kdbx format and retry later if that is the wrong guess:
|
//ok, try to load the database. Let's start with Kdbx format and retry later if that is the wrong guess:
|
||||||
_format = new KdbxDatabaseFormat(KdbxDatabaseFormat.GetFormatToUse(fileStorage.GetFileExtension(_ioc)));
|
_format = new KdbxDatabaseFormat(KdbxDatabaseFormat.GetFormatToUse(_app.GetFileStorage(_ioc).GetFileExtension(_ioc)));
|
||||||
TryLoad(databaseStream);
|
TryLoad(databaseStream);
|
||||||
|
|
||||||
|
|
||||||
@@ -153,13 +120,7 @@ namespace keepass2android
|
|||||||
Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError) + " " + ExceptionUtil.GetErrorMessage(e) + _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional), false, Exception);
|
Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError) + " " + ExceptionUtil.GetErrorMessage(e) + _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional), false, Exception);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (Java.Lang.InterruptedException)
|
catch (Exception e)
|
||||||
{
|
|
||||||
Kp2aLog.Log("Load interrupted");
|
|
||||||
//close without Finish()
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
{
|
||||||
if (!(e is InvalidCompositeKeyException))
|
if (!(e is InvalidCompositeKeyException))
|
||||||
Kp2aLog.LogUnexpectedError(e);
|
Kp2aLog.LogUnexpectedError(e);
|
||||||
@@ -170,15 +131,14 @@ namespace keepass2android
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RequiresSubsequentSync { get; set; } = false;
|
/// <summary>
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Holds the exception which was thrown during execution (if any)
|
/// Holds the exception which was thrown during execution (if any)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Exception Exception { get; set; }
|
public Exception Exception { get; set; }
|
||||||
|
|
||||||
Database TryLoad(MemoryStream databaseStream)
|
Database TryLoad(MemoryStream databaseStream)
|
||||||
{
|
{
|
||||||
|
Kp2aLog.Log("LoadDb: Copying database in memory");
|
||||||
//create a copy of the stream so we can try again if we get an exception which indicates we should change parameters
|
//create a copy of the stream so we can try again if we get an exception which indicates we should change parameters
|
||||||
//This is not optimal in terms of (short-time) memory usage but is hard to avoid because the Keepass library closes streams also in case of errors.
|
//This is not optimal in terms of (short-time) memory usage but is hard to avoid because the Keepass library closes streams also in case of errors.
|
||||||
//Alternatives would involve increased traffic (if file is on remote) and slower loading times, so this seems to be the best choice.
|
//Alternatives would involve increased traffic (if file is on remote) and slower loading times, so this seems to be the best choice.
|
||||||
@@ -187,21 +147,16 @@ namespace keepass2android
|
|||||||
workingCopy.Seek(0, SeekOrigin.Begin);
|
workingCopy.Seek(0, SeekOrigin.Begin);
|
||||||
//reset stream if we need to reuse it later:
|
//reset stream if we need to reuse it later:
|
||||||
databaseStream.Seek(0, SeekOrigin.Begin);
|
databaseStream.Seek(0, SeekOrigin.Begin);
|
||||||
if (!StatusLogger.ContinueWork())
|
Kp2aLog.Log("LoadDb: Ready to start loading");
|
||||||
{
|
//now let's go:
|
||||||
throw new Java.Lang.InterruptedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
//now let's go:
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Database newDb =
|
Database newDb = _app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format, _makeCurrent);
|
||||||
_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format, _makeCurrent, _modificationWatcher);
|
Kp2aLog.Log("LoadDB OK");
|
||||||
Kp2aLog.Log("LoadDB OK");
|
|
||||||
|
|
||||||
Finish(true, _format.SuccessMessage);
|
Finish(true, _format.SuccessMessage);
|
||||||
return newDb;
|
return newDb;
|
||||||
}
|
}
|
||||||
catch (OldFormatException)
|
catch (OldFormatException)
|
||||||
{
|
{
|
||||||
_format = new KdbDatabaseFormat(_app);
|
_format = new KdbDatabaseFormat(_app);
|
||||||
|
|||||||
@@ -10,16 +10,18 @@ using KeePassLib.Interfaces;
|
|||||||
|
|
||||||
namespace keepass2android.database.edit
|
namespace keepass2android.database.edit
|
||||||
{
|
{
|
||||||
public class MoveElements: OperationWithFinishHandler
|
public class MoveElements: RunnableOnFinish
|
||||||
{
|
{
|
||||||
private readonly List<IStructureItem> _elementsToMove;
|
private readonly List<IStructureItem> _elementsToMove;
|
||||||
private readonly PwGroup _targetGroup;
|
private readonly PwGroup _targetGroup;
|
||||||
private readonly IKp2aApp _app;
|
private readonly Activity _ctx;
|
||||||
|
private readonly IKp2aApp _app;
|
||||||
|
|
||||||
public MoveElements(List<IStructureItem> elementsToMove, PwGroup targetGroup,IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler) : base(app, operationFinishedHandler)
|
public MoveElements(List<IStructureItem> elementsToMove, PwGroup targetGroup, Activity ctx, IKp2aApp app, OnFinish finish) : base(ctx, finish)
|
||||||
{
|
{
|
||||||
_elementsToMove = elementsToMove;
|
_elementsToMove = elementsToMove;
|
||||||
_targetGroup = targetGroup;
|
_targetGroup = targetGroup;
|
||||||
|
_ctx = ctx;
|
||||||
_app = app;
|
_app = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,24 +123,24 @@ namespace keepass2android.database.edit
|
|||||||
|
|
||||||
int indexToSave = 0;
|
int indexToSave = 0;
|
||||||
bool allSavesSuccess = true;
|
bool allSavesSuccess = true;
|
||||||
void ContinueSave(bool success, string message, Context activeActivity)
|
void ContinueSave(bool success, string message, Activity activeActivity)
|
||||||
{
|
{
|
||||||
allSavesSuccess &= success;
|
allSavesSuccess &= success;
|
||||||
indexToSave++;
|
indexToSave++;
|
||||||
if (indexToSave == allDatabasesToSave.Count)
|
if (indexToSave == allDatabasesToSave.Count)
|
||||||
{
|
{
|
||||||
operationFinishedHandler.SetResult(allSavesSuccess);
|
OnFinishToRun.SetResult(allSavesSuccess);
|
||||||
operationFinishedHandler.Run();
|
OnFinishToRun.Run();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SaveDb saveDb = new SaveDb( _app, allDatabasesToSave[indexToSave], new ActionOnOperationFinished(_app, ContinueSave), false, null);
|
SaveDb saveDb = new SaveDb(_ctx, _app, allDatabasesToSave[indexToSave], new ActionOnFinish(activeActivity, ContinueSave), false);
|
||||||
saveDb.SetStatusLogger(StatusLogger);
|
saveDb.SetStatusLogger(StatusLogger);
|
||||||
saveDb.ShowDatabaseIocInStatus = allDatabasesToSave.Count > 1;
|
saveDb.ShowDatabaseIocInStatus = allDatabasesToSave.Count > 1;
|
||||||
saveDb.Run();
|
saveDb.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SaveDb save = new SaveDb(_app, allDatabasesToSave[0], new ActionOnOperationFinished(_app, ContinueSave), false, null);
|
SaveDb save = new SaveDb(_ctx, _app, allDatabasesToSave[0], new ActionOnFinish(ActiveActivity, ContinueSave), false);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.ShowDatabaseIocInStatus = allDatabasesToSave.Count > 1;
|
save.ShowDatabaseIocInStatus = allDatabasesToSave.Count > 1;
|
||||||
save.Run();
|
save.Run();
|
||||||
|
|||||||
@@ -22,16 +22,10 @@ using Android.Content;
|
|||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
using Google.Android.Material.Dialog;
|
using Google.Android.Material.Dialog;
|
||||||
using KeePassLib.Interfaces;
|
|
||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
public interface IActiveContextProvider
|
public abstract class OnFinish
|
||||||
{
|
|
||||||
Context ActiveContext { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class OnOperationFinishedHandler
|
|
||||||
{
|
{
|
||||||
protected bool Success;
|
protected bool Success;
|
||||||
protected String Message;
|
protected String Message;
|
||||||
@@ -43,41 +37,63 @@ namespace keepass2android
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Context ActiveContext
|
protected OnFinish BaseOnFinish;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _activeContextProvider?.ActiveContext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected OnOperationFinishedHandler NextOnOperationFinishedHandler;
|
|
||||||
protected Handler Handler;
|
protected Handler Handler;
|
||||||
private IKp2aStatusLogger _statusLogger = new Kp2aNullStatusLogger(); //default: no logging but not null -> can be used whenever desired
|
private ProgressDialogStatusLogger _statusLogger = new ProgressDialogStatusLogger(); //default: no logging but not null -> can be used whenever desired
|
||||||
private readonly IActiveContextProvider _activeContextProvider;
|
private Activity _activeActivity, _previouslyActiveActivity;
|
||||||
|
|
||||||
public IKp2aStatusLogger StatusLogger
|
|
||||||
|
public ProgressDialogStatusLogger StatusLogger
|
||||||
{
|
{
|
||||||
get { return _statusLogger; }
|
get { return _statusLogger; }
|
||||||
set { _statusLogger = value; }
|
set { _statusLogger = value; }
|
||||||
} protected OnOperationFinishedHandler(IActiveContextProvider activeContextProvider, Handler handler)
|
|
||||||
{
|
|
||||||
_activeContextProvider = activeContextProvider;
|
|
||||||
NextOnOperationFinishedHandler = null;
|
|
||||||
Handler = handler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OnOperationFinishedHandler(IActiveContextProvider activeContextProvider, OnOperationFinishedHandler operationFinishedHandler, Handler handler)
|
public Activity ActiveActivity
|
||||||
|
{
|
||||||
|
get { return _activeActivity; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_activeActivity != null && _activeActivity != _previouslyActiveActivity)
|
||||||
|
{
|
||||||
|
_previouslyActiveActivity = _activeActivity;
|
||||||
|
|
||||||
|
}
|
||||||
|
_activeActivity = value;
|
||||||
|
if (BaseOnFinish != null)
|
||||||
|
{
|
||||||
|
BaseOnFinish.ActiveActivity = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Activity PreviouslyActiveActivity
|
||||||
|
{
|
||||||
|
get { return _previouslyActiveActivity; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected OnFinish(Activity activeActivity, Handler handler)
|
||||||
|
{
|
||||||
|
ActiveActivity = activeActivity;
|
||||||
|
BaseOnFinish = null;
|
||||||
|
Handler = handler;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OnFinish(Activity activeActivity, OnFinish finish, Handler handler)
|
||||||
{
|
{
|
||||||
_activeContextProvider = activeContextProvider;
|
ActiveActivity = activeActivity;
|
||||||
NextOnOperationFinishedHandler = operationFinishedHandler;
|
BaseOnFinish = finish;
|
||||||
Handler = handler;
|
Handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OnOperationFinishedHandler(IActiveContextProvider activeContextProvider, OnOperationFinishedHandler operationFinishedHandler)
|
protected OnFinish(Activity activeActivity, OnFinish finish)
|
||||||
{
|
{
|
||||||
_activeContextProvider = activeContextProvider;
|
ActiveActivity = activeActivity;
|
||||||
NextOnOperationFinishedHandler = operationFinishedHandler;
|
BaseOnFinish = finish;
|
||||||
Handler = null;
|
Handler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,19 +110,14 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Run() {
|
public virtual void Run() {
|
||||||
if (NextOnOperationFinishedHandler == null) return;
|
if (BaseOnFinish == null) return;
|
||||||
// Pass on result on call finish
|
// Pass on result on call finish
|
||||||
NextOnOperationFinishedHandler.SetResult(Success, Message, ImportantMessage, Exception);
|
BaseOnFinish.SetResult(Success, Message, ImportantMessage, Exception);
|
||||||
|
|
||||||
var handler = Handler ?? NextOnOperationFinishedHandler.Handler ?? null;
|
if ( Handler != null ) {
|
||||||
|
Handler.Post(BaseOnFinish.Run);
|
||||||
if (handler != null ) {
|
|
||||||
handler.Post(() =>
|
|
||||||
{
|
|
||||||
NextOnOperationFinishedHandler.Run();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
NextOnOperationFinishedHandler.Run();
|
BaseOnFinish.Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +128,7 @@ namespace keepass2android
|
|||||||
public static void DisplayMessage(Context ctx, string message, bool makeDialog)
|
public static void DisplayMessage(Context ctx, string message, bool makeDialog)
|
||||||
{
|
{
|
||||||
if ( !String.IsNullOrEmpty(message) ) {
|
if ( !String.IsNullOrEmpty(message) ) {
|
||||||
Kp2aLog.Log("OnOperationFinishedHandler message: " + message);
|
Kp2aLog.Log("OnFinish message: " + message);
|
||||||
if (makeDialog && ctx != null)
|
if (makeDialog && ctx != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -141,4 +152,3 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
|
|
||||||
|
|
||||||
Keepass2Android is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Keepass2Android is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
using System;
|
|
||||||
using Android.App;
|
|
||||||
using Android.Content;
|
|
||||||
using KeePassLib.Interfaces;
|
|
||||||
|
|
||||||
namespace keepass2android
|
|
||||||
{
|
|
||||||
|
|
||||||
public abstract class OperationWithFinishHandler {
|
|
||||||
|
|
||||||
protected OnOperationFinishedHandler _operationFinishedHandler;
|
|
||||||
public IKp2aStatusLogger StatusLogger = new Kp2aNullStatusLogger(); //default: empty but not null
|
|
||||||
private IActiveContextProvider _activeContextProvider;
|
|
||||||
|
|
||||||
protected OperationWithFinishHandler(IActiveContextProvider activeContextProvider, OnOperationFinishedHandler operationFinishedHandler)
|
|
||||||
{
|
|
||||||
_activeContextProvider = activeContextProvider;
|
|
||||||
_operationFinishedHandler = operationFinishedHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OnOperationFinishedHandler operationFinishedHandler
|
|
||||||
{
|
|
||||||
get { return _operationFinishedHandler; }
|
|
||||||
set { _operationFinishedHandler = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected void Finish(bool result, String message, bool importantMessage = false, Exception exception = null) {
|
|
||||||
if ( operationFinishedHandler != null ) {
|
|
||||||
operationFinishedHandler.SetResult(result, message, importantMessage, exception);
|
|
||||||
operationFinishedHandler.Run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void Finish(bool result) {
|
|
||||||
if ( operationFinishedHandler != null ) {
|
|
||||||
operationFinishedHandler.SetResult(result);
|
|
||||||
operationFinishedHandler.Run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetStatusLogger(IKp2aStatusLogger statusLogger) {
|
|
||||||
if (operationFinishedHandler != null)
|
|
||||||
{
|
|
||||||
operationFinishedHandler.StatusLogger = statusLogger;
|
|
||||||
}
|
|
||||||
StatusLogger = statusLogger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void Run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
78
src/Kp2aBusinessLogic/database/edit/RunnableOnFinish.cs
Normal file
78
src/Kp2aBusinessLogic/database/edit/RunnableOnFinish.cs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.
|
||||||
|
|
||||||
|
Keepass2Android is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Keepass2Android is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
|
||||||
|
namespace keepass2android
|
||||||
|
{
|
||||||
|
|
||||||
|
public abstract class RunnableOnFinish {
|
||||||
|
|
||||||
|
protected OnFinish _onFinishToRun;
|
||||||
|
public ProgressDialogStatusLogger StatusLogger = new ProgressDialogStatusLogger(); //default: empty but not null
|
||||||
|
private Activity _activeActivity;
|
||||||
|
|
||||||
|
protected RunnableOnFinish(Activity activeActivity, OnFinish finish)
|
||||||
|
{
|
||||||
|
_activeActivity = activeActivity;
|
||||||
|
_onFinishToRun = finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OnFinish OnFinishToRun
|
||||||
|
{
|
||||||
|
get { return _onFinishToRun; }
|
||||||
|
set { _onFinishToRun = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Activity ActiveActivity
|
||||||
|
{
|
||||||
|
get { return _activeActivity; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_activeActivity = value;
|
||||||
|
if (_onFinishToRun != null)
|
||||||
|
_onFinishToRun.ActiveActivity = _activeActivity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Finish(bool result, String message, bool importantMessage = false, Exception exception = null) {
|
||||||
|
if ( OnFinishToRun != null ) {
|
||||||
|
OnFinishToRun.SetResult(result, message, importantMessage, exception);
|
||||||
|
OnFinishToRun.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Finish(bool result) {
|
||||||
|
if ( OnFinishToRun != null ) {
|
||||||
|
OnFinishToRun.SetResult(result);
|
||||||
|
OnFinishToRun.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetStatusLogger(ProgressDialogStatusLogger status) {
|
||||||
|
if (OnFinishToRun != null)
|
||||||
|
{
|
||||||
|
OnFinishToRun.StatusLogger = status;
|
||||||
|
}
|
||||||
|
StatusLogger = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -30,69 +30,57 @@ using keepass2android.Io;
|
|||||||
using Debug = System.Diagnostics.Debug;
|
using Debug = System.Diagnostics.Debug;
|
||||||
using Exception = System.Exception;
|
using Exception = System.Exception;
|
||||||
using KeePass.Util;
|
using KeePass.Util;
|
||||||
using Thread = System.Threading.Thread;
|
|
||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Save the database. If the file has changed, ask the user if he wants to overwrite or sync.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public class SaveDb : OperationWithFinishHandler {
|
public class SaveDb : RunnableOnFinish {
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
private readonly Database _db;
|
private readonly Database _db;
|
||||||
private readonly bool _dontSave;
|
private readonly bool _dontSave;
|
||||||
private readonly IDatabaseModificationWatcher _modificationWatcher;
|
|
||||||
private bool requiresSubsequentSync = false; //if true, we need to sync the file after saving.
|
|
||||||
|
|
||||||
public bool DoNotSetStatusLoggerMessage = false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// stream for reading the data from the original file. If this is set to a non-null value, we know we need to sync
|
/// stream for reading the data from the original file. If this is set to a non-null value, we know we need to sync
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Stream _streamForOrigFile;
|
private readonly Stream _streamForOrigFile;
|
||||||
|
private readonly Context _ctx;
|
||||||
private Java.Lang.Thread _workerThread;
|
private Java.Lang.Thread _workerThread;
|
||||||
|
|
||||||
public SaveDb(IKp2aApp app, Database db, OnOperationFinishedHandler operationFinishedHandler, bool dontSave, IDatabaseModificationWatcher modificationWatcher)
|
public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish, bool dontSave)
|
||||||
: base(app, operationFinishedHandler)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
|
_ctx = ctx;
|
||||||
_app = app;
|
_app = app;
|
||||||
_dontSave = dontSave;
|
_dontSave = dontSave;
|
||||||
_modificationWatcher = modificationWatcher ?? new NullDatabaseModificationWatcher();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor for sync
|
/// Constructor for sync
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="ctx"></param>
|
||||||
/// <param name="app"></param>
|
/// <param name="app"></param>
|
||||||
/// <param name="operationFinishedHandler"></param>
|
/// <param name="finish"></param>
|
||||||
/// <param name="dontSave"></param>
|
/// <param name="dontSave"></param>
|
||||||
/// <param name="streamForOrigFile">Stream for reading the data from the (changed) original location</param>
|
/// <param name="streamForOrigFile">Stream for reading the data from the (changed) original location</param>
|
||||||
public SaveDb(IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler, Database db, bool dontSave, Stream streamForOrigFile, IDatabaseModificationWatcher modificationWatcher = null)
|
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, Database db, bool dontSave, Stream streamForOrigFile)
|
||||||
: base(app, operationFinishedHandler)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
_modificationWatcher = modificationWatcher ?? new NullDatabaseModificationWatcher();
|
_db = db;
|
||||||
_db = db;
|
_ctx = ctx;
|
||||||
_app = app;
|
_app = app;
|
||||||
_dontSave = dontSave;
|
_dontSave = dontSave;
|
||||||
_streamForOrigFile = streamForOrigFile;
|
_streamForOrigFile = streamForOrigFile;
|
||||||
SyncInBackground = _app.SyncInBackgroundPreference;
|
}
|
||||||
|
|
||||||
}
|
public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish)
|
||||||
|
: base(ctx, finish)
|
||||||
public SaveDb(IKp2aApp app, Database db, OnOperationFinishedHandler operationFinishedHandler, IDatabaseModificationWatcher modificationWatcher = null)
|
|
||||||
: base(app, operationFinishedHandler)
|
|
||||||
{
|
{
|
||||||
|
_ctx = ctx;
|
||||||
_modificationWatcher = modificationWatcher ?? new NullDatabaseModificationWatcher();
|
_app = app;
|
||||||
_app = app;
|
|
||||||
_db = db;
|
_db = db;
|
||||||
_dontSave = false;
|
_dontSave = false;
|
||||||
SyncInBackground = _app.SyncInBackgroundPreference;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShowDatabaseIocInStatus { get; set; }
|
public bool ShowDatabaseIocInStatus { get; set; }
|
||||||
|
|
||||||
@@ -115,42 +103,29 @@ namespace keepass2android
|
|||||||
if (ShowDatabaseIocInStatus)
|
if (ShowDatabaseIocInStatus)
|
||||||
message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
|
message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
|
||||||
|
|
||||||
if (!DoNotSetStatusLoggerMessage)
|
StatusLogger.UpdateMessage(message);
|
||||||
{
|
|
||||||
StatusLogger.UpdateMessage(message);
|
IOConnectionInfo ioc = _db.Ioc;
|
||||||
}
|
|
||||||
|
|
||||||
IOConnectionInfo ioc = _db.Ioc;
|
|
||||||
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
||||||
|
|
||||||
if (SyncInBackground && fileStorage is IOfflineSwitchable offlineSwitchable)
|
if (_streamForOrigFile == null)
|
||||||
{
|
|
||||||
offlineSwitchable.IsOffline = true;
|
|
||||||
//no warning. We'll trigger a sync later.
|
|
||||||
offlineSwitchable.TriggerWarningWhenFallingBackToCache = false;
|
|
||||||
requiresSubsequentSync = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (_streamForOrigFile == null)
|
|
||||||
{
|
{
|
||||||
if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave))
|
if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave))
|
||||||
|| (_db.KpDatabase.HashOfFileOnDisk == null)) //first time saving
|
|| (_db.KpDatabase.HashOfFileOnDisk == null)) //first time saving
|
||||||
{
|
{
|
||||||
PerformSaveWithoutCheck(fileStorage, ioc);
|
PerformSaveWithoutCheck(fileStorage, ioc);
|
||||||
FinishWithSuccess();
|
Finish(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool hasStreamForOrigFile = (_streamForOrigFile != null);
|
bool hasStreamForOrigFile = (_streamForOrigFile != null);
|
||||||
bool hasChangeFast = hasStreamForOrigFile ||
|
bool hasChangeFast = hasStreamForOrigFile ||
|
||||||
fileStorage.CheckForFileChangeFast(ioc, _db.LastFileVersion); //first try to use the fast change detection;
|
fileStorage.CheckForFileChangeFast(ioc, _db.LastFileVersion); //first try to use the fast change detection;
|
||||||
bool hasHashChanged = !requiresSubsequentSync && (
|
bool hasHashChanged = hasChangeFast ||
|
||||||
hasChangeFast ||
|
|
||||||
(FileHashChanged(ioc, _db.KpDatabase.HashOfFileOnDisk) ==
|
(FileHashChanged(ioc, _db.KpDatabase.HashOfFileOnDisk) ==
|
||||||
FileHashChange.Changed)); //if that fails, hash the file and compare:
|
FileHashChange.Changed); //if that fails, hash the file and compare:
|
||||||
|
|
||||||
if (hasHashChanged)
|
if (hasHashChanged)
|
||||||
{
|
{
|
||||||
@@ -183,14 +158,15 @@ namespace keepass2android
|
|||||||
RunInWorkerThread(() =>
|
RunInWorkerThread(() =>
|
||||||
{
|
{
|
||||||
PerformSaveWithoutCheck(fileStorage, ioc);
|
PerformSaveWithoutCheck(fileStorage, ioc);
|
||||||
FinishWithSuccess();
|
Finish(true);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//cancel
|
//cancel
|
||||||
(sender, args) =>
|
(sender, args) =>
|
||||||
{
|
{
|
||||||
RunInWorkerThread(() => Finish(false));
|
RunInWorkerThread(() => Finish(false));
|
||||||
}
|
},
|
||||||
|
_ctx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +174,7 @@ namespace keepass2android
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
PerformSaveWithoutCheck(fileStorage, ioc);
|
PerformSaveWithoutCheck(fileStorage, ioc);
|
||||||
FinishWithSuccess();
|
Finish(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -218,67 +194,22 @@ namespace keepass2android
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FinishWithSuccess();
|
Finish(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SyncInBackground { get; set; }
|
|
||||||
|
|
||||||
private void FinishWithSuccess()
|
|
||||||
{
|
|
||||||
if (requiresSubsequentSync)
|
|
||||||
{
|
|
||||||
var syncTask = new SynchronizeCachedDatabase(_app, _db, new ActionOnOperationFinished(_app,
|
|
||||||
(success, message, context) =>
|
|
||||||
{
|
|
||||||
if (!System.String.IsNullOrEmpty(message))
|
|
||||||
_app.ShowMessage(context, message, success ? MessageSeverity.Info : MessageSeverity.Error);
|
|
||||||
|
|
||||||
}), new BackgroundDatabaseModificationLocker(_app)
|
|
||||||
);
|
|
||||||
OperationRunner.Instance.Run(_app, syncTask);
|
|
||||||
}
|
|
||||||
Finish(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MergeAndFinish(IFileStorage fileStorage, IOConnectionInfo ioc)
|
private void MergeAndFinish(IFileStorage fileStorage, IOConnectionInfo ioc)
|
||||||
{
|
{
|
||||||
//note: when synced, the file might be downloaded once again from the server. Caching the data
|
//note: when synced, the file might be downloaded once again from the server. Caching the data
|
||||||
//in the hashing function would solve this but increases complexity. I currently assume the files are
|
//in the hashing function would solve this but increases complexity. I currently assume the files are
|
||||||
//small.
|
//small.
|
||||||
|
MergeIn(fileStorage, ioc);
|
||||||
try
|
|
||||||
{
|
|
||||||
_modificationWatcher.BeforeModifyDatabases();
|
|
||||||
}
|
|
||||||
catch (Java.Lang.InterruptedException)
|
|
||||||
{
|
|
||||||
// leave without Finish()
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MergeIn(fileStorage, ioc);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_modificationWatcher.AfterModifyDatabases();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
PerformSaveWithoutCheck(fileStorage, ioc);
|
PerformSaveWithoutCheck(fileStorage, ioc);
|
||||||
new Handler(Looper.MainLooper).Post(() =>
|
_db.UpdateGlobals();
|
||||||
{
|
Finish(true);
|
||||||
_db.UpdateGlobals();
|
|
||||||
});
|
|
||||||
|
|
||||||
FinishWithSuccess();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void RunInWorkerThread(Action runHandler)
|
private void RunInWorkerThread(Action runHandler)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -351,7 +282,7 @@ namespace keepass2android
|
|||||||
private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc)
|
private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc)
|
||||||
{
|
{
|
||||||
StatusLogger.UpdateSubMessage("");
|
StatusLogger.UpdateSubMessage("");
|
||||||
_db.SaveData(fileStorage);
|
_db.SaveData();
|
||||||
_db.LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
|
_db.LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,24 +22,26 @@ using KeePassLib.Keys;
|
|||||||
|
|
||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
public class SetPassword : OperationWithFinishHandler {
|
public class SetPassword : RunnableOnFinish {
|
||||||
|
|
||||||
private readonly String _password;
|
private readonly String _password;
|
||||||
private readonly String _keyfile;
|
private readonly String _keyfile;
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
private readonly bool _dontSave;
|
private readonly bool _dontSave;
|
||||||
|
private readonly Activity _ctx;
|
||||||
|
|
||||||
public SetPassword(IKp2aApp app, String password, String keyfile, OnOperationFinishedHandler operationFinishedHandler): base(app, operationFinishedHandler) {
|
public SetPassword(Activity ctx, IKp2aApp app, String password, String keyfile, OnFinish finish): base(ctx, finish) {
|
||||||
|
_ctx = ctx;
|
||||||
_app = app;
|
_app = app;
|
||||||
_password = password;
|
_password = password;
|
||||||
_keyfile = keyfile;
|
_keyfile = keyfile;
|
||||||
_dontSave = false;
|
_dontSave = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SetPassword(IKp2aApp app, String password, String keyfile, OnOperationFinishedHandler operationFinishedHandler, bool dontSave)
|
public SetPassword(Activity ctx, IKp2aApp app, String password, String keyfile, OnFinish finish, bool dontSave)
|
||||||
: base(app, operationFinishedHandler)
|
: base(ctx, finish)
|
||||||
{
|
{
|
||||||
|
_ctx = ctx;
|
||||||
_app = app;
|
_app = app;
|
||||||
_password = password;
|
_password = password;
|
||||||
_keyfile = keyfile;
|
_keyfile = keyfile;
|
||||||
@@ -71,18 +73,18 @@ namespace keepass2android
|
|||||||
pm.MasterKey = newKey;
|
pm.MasterKey = newKey;
|
||||||
|
|
||||||
// Save Database
|
// Save Database
|
||||||
_operationFinishedHandler = new AfterSave(_app, previousKey, previousMasterKeyChanged, pm, operationFinishedHandler);
|
_onFinishToRun = new AfterSave(ActiveActivity, previousKey, previousMasterKeyChanged, pm, OnFinishToRun);
|
||||||
SaveDb save = new SaveDb(_app, _app.CurrentDb, operationFinishedHandler, _dontSave, null);
|
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, _dontSave);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterSave : OnOperationFinishedHandler {
|
private class AfterSave : OnFinish {
|
||||||
private readonly CompositeKey _backup;
|
private readonly CompositeKey _backup;
|
||||||
private readonly DateTime _previousKeyChanged;
|
private readonly DateTime _previousKeyChanged;
|
||||||
private readonly PwDatabase _db;
|
private readonly PwDatabase _db;
|
||||||
|
|
||||||
public AfterSave(IActiveContextProvider activeContextProvider, CompositeKey backup, DateTime previousKeyChanged, PwDatabase db, OnOperationFinishedHandler operationFinishedHandler): base(activeContextProvider, operationFinishedHandler) {
|
public AfterSave(Activity activity, CompositeKey backup, DateTime previousKeyChanged, PwDatabase db, OnFinish finish): base(activity, finish) {
|
||||||
_previousKeyChanged = previousKeyChanged;
|
_previousKeyChanged = previousKeyChanged;
|
||||||
_backup = backup;
|
_backup = backup;
|
||||||
_db = db;
|
_db = db;
|
||||||
|
|||||||
@@ -22,29 +22,31 @@ using KeePassLib;
|
|||||||
namespace keepass2android
|
namespace keepass2android
|
||||||
{
|
{
|
||||||
|
|
||||||
public class UpdateEntry : OperationWithFinishHandler {
|
public class UpdateEntry : RunnableOnFinish {
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
|
private readonly Activity _ctx;
|
||||||
|
|
||||||
|
public UpdateEntry(Activity ctx, IKp2aApp app, PwEntry oldE, PwEntry newE, OnFinish finish):base(ctx, finish) {
|
||||||
|
_ctx = ctx;
|
||||||
|
_app = app;
|
||||||
|
|
||||||
public UpdateEntry(IKp2aApp app, PwEntry oldE, PwEntry newE, OnOperationFinishedHandler operationFinishedHandler):base(app, operationFinishedHandler) {
|
_onFinishToRun = new AfterUpdate(ctx, oldE, newE, app, finish);
|
||||||
_app = app;
|
|
||||||
|
|
||||||
_operationFinishedHandler = new AfterUpdate( oldE, newE, app, operationFinishedHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void Run() {
|
public override void Run() {
|
||||||
// Commit to disk
|
// Commit to disk
|
||||||
SaveDb save = new SaveDb(_app, _app.CurrentDb, operationFinishedHandler);
|
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||||
save.SetStatusLogger(StatusLogger);
|
save.SetStatusLogger(StatusLogger);
|
||||||
save.Run();
|
save.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AfterUpdate : OnOperationFinishedHandler {
|
private class AfterUpdate : OnFinish {
|
||||||
private readonly PwEntry _backup;
|
private readonly PwEntry _backup;
|
||||||
private readonly PwEntry _updatedEntry;
|
private readonly PwEntry _updatedEntry;
|
||||||
private readonly IKp2aApp _app;
|
private readonly IKp2aApp _app;
|
||||||
|
|
||||||
public AfterUpdate(PwEntry backup, PwEntry updatedEntry, IKp2aApp app, OnOperationFinishedHandler operationFinishedHandler):base(app, operationFinishedHandler) {
|
public AfterUpdate(Activity activity, PwEntry backup, PwEntry updatedEntry, IKp2aApp app, OnFinish finish):base(activity, finish) {
|
||||||
_backup = backup;
|
_backup = backup;
|
||||||
_updatedEntry = updatedEntry;
|
_updatedEntry = updatedEntry;
|
||||||
_app = app;
|
_app = app;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-android</TargetFramework>
|
<TargetFramework>net9.0-android</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ git clone --recurse-submodules https://github.com/PhilippC/keepass2android.git
|
|||||||
cd keepass2android/src/build-scripts
|
cd keepass2android/src/build-scripts
|
||||||
./build-java.sh && ./build-native.sh
|
./build-java.sh && ./build-native.sh
|
||||||
cd ..
|
cd ..
|
||||||
cp Kp2aBusinessLogic/Io/DropboxFileStorageKeysDummy.cs Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs
|
|
||||||
cd keepass2android-app
|
cd keepass2android-app
|
||||||
ln -s Manifests/AndroidManifest_debug.xml AndroidManifest.xml
|
ln -s Manifests/AndroidManifest_debug.xml AndroidManifest.xml
|
||||||
dotnet workload restore
|
dotnet workload restore
|
||||||
|
|||||||
22
src/build-scripts/rename-output-apks.sh
Normal file
22
src/build-scripts/rename-output-apks.sh
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BASE_DIR="${1}"
|
||||||
|
|
||||||
|
for arch_dir in "$BASE_DIR"/android-*/; do
|
||||||
|
arch=$(basename "$arch_dir")
|
||||||
|
arch=${arch#android-}
|
||||||
|
APK_DIR="${arch_dir}publish"
|
||||||
|
if [[ -d "$APK_DIR" ]]; then
|
||||||
|
apk_path=$(find "$APK_DIR" -maxdepth 1 -type f -name "*.apk" | head -n1)
|
||||||
|
if [[ -n "$apk_path" ]]; then
|
||||||
|
base=$(basename "$apk_path" .apk)
|
||||||
|
new_path="$APK_DIR/${base}-${arch}.apk"
|
||||||
|
mv "$apk_path" "$new_path"
|
||||||
|
echo "Renamed $apk_path to $new_path"
|
||||||
|
else
|
||||||
|
echo "No APK found in $APK_DIR"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Directory $APK_DIR does not exist"
|
||||||
|
fi
|
||||||
|
done
|
||||||
@@ -6,8 +6,8 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 33
|
targetSdkVersion 35
|
||||||
compileSdk 34
|
compileSdk 35
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@@ -51,4 +51,6 @@ dependencies {
|
|||||||
implementation 'com.pcloud.sdk:android:1.9.1'
|
implementation 'com.pcloud.sdk:android:1.9.1'
|
||||||
implementation 'com.google.code.gson:gson:2.8.6'
|
implementation 'com.google.code.gson:gson:2.8.6'
|
||||||
|
|
||||||
|
implementation 'com.github.mwiede:jsch:2.27.2'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
public interface AgentConnector {
|
|
||||||
String getName();
|
|
||||||
boolean isAvailable();
|
|
||||||
void query(Buffer buffer) throws AgentProxyException;
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
class AgentIdentity implements Identity {
|
|
||||||
|
|
||||||
private AgentProxy agent;
|
|
||||||
private byte[] blob;
|
|
||||||
private String comment;
|
|
||||||
private String algname;
|
|
||||||
AgentIdentity(AgentProxy agent, byte[] blob, String comment) {
|
|
||||||
this.agent = agent;
|
|
||||||
this.blob = blob;
|
|
||||||
this.comment = comment;
|
|
||||||
algname = Util.byte2str((new Buffer(blob)).getString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setPassphrase(byte[] passphrase) throws JSchException{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getPublicKeyBlob() { return blob; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getSignature(byte[] data){
|
|
||||||
return agent.sign(blob, data, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getSignature(byte[] data, String alg){
|
|
||||||
return agent.sign(blob, data, alg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Deprecated
|
|
||||||
public boolean decrypt() {
|
|
||||||
throw new RuntimeException("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAlgName() { return algname; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() { return comment; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEncrypted() { return false; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() { }
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
public class AgentIdentityRepository implements IdentityRepository {
|
|
||||||
|
|
||||||
private AgentProxy agent;
|
|
||||||
public AgentIdentityRepository(AgentConnector connector) {
|
|
||||||
this.agent = new AgentProxy(connector);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vector<Identity> getIdentities() {
|
|
||||||
return agent.getIdentities();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean add(byte[] identity) {
|
|
||||||
return agent.addIdentity(identity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean remove(byte[] blob) {
|
|
||||||
return agent.removeIdentity(blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeAll() {
|
|
||||||
agent.removeAllIdentities();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return agent.getConnector().getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getStatus() {
|
|
||||||
if(agent.getConnector().isAvailable()){
|
|
||||||
return RUNNING;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return NOTRUNNING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2012 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
class AgentProxy {
|
|
||||||
|
|
||||||
private static final byte SSH_AGENTC_REQUEST_RSA_IDENTITIES = 1;
|
|
||||||
private static final byte SSH_AGENT_RSA_IDENTITIES_ANSWER = 2;
|
|
||||||
private static final byte SSH_AGENTC_RSA_CHALLENGE = 3;
|
|
||||||
private static final byte SSH_AGENT_RSA_RESPONSE = 4;
|
|
||||||
private static final byte SSH_AGENT_FAILURE = 5;
|
|
||||||
private static final byte SSH_AGENT_SUCCESS = 6;
|
|
||||||
private static final byte SSH_AGENTC_ADD_RSA_IDENTITY = 7;
|
|
||||||
private static final byte SSH_AGENTC_REMOVE_RSA_IDENTITY = 8;
|
|
||||||
private static final byte SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES = 9;
|
|
||||||
|
|
||||||
private static final byte SSH2_AGENTC_REQUEST_IDENTITIES = 11;
|
|
||||||
private static final byte SSH2_AGENT_IDENTITIES_ANSWER = 12;
|
|
||||||
private static final byte SSH2_AGENTC_SIGN_REQUEST = 13;
|
|
||||||
private static final byte SSH2_AGENT_SIGN_RESPONSE = 14;
|
|
||||||
private static final byte SSH2_AGENTC_ADD_IDENTITY = 17;
|
|
||||||
private static final byte SSH2_AGENTC_REMOVE_IDENTITY = 18;
|
|
||||||
private static final byte SSH2_AGENTC_REMOVE_ALL_IDENTITIES = 19;
|
|
||||||
|
|
||||||
private static final byte SSH_AGENTC_ADD_SMARTCARD_KEY = 20;
|
|
||||||
private static final byte SSH_AGENTC_REMOVE_SMARTCARD_KEY = 21;
|
|
||||||
|
|
||||||
private static final byte SSH_AGENTC_LOCK = 22;
|
|
||||||
private static final byte SSH_AGENTC_UNLOCK = 23;
|
|
||||||
|
|
||||||
private static final byte SSH_AGENTC_ADD_RSA_ID_CONSTRAINED = 24;
|
|
||||||
private static final byte SSH2_AGENTC_ADD_ID_CONSTRAINED = 25;
|
|
||||||
private static final byte SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED = 26;
|
|
||||||
|
|
||||||
private static final byte SSH_AGENT_CONSTRAIN_LIFETIME = 1;
|
|
||||||
private static final byte SSH_AGENT_CONSTRAIN_CONFIRM = 2;
|
|
||||||
|
|
||||||
private static final byte SSH2_AGENT_FAILURE = 30;
|
|
||||||
|
|
||||||
private static final byte SSH_COM_AGENT2_FAILURE = 102;
|
|
||||||
|
|
||||||
//private static final byte SSH_AGENT_OLD_SIGNATURE = 0x1;
|
|
||||||
private static final int SSH_AGENT_RSA_SHA2_256 = 0x2;
|
|
||||||
private static final int SSH_AGENT_RSA_SHA2_512 = 0x4;
|
|
||||||
|
|
||||||
private static final int MAX_AGENT_IDENTITIES = 2048;
|
|
||||||
|
|
||||||
private final byte[] buf = new byte[1024];
|
|
||||||
private final Buffer buffer = new Buffer(buf);
|
|
||||||
|
|
||||||
private AgentConnector connector;
|
|
||||||
|
|
||||||
AgentProxy(AgentConnector connector){
|
|
||||||
this.connector = connector;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized Vector<Identity> getIdentities() {
|
|
||||||
Vector<Identity> identities = new Vector<>();
|
|
||||||
|
|
||||||
int required_size = 1 + 4;
|
|
||||||
buffer.reset();
|
|
||||||
buffer.checkFreeSize(required_size);
|
|
||||||
buffer.putInt(required_size - 4);
|
|
||||||
buffer.putByte(SSH2_AGENTC_REQUEST_IDENTITIES);
|
|
||||||
|
|
||||||
try {
|
|
||||||
connector.query(buffer);
|
|
||||||
}
|
|
||||||
catch(AgentProxyException e){
|
|
||||||
buffer.rewind();
|
|
||||||
buffer.putByte(SSH_AGENT_FAILURE);
|
|
||||||
return identities;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rcode = buffer.getByte();
|
|
||||||
|
|
||||||
//System.out.println(rcode == SSH2_AGENT_IDENTITIES_ANSWER);
|
|
||||||
|
|
||||||
if(rcode != SSH2_AGENT_IDENTITIES_ANSWER) {
|
|
||||||
return identities;
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = buffer.getInt();
|
|
||||||
//System.out.println(count);
|
|
||||||
if(count <= 0 || count > MAX_AGENT_IDENTITIES) {
|
|
||||||
return identities;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i=0; i<count; i++){
|
|
||||||
byte[] blob = buffer.getString();
|
|
||||||
String comment = Util.byte2str(buffer.getString());
|
|
||||||
identities.add(new AgentIdentity(this, blob, comment));
|
|
||||||
}
|
|
||||||
|
|
||||||
return identities;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized byte[] sign(byte[] blob, byte[] data, String alg) {
|
|
||||||
int flags = 0x0;
|
|
||||||
if(alg != null) {
|
|
||||||
if(alg.equals("rsa-sha2-256")) {
|
|
||||||
flags = SSH_AGENT_RSA_SHA2_256;
|
|
||||||
}
|
|
||||||
else if(alg.equals("rsa-sha2-512")) {
|
|
||||||
flags = SSH_AGENT_RSA_SHA2_512;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int required_size = 1 + 4*4 + blob.length + data.length;
|
|
||||||
buffer.reset();
|
|
||||||
buffer.checkFreeSize(required_size);
|
|
||||||
buffer.putInt(required_size - 4);
|
|
||||||
buffer.putByte(SSH2_AGENTC_SIGN_REQUEST);
|
|
||||||
buffer.putString(blob);
|
|
||||||
buffer.putString(data);
|
|
||||||
buffer.putInt(flags);
|
|
||||||
|
|
||||||
try {
|
|
||||||
connector.query(buffer);
|
|
||||||
}
|
|
||||||
catch(AgentProxyException e){
|
|
||||||
buffer.rewind();
|
|
||||||
buffer.putByte(SSH_AGENT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int rcode = buffer.getByte();
|
|
||||||
|
|
||||||
//System.out.println(rcode == SSH2_AGENT_SIGN_RESPONSE);
|
|
||||||
|
|
||||||
if(rcode != SSH2_AGENT_SIGN_RESPONSE) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.getString();
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized boolean removeIdentity(byte[] blob) {
|
|
||||||
int required_size = 1 + 4*2 + blob.length;
|
|
||||||
buffer.reset();
|
|
||||||
buffer.checkFreeSize(required_size);
|
|
||||||
buffer.putInt(required_size - 4);
|
|
||||||
buffer.putByte(SSH2_AGENTC_REMOVE_IDENTITY);
|
|
||||||
buffer.putString(blob);
|
|
||||||
|
|
||||||
try {
|
|
||||||
connector.query(buffer);
|
|
||||||
}
|
|
||||||
catch(AgentProxyException e){
|
|
||||||
buffer.rewind();
|
|
||||||
buffer.putByte(SSH_AGENT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int rcode = buffer.getByte();
|
|
||||||
|
|
||||||
//System.out.println(rcode == SSH_AGENT_SUCCESS);
|
|
||||||
|
|
||||||
return rcode == SSH_AGENT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized void removeAllIdentities() {
|
|
||||||
int required_size = 1 + 4;
|
|
||||||
buffer.reset();
|
|
||||||
buffer.checkFreeSize(required_size);
|
|
||||||
buffer.putInt(required_size - 4);
|
|
||||||
buffer.putByte(SSH2_AGENTC_REMOVE_ALL_IDENTITIES);
|
|
||||||
|
|
||||||
try {
|
|
||||||
connector.query(buffer);
|
|
||||||
}
|
|
||||||
catch(AgentProxyException e){
|
|
||||||
buffer.rewind();
|
|
||||||
buffer.putByte(SSH_AGENT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
//int rcode = buffer.getByte();
|
|
||||||
|
|
||||||
//System.out.println(rcode == SSH_AGENT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized boolean addIdentity(byte[] identity) {
|
|
||||||
int required_size = 1 + 4 + identity.length;
|
|
||||||
buffer.reset();
|
|
||||||
buffer.checkFreeSize(required_size);
|
|
||||||
buffer.putInt(required_size - 4);
|
|
||||||
buffer.putByte(SSH2_AGENTC_ADD_IDENTITY);
|
|
||||||
buffer.putByte(identity);
|
|
||||||
|
|
||||||
try {
|
|
||||||
connector.query(buffer);
|
|
||||||
}
|
|
||||||
catch(AgentProxyException e){
|
|
||||||
buffer.rewind();
|
|
||||||
buffer.putByte(SSH_AGENT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int rcode = buffer.getByte();
|
|
||||||
|
|
||||||
//System.out.println(rcode == SSH_AGENT_SUCCESS);
|
|
||||||
|
|
||||||
return rcode == SSH_AGENT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized boolean isRunning(){
|
|
||||||
int required_size = 1 + 4;
|
|
||||||
buffer.reset();
|
|
||||||
buffer.checkFreeSize(required_size);
|
|
||||||
buffer.putInt(required_size - 4);
|
|
||||||
buffer.putByte(SSH2_AGENTC_REQUEST_IDENTITIES);
|
|
||||||
|
|
||||||
try {
|
|
||||||
connector.query(buffer);
|
|
||||||
}
|
|
||||||
catch(AgentProxyException e){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rcode = buffer.getByte();
|
|
||||||
|
|
||||||
//System.out.println(rcode == SSH2_AGENT_IDENTITIES_ANSWER);
|
|
||||||
|
|
||||||
return rcode == SSH2_AGENT_IDENTITIES_ANSWER;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized AgentConnector getConnector() {
|
|
||||||
return connector;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
public class AgentProxyException extends Exception {
|
|
||||||
private static final long serialVersionUID=-1L;
|
|
||||||
public AgentProxyException(String message){
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
public AgentProxyException(String message, Throwable e){
|
|
||||||
super(message, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
public class Buffer{
|
|
||||||
final byte[] tmp=new byte[4];
|
|
||||||
byte[] buffer;
|
|
||||||
int index;
|
|
||||||
int s;
|
|
||||||
public Buffer(int size){
|
|
||||||
buffer=new byte[size];
|
|
||||||
index=0;
|
|
||||||
s=0;
|
|
||||||
}
|
|
||||||
public Buffer(byte[] buffer){
|
|
||||||
this.buffer=buffer;
|
|
||||||
index=0;
|
|
||||||
s=0;
|
|
||||||
}
|
|
||||||
public Buffer(){ this(1024*10*2); }
|
|
||||||
public void putByte(byte foo){
|
|
||||||
buffer[index++]=foo;
|
|
||||||
}
|
|
||||||
public void putByte(byte[] foo) {
|
|
||||||
putByte(foo, 0, foo.length);
|
|
||||||
}
|
|
||||||
public void putByte(byte[] foo, int begin, int length) {
|
|
||||||
System.arraycopy(foo, begin, buffer, index, length);
|
|
||||||
index+=length;
|
|
||||||
}
|
|
||||||
public void putString(byte[] foo){
|
|
||||||
putString(foo, 0, foo.length);
|
|
||||||
}
|
|
||||||
public void putString(byte[] foo, int begin, int length) {
|
|
||||||
putInt(length);
|
|
||||||
putByte(foo, begin, length);
|
|
||||||
}
|
|
||||||
public void putInt(int val) {
|
|
||||||
tmp[0]=(byte)(val >>> 24);
|
|
||||||
tmp[1]=(byte)(val >>> 16);
|
|
||||||
tmp[2]=(byte)(val >>> 8);
|
|
||||||
tmp[3]=(byte)(val);
|
|
||||||
System.arraycopy(tmp, 0, buffer, index, 4);
|
|
||||||
index+=4;
|
|
||||||
}
|
|
||||||
public void putLong(long val) {
|
|
||||||
tmp[0]=(byte)(val >>> 56);
|
|
||||||
tmp[1]=(byte)(val >>> 48);
|
|
||||||
tmp[2]=(byte)(val >>> 40);
|
|
||||||
tmp[3]=(byte)(val >>> 32);
|
|
||||||
System.arraycopy(tmp, 0, buffer, index, 4);
|
|
||||||
tmp[0]=(byte)(val >>> 24);
|
|
||||||
tmp[1]=(byte)(val >>> 16);
|
|
||||||
tmp[2]=(byte)(val >>> 8);
|
|
||||||
tmp[3]=(byte)(val);
|
|
||||||
System.arraycopy(tmp, 0, buffer, index+4, 4);
|
|
||||||
index+=8;
|
|
||||||
}
|
|
||||||
void skip(int n) {
|
|
||||||
index+=n;
|
|
||||||
}
|
|
||||||
void putPad(int n) {
|
|
||||||
while(n>0){
|
|
||||||
buffer[index++]=(byte)0;
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void putMPInt(byte[] foo){
|
|
||||||
int i=foo.length;
|
|
||||||
if((foo[0]&0x80)!=0){
|
|
||||||
i++;
|
|
||||||
putInt(i);
|
|
||||||
putByte((byte)0);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
putInt(i);
|
|
||||||
}
|
|
||||||
putByte(foo);
|
|
||||||
}
|
|
||||||
public int getLength(){
|
|
||||||
return index-s;
|
|
||||||
}
|
|
||||||
public int getOffSet(){
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
public void setOffSet(int s){
|
|
||||||
this.s=s;
|
|
||||||
}
|
|
||||||
public long getLong(){
|
|
||||||
long foo = getInt()&0xffffffffL;
|
|
||||||
foo = ((foo<<32)) | (getInt()&0xffffffffL);
|
|
||||||
return foo;
|
|
||||||
}
|
|
||||||
public int getInt(){
|
|
||||||
int foo = getShort();
|
|
||||||
foo = ((foo<<16)&0xffff0000) | (getShort()&0xffff);
|
|
||||||
return foo;
|
|
||||||
}
|
|
||||||
public long getUInt(){
|
|
||||||
long foo = 0L;
|
|
||||||
long bar = 0L;
|
|
||||||
foo = getByte();
|
|
||||||
foo = ((foo<<8)&0xff00)|(getByte()&0xff);
|
|
||||||
bar = getByte();
|
|
||||||
bar = ((bar<<8)&0xff00)|(getByte()&0xff);
|
|
||||||
foo = ((foo<<16)&0xffff0000) | (bar&0xffff);
|
|
||||||
return foo;
|
|
||||||
}
|
|
||||||
int getShort() {
|
|
||||||
int foo = getByte();
|
|
||||||
foo = ((foo<<8)&0xff00)|(getByte()&0xff);
|
|
||||||
return foo;
|
|
||||||
}
|
|
||||||
public int getByte() {
|
|
||||||
return (buffer[s++]&0xff);
|
|
||||||
}
|
|
||||||
public void getByte(byte[] foo) {
|
|
||||||
getByte(foo, 0, foo.length);
|
|
||||||
}
|
|
||||||
void getByte(byte[] foo, int start, int len) {
|
|
||||||
System.arraycopy(buffer, s, foo, start, len);
|
|
||||||
s+=len;
|
|
||||||
}
|
|
||||||
public int getByte(int len) {
|
|
||||||
int foo=s;
|
|
||||||
s+=len;
|
|
||||||
return foo;
|
|
||||||
}
|
|
||||||
public byte[] getMPInt() {
|
|
||||||
int i=getInt(); // uint32
|
|
||||||
if(i<0 || // bigger than 0x7fffffff
|
|
||||||
i>8*1024){
|
|
||||||
// TODO: an exception should be thrown.
|
|
||||||
i = 8*1024; // the session will be broken, but working around OOME.
|
|
||||||
}
|
|
||||||
byte[] foo=new byte[i];
|
|
||||||
getByte(foo, 0, i);
|
|
||||||
return foo;
|
|
||||||
}
|
|
||||||
public byte[] getMPIntBits() {
|
|
||||||
int bits=getInt();
|
|
||||||
int bytes=(bits+7)/8;
|
|
||||||
byte[] foo=new byte[bytes];
|
|
||||||
getByte(foo, 0, bytes);
|
|
||||||
if((foo[0]&0x80)!=0){
|
|
||||||
byte[] bar=new byte[foo.length+1];
|
|
||||||
bar[0]=0; // ??
|
|
||||||
System.arraycopy(foo, 0, bar, 1, foo.length);
|
|
||||||
foo=bar;
|
|
||||||
}
|
|
||||||
return foo;
|
|
||||||
}
|
|
||||||
public byte[] getString() {
|
|
||||||
int i = getInt(); // uint32
|
|
||||||
if(i<0 || // bigger than 0x7fffffff
|
|
||||||
i>256*1024){
|
|
||||||
// TODO: an exception should be thrown.
|
|
||||||
i = 256*1024; // the session will be broken, but working around OOME.
|
|
||||||
}
|
|
||||||
byte[] foo=new byte[i];
|
|
||||||
getByte(foo, 0, i);
|
|
||||||
return foo;
|
|
||||||
}
|
|
||||||
byte[] getString(int[]start, int[]len) {
|
|
||||||
int i=getInt();
|
|
||||||
start[0]=getByte(i);
|
|
||||||
len[0]=i;
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
public void reset(){
|
|
||||||
index=0;
|
|
||||||
s=0;
|
|
||||||
}
|
|
||||||
public void shift(){
|
|
||||||
if(s==0)return;
|
|
||||||
System.arraycopy(buffer, s, buffer, 0, index-s);
|
|
||||||
index=index-s;
|
|
||||||
s=0;
|
|
||||||
}
|
|
||||||
void rewind(){
|
|
||||||
s=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte getCommand(){
|
|
||||||
return buffer[5];
|
|
||||||
}
|
|
||||||
|
|
||||||
void checkFreeSize(int n){
|
|
||||||
int size = index+n+Session.buffer_margin;
|
|
||||||
if(buffer.length<size){
|
|
||||||
int i = buffer.length*2;
|
|
||||||
if(i<size) i = size;
|
|
||||||
byte[] tmp = new byte[i];
|
|
||||||
System.arraycopy(buffer, 0, tmp, 0, index);
|
|
||||||
buffer = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[][] getBytes(int n, String msg) throws JSchException {
|
|
||||||
byte[][] tmp = new byte[n][];
|
|
||||||
for(int i = 0; i < n; i++){
|
|
||||||
int j = getInt();
|
|
||||||
if(getLength() < j){
|
|
||||||
throw new JSchException(msg);
|
|
||||||
}
|
|
||||||
tmp[i] = new byte[j];
|
|
||||||
getByte(tmp[i]);
|
|
||||||
}
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
static Buffer fromBytes(byte[]... args){
|
|
||||||
int length = args.length*4;
|
|
||||||
for(int i = 0; i < args.length; i++){
|
|
||||||
length += args[i].length;
|
|
||||||
}
|
|
||||||
Buffer buf = new Buffer(length);
|
|
||||||
for(int i = 0; i < args.length; i++){
|
|
||||||
buf.putString(args[i]);
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
static Buffer fromBytes(byte[][] args){
|
|
||||||
int length = args.length*4;
|
|
||||||
for(int i = 0; i < args.length; i++){
|
|
||||||
length += args[i].length;
|
|
||||||
}
|
|
||||||
Buffer buf = new Buffer(length);
|
|
||||||
for(int i = 0; i < args.length; i++){
|
|
||||||
buf.putString(args[i]);
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
static String[] chars={
|
|
||||||
"0","1","2","3","4","5","6","7","8","9", "a","b","c","d","e","f"
|
|
||||||
};
|
|
||||||
static void dump_buffer(){
|
|
||||||
int foo;
|
|
||||||
for(int i=0; i<tmp_buffer_index; i++){
|
|
||||||
foo=tmp_buffer[i]&0xff;
|
|
||||||
System.err.print(chars[(foo>>>4)&0xf]);
|
|
||||||
System.err.print(chars[foo&0xf]);
|
|
||||||
if(i%16==15){
|
|
||||||
System.err.println("");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(i>0 && i%2==1){
|
|
||||||
System.err.print(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.err.println("");
|
|
||||||
}
|
|
||||||
static void dump(byte[] b){
|
|
||||||
dump(b, 0, b.length);
|
|
||||||
}
|
|
||||||
static void dump(byte[] b, int s, int l){
|
|
||||||
for(int i=s; i<s+l; i++){
|
|
||||||
System.err.print(Integer.toHexString(b[i]&0xff)+":");
|
|
||||||
}
|
|
||||||
System.err.println("");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,782 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
public abstract class Channel{
|
|
||||||
|
|
||||||
static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION= 91;
|
|
||||||
static final int SSH_MSG_CHANNEL_OPEN_FAILURE= 92;
|
|
||||||
static final int SSH_MSG_CHANNEL_WINDOW_ADJUST= 93;
|
|
||||||
|
|
||||||
static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED= 1;
|
|
||||||
static final int SSH_OPEN_CONNECT_FAILED= 2;
|
|
||||||
static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE= 3;
|
|
||||||
static final int SSH_OPEN_RESOURCE_SHORTAGE= 4;
|
|
||||||
|
|
||||||
static int index=0;
|
|
||||||
private static Vector<Channel> pool=new Vector<>();
|
|
||||||
static Channel getChannel(String type, Session session){
|
|
||||||
Channel ret = null;
|
|
||||||
if(type.equals("session")){
|
|
||||||
ret = new ChannelSession();
|
|
||||||
}
|
|
||||||
if(type.equals("shell")){
|
|
||||||
ret = new ChannelShell();
|
|
||||||
}
|
|
||||||
if(type.equals("exec")){
|
|
||||||
ret = new ChannelExec();
|
|
||||||
}
|
|
||||||
if(type.equals("x11")){
|
|
||||||
ret = new ChannelX11();
|
|
||||||
}
|
|
||||||
if(type.equals("auth-agent@openssh.com")){
|
|
||||||
ret = new ChannelAgentForwarding();
|
|
||||||
}
|
|
||||||
if(type.equals("direct-tcpip")){
|
|
||||||
ret = new ChannelDirectTCPIP();
|
|
||||||
}
|
|
||||||
if(type.equals("forwarded-tcpip")){
|
|
||||||
ret = new ChannelForwardedTCPIP();
|
|
||||||
}
|
|
||||||
if(type.equals("sftp")){
|
|
||||||
ret = new ChannelSftp();
|
|
||||||
}
|
|
||||||
if(type.equals("subsystem")){
|
|
||||||
ret = new ChannelSubsystem();
|
|
||||||
}
|
|
||||||
if(type.equals("direct-streamlocal@openssh.com")){
|
|
||||||
ret = new ChannelDirectStreamLocal();
|
|
||||||
}
|
|
||||||
if (ret == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ret.setSession(session);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
static Channel getChannel(int id, Session session){
|
|
||||||
synchronized(pool){
|
|
||||||
for(int i=0; i<pool.size(); i++){
|
|
||||||
Channel c=pool.elementAt(i);
|
|
||||||
if(c.id==id && c.session==session) return c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
static void del(Channel c){
|
|
||||||
synchronized(pool){
|
|
||||||
pool.removeElement(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int id;
|
|
||||||
volatile int recipient=-1;
|
|
||||||
protected byte[] type=Util.str2byte("foo");
|
|
||||||
volatile int lwsize_max=0x100000;
|
|
||||||
volatile int lwsize=lwsize_max; // local initial window size
|
|
||||||
volatile int lmpsize=0x4000; // local maximum packet size
|
|
||||||
|
|
||||||
volatile long rwsize=0; // remote initial window size
|
|
||||||
volatile int rmpsize=0; // remote maximum packet size
|
|
||||||
|
|
||||||
IO io=null;
|
|
||||||
Thread thread=null;
|
|
||||||
|
|
||||||
volatile boolean eof_local=false;
|
|
||||||
volatile boolean eof_remote=false;
|
|
||||||
|
|
||||||
volatile boolean close=false;
|
|
||||||
volatile boolean connected=false;
|
|
||||||
volatile boolean open_confirmation=false;
|
|
||||||
|
|
||||||
volatile int exitstatus=-1;
|
|
||||||
|
|
||||||
volatile int reply=0;
|
|
||||||
volatile int connectTimeout=0;
|
|
||||||
|
|
||||||
protected Session session;
|
|
||||||
|
|
||||||
int notifyme=0;
|
|
||||||
|
|
||||||
Channel(){
|
|
||||||
synchronized(pool){
|
|
||||||
id=index++;
|
|
||||||
pool.addElement(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
synchronized void setRecipient(int foo){
|
|
||||||
this.recipient=foo;
|
|
||||||
if(notifyme>0)
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
int getRecipient(){
|
|
||||||
return recipient;
|
|
||||||
}
|
|
||||||
|
|
||||||
void init() throws JSchException {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connect() throws JSchException{
|
|
||||||
connect(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connect(int connectTimeout) throws JSchException{
|
|
||||||
this.connectTimeout=connectTimeout;
|
|
||||||
try{
|
|
||||||
sendChannelOpen();
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
connected=false;
|
|
||||||
disconnect();
|
|
||||||
if(e instanceof JSchException)
|
|
||||||
throw (JSchException)e;
|
|
||||||
throw new JSchException(e.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setXForwarding(boolean foo){
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() throws JSchException{}
|
|
||||||
|
|
||||||
public boolean isEOF() {return eof_remote;}
|
|
||||||
|
|
||||||
void getData(Buffer buf){
|
|
||||||
setRecipient(buf.getInt());
|
|
||||||
setRemoteWindowSize(buf.getUInt());
|
|
||||||
setRemotePacketSize(buf.getInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInputStream(InputStream in){
|
|
||||||
io.setInputStream(in, false);
|
|
||||||
}
|
|
||||||
public void setInputStream(InputStream in, boolean dontclose){
|
|
||||||
io.setInputStream(in, dontclose);
|
|
||||||
}
|
|
||||||
public void setOutputStream(OutputStream out){
|
|
||||||
io.setOutputStream(out, false);
|
|
||||||
}
|
|
||||||
public void setOutputStream(OutputStream out, boolean dontclose){
|
|
||||||
io.setOutputStream(out, dontclose);
|
|
||||||
}
|
|
||||||
public void setExtOutputStream(OutputStream out){
|
|
||||||
io.setExtOutputStream(out, false);
|
|
||||||
}
|
|
||||||
public void setExtOutputStream(OutputStream out, boolean dontclose){
|
|
||||||
io.setExtOutputStream(out, dontclose);
|
|
||||||
}
|
|
||||||
public InputStream getInputStream() throws IOException {
|
|
||||||
int max_input_buffer_size = 32*1024;
|
|
||||||
try {
|
|
||||||
max_input_buffer_size =
|
|
||||||
Integer.parseInt(getSession().getConfig("max_input_buffer_size"));
|
|
||||||
}
|
|
||||||
catch(Exception e){}
|
|
||||||
PipedInputStream in =
|
|
||||||
new MyPipedInputStream(
|
|
||||||
32*1024, // this value should be customizable.
|
|
||||||
max_input_buffer_size
|
|
||||||
);
|
|
||||||
boolean resizable = 32*1024<max_input_buffer_size;
|
|
||||||
io.setOutputStream(new PassiveOutputStream(in, resizable), false);
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
public InputStream getExtInputStream() throws IOException {
|
|
||||||
int max_input_buffer_size = 32*1024;
|
|
||||||
try {
|
|
||||||
max_input_buffer_size =
|
|
||||||
Integer.parseInt(getSession().getConfig("max_input_buffer_size"));
|
|
||||||
}
|
|
||||||
catch(Exception e){}
|
|
||||||
PipedInputStream in =
|
|
||||||
new MyPipedInputStream(
|
|
||||||
32*1024, // this value should be customizable.
|
|
||||||
max_input_buffer_size
|
|
||||||
);
|
|
||||||
boolean resizable = 32*1024<max_input_buffer_size;
|
|
||||||
io.setExtOutputStream(new PassiveOutputStream(in, resizable), false);
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
public OutputStream getOutputStream() throws IOException {
|
|
||||||
|
|
||||||
final Channel channel=this;
|
|
||||||
OutputStream out=new OutputStream(){
|
|
||||||
private int dataLen=0;
|
|
||||||
private Buffer buffer=null;
|
|
||||||
private Packet packet=null;
|
|
||||||
private boolean closed=false;
|
|
||||||
private synchronized void init() throws IOException{
|
|
||||||
buffer=new Buffer(rmpsize);
|
|
||||||
packet=new Packet(buffer);
|
|
||||||
|
|
||||||
byte[] _buf=buffer.buffer;
|
|
||||||
if(_buf.length-(14+0)-Session.buffer_margin<=0){
|
|
||||||
buffer=null;
|
|
||||||
packet=null;
|
|
||||||
throw new IOException("failed to initialize the channel.");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
byte[] b=new byte[1];
|
|
||||||
@Override
|
|
||||||
public void write(int w) throws IOException{
|
|
||||||
b[0]=(byte)w;
|
|
||||||
write(b, 0, 1);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void write(byte[] buf, int s, int l) throws IOException{
|
|
||||||
if(packet==null){
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(closed){
|
|
||||||
throw new IOException("Already closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] _buf=buffer.buffer;
|
|
||||||
int _bufl=_buf.length;
|
|
||||||
while(l>0){
|
|
||||||
int _l=l;
|
|
||||||
if(l>_bufl-(14+dataLen)-Session.buffer_margin){
|
|
||||||
_l=_bufl-(14+dataLen)-Session.buffer_margin;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_l<=0){
|
|
||||||
flush();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.arraycopy(buf, s, _buf, 14+dataLen, _l);
|
|
||||||
dataLen+=_l;
|
|
||||||
s+=_l;
|
|
||||||
l-=_l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() throws IOException{
|
|
||||||
if(closed){
|
|
||||||
throw new IOException("Already closed");
|
|
||||||
}
|
|
||||||
if(dataLen==0)
|
|
||||||
return;
|
|
||||||
packet.reset();
|
|
||||||
buffer.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
|
|
||||||
buffer.putInt(recipient);
|
|
||||||
buffer.putInt(dataLen);
|
|
||||||
buffer.skip(dataLen);
|
|
||||||
try{
|
|
||||||
int foo=dataLen;
|
|
||||||
dataLen=0;
|
|
||||||
synchronized(channel){
|
|
||||||
if(!channel.close)
|
|
||||||
getSession().write(packet, channel, foo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
close();
|
|
||||||
throw new IOException(e.toString(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException{
|
|
||||||
if(packet==null){
|
|
||||||
try{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
catch(IOException e){
|
|
||||||
// close should be finished silently.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(closed){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(dataLen>0){
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
channel.eof();
|
|
||||||
closed=true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MyPipedInputStream extends PipedInputStream{
|
|
||||||
private int BUFFER_SIZE = 1024;
|
|
||||||
private int max_buffer_size = BUFFER_SIZE;
|
|
||||||
MyPipedInputStream() throws IOException{ super(); }
|
|
||||||
MyPipedInputStream(int size) throws IOException{
|
|
||||||
super();
|
|
||||||
buffer=new byte[size];
|
|
||||||
BUFFER_SIZE = size;
|
|
||||||
max_buffer_size = size;
|
|
||||||
}
|
|
||||||
MyPipedInputStream(int size, int max_buffer_size) throws IOException{
|
|
||||||
this(size);
|
|
||||||
this.max_buffer_size = max_buffer_size;
|
|
||||||
}
|
|
||||||
MyPipedInputStream(PipedOutputStream out) throws IOException{ super(out); }
|
|
||||||
MyPipedInputStream(PipedOutputStream out, int size) throws IOException{
|
|
||||||
super(out);
|
|
||||||
buffer=new byte[size];
|
|
||||||
BUFFER_SIZE=size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: We should have our own Piped[I/O]Stream implementation.
|
|
||||||
* Before accepting data, JDK's PipedInputStream will check the existence of
|
|
||||||
* reader thread, and if it is not alive, the stream will be closed.
|
|
||||||
* That behavior may cause the problem if multiple threads make access to it.
|
|
||||||
*/
|
|
||||||
public synchronized void updateReadSide() throws IOException {
|
|
||||||
if(available() != 0){ // not empty
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
in = 0;
|
|
||||||
out = 0;
|
|
||||||
buffer[in++] = 0;
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int freeSpace(){
|
|
||||||
int size = 0;
|
|
||||||
if(out < in) {
|
|
||||||
size = buffer.length-in;
|
|
||||||
}
|
|
||||||
else if(in < out){
|
|
||||||
if(in == -1) size = buffer.length;
|
|
||||||
else size = out - in;
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
synchronized void checkSpace(int len) throws IOException {
|
|
||||||
int size = freeSpace();
|
|
||||||
if(size<len){
|
|
||||||
int datasize=buffer.length-size;
|
|
||||||
int foo = buffer.length;
|
|
||||||
while((foo - datasize) < len){
|
|
||||||
foo*=2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(foo > max_buffer_size){
|
|
||||||
foo = max_buffer_size;
|
|
||||||
}
|
|
||||||
if((foo - datasize) < len) return;
|
|
||||||
|
|
||||||
byte[] tmp = new byte[foo];
|
|
||||||
if(out < in) {
|
|
||||||
System.arraycopy(buffer, 0, tmp, 0, buffer.length);
|
|
||||||
}
|
|
||||||
else if(in < out){
|
|
||||||
if(in == -1) {
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.arraycopy(buffer, 0, tmp, 0, in);
|
|
||||||
System.arraycopy(buffer, out,
|
|
||||||
tmp, tmp.length-(buffer.length-out),
|
|
||||||
(buffer.length-out));
|
|
||||||
out = tmp.length-(buffer.length-out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(in == out){
|
|
||||||
System.arraycopy(buffer, 0, tmp, 0, buffer.length);
|
|
||||||
in=buffer.length;
|
|
||||||
}
|
|
||||||
buffer=tmp;
|
|
||||||
}
|
|
||||||
else if(buffer.length == size && size > BUFFER_SIZE) {
|
|
||||||
int i = size/2;
|
|
||||||
if(i<BUFFER_SIZE) i = BUFFER_SIZE;
|
|
||||||
byte[] tmp = new byte[i];
|
|
||||||
buffer=tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void setLocalWindowSizeMax(int foo){ this.lwsize_max=foo; }
|
|
||||||
void setLocalWindowSize(int foo){ this.lwsize=foo; }
|
|
||||||
void setLocalPacketSize(int foo){ this.lmpsize=foo; }
|
|
||||||
synchronized void setRemoteWindowSize(long foo){ this.rwsize=foo; }
|
|
||||||
synchronized void addRemoteWindowSize(long foo){
|
|
||||||
this.rwsize+=foo;
|
|
||||||
if(notifyme>0)
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
void setRemotePacketSize(int foo){ this.rmpsize=foo; }
|
|
||||||
|
|
||||||
abstract void run();
|
|
||||||
|
|
||||||
void write(byte[] foo) throws IOException {
|
|
||||||
write(foo, 0, foo.length);
|
|
||||||
}
|
|
||||||
void write(byte[] foo, int s, int l) throws IOException {
|
|
||||||
try{
|
|
||||||
io.put(foo, s, l);
|
|
||||||
}catch(NullPointerException e){}
|
|
||||||
}
|
|
||||||
void write_ext(byte[] foo, int s, int l) throws IOException {
|
|
||||||
try{
|
|
||||||
io.put_ext(foo, s, l);
|
|
||||||
}catch(NullPointerException e){}
|
|
||||||
}
|
|
||||||
|
|
||||||
void eof_remote(){
|
|
||||||
eof_remote=true;
|
|
||||||
try{
|
|
||||||
io.out_close();
|
|
||||||
}
|
|
||||||
catch(NullPointerException e){}
|
|
||||||
}
|
|
||||||
|
|
||||||
void eof(){
|
|
||||||
if(eof_local)return;
|
|
||||||
eof_local=true;
|
|
||||||
|
|
||||||
int i = getRecipient();
|
|
||||||
if(i == -1) return;
|
|
||||||
|
|
||||||
try{
|
|
||||||
Buffer buf=new Buffer(100);
|
|
||||||
Packet packet=new Packet(buf);
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte)Session.SSH_MSG_CHANNEL_EOF);
|
|
||||||
buf.putInt(i);
|
|
||||||
synchronized(this){
|
|
||||||
if(!close)
|
|
||||||
getSession().write(packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
//System.err.println("Channel.eof");
|
|
||||||
//e.printStackTrace();
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if(!isConnected()){ disconnect(); }
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
http://www1.ietf.org/internet-drafts/draft-ietf-secsh-connect-24.txt
|
|
||||||
|
|
||||||
5.3 Closing a Channel
|
|
||||||
When a party will no longer send more data to a channel, it SHOULD
|
|
||||||
send SSH_MSG_CHANNEL_EOF.
|
|
||||||
|
|
||||||
byte SSH_MSG_CHANNEL_EOF
|
|
||||||
uint32 recipient_channel
|
|
||||||
|
|
||||||
No explicit response is sent to this message. However, the
|
|
||||||
application may send EOF to whatever is at the other end of the
|
|
||||||
channel. Note that the channel remains open after this message, and
|
|
||||||
more data may still be sent in the other direction. This message
|
|
||||||
does not consume window space and can be sent even if no window space
|
|
||||||
is available.
|
|
||||||
|
|
||||||
When either party wishes to terminate the channel, it sends
|
|
||||||
SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST
|
|
||||||
send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this
|
|
||||||
message for the channel. The channel is considered closed for a
|
|
||||||
party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and
|
|
||||||
the party may then reuse the channel number. A party MAY send
|
|
||||||
SSH_MSG_CHANNEL_CLOSE without having sent or received
|
|
||||||
SSH_MSG_CHANNEL_EOF.
|
|
||||||
|
|
||||||
byte SSH_MSG_CHANNEL_CLOSE
|
|
||||||
uint32 recipient_channel
|
|
||||||
|
|
||||||
This message does not consume window space and can be sent even if no
|
|
||||||
window space is available.
|
|
||||||
|
|
||||||
It is recommended that any data sent before this message is delivered
|
|
||||||
to the actual destination, if possible.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void close(){
|
|
||||||
if(close)return;
|
|
||||||
close=true;
|
|
||||||
eof_local=eof_remote=true;
|
|
||||||
|
|
||||||
int i = getRecipient();
|
|
||||||
if(i == -1) return;
|
|
||||||
|
|
||||||
try{
|
|
||||||
Buffer buf=new Buffer(100);
|
|
||||||
Packet packet=new Packet(buf);
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte)Session.SSH_MSG_CHANNEL_CLOSE);
|
|
||||||
buf.putInt(i);
|
|
||||||
synchronized(this){
|
|
||||||
getSession().write(packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
//e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public boolean isClosed(){
|
|
||||||
return close;
|
|
||||||
}
|
|
||||||
static void disconnect(Session session){
|
|
||||||
Channel[] channels=null;
|
|
||||||
int count=0;
|
|
||||||
synchronized(pool){
|
|
||||||
channels=new Channel[pool.size()];
|
|
||||||
for(int i=0; i<pool.size(); i++){
|
|
||||||
try{
|
|
||||||
Channel c=pool.elementAt(i);
|
|
||||||
if(c.session==session){
|
|
||||||
channels[count++]=c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(int i=0; i<count; i++){
|
|
||||||
channels[i].disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnect(){
|
|
||||||
//System.err.println(this+":disconnect "+io+" "+connected);
|
|
||||||
//Thread.dumpStack();
|
|
||||||
|
|
||||||
try{
|
|
||||||
|
|
||||||
synchronized(this){
|
|
||||||
if(!connected){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connected=false;
|
|
||||||
}
|
|
||||||
|
|
||||||
close();
|
|
||||||
|
|
||||||
eof_remote=eof_local=true;
|
|
||||||
|
|
||||||
thread=null;
|
|
||||||
|
|
||||||
try{
|
|
||||||
if(io!=null){
|
|
||||||
io.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
//e.printStackTrace();
|
|
||||||
}
|
|
||||||
// io=null;
|
|
||||||
}
|
|
||||||
finally{
|
|
||||||
Channel.del(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isConnected(){
|
|
||||||
Session _session=this.session;
|
|
||||||
if(_session!=null){
|
|
||||||
return _session.isConnected() && connected;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendSignal(String signal) throws Exception {
|
|
||||||
RequestSignal request=new RequestSignal();
|
|
||||||
request.setSignal(signal);
|
|
||||||
request.request(getSession(), this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// public String toString(){
|
|
||||||
// return "Channel: type="+new String(type)+",id="+id+",recipient="+recipient+",window_size="+window_size+",packet_size="+packet_size;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/*
|
|
||||||
class OutputThread extends Thread{
|
|
||||||
Channel c;
|
|
||||||
OutputThread(Channel c){ this.c=c;}
|
|
||||||
public void run(){c.output_thread();}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
static class PassiveInputStream extends MyPipedInputStream{
|
|
||||||
PipedOutputStream os;
|
|
||||||
PassiveInputStream(PipedOutputStream out, int size) throws IOException{
|
|
||||||
super(out, size);
|
|
||||||
this.os=out;
|
|
||||||
}
|
|
||||||
PassiveInputStream(PipedOutputStream out) throws IOException{
|
|
||||||
super(out);
|
|
||||||
this.os=out;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException{
|
|
||||||
if(this.os!=null){
|
|
||||||
this.os.close();
|
|
||||||
}
|
|
||||||
this.os=null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static class PassiveOutputStream extends PipedOutputStream{
|
|
||||||
private MyPipedInputStream _sink=null;
|
|
||||||
PassiveOutputStream(PipedInputStream in,
|
|
||||||
boolean resizable_buffer) throws IOException{
|
|
||||||
super(in);
|
|
||||||
if(resizable_buffer && (in instanceof MyPipedInputStream)) {
|
|
||||||
this._sink=(MyPipedInputStream)in;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void write(int b) throws IOException {
|
|
||||||
if(_sink != null) {
|
|
||||||
_sink.checkSpace(1);
|
|
||||||
}
|
|
||||||
super.write(b);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void write(byte[] b, int off, int len) throws IOException {
|
|
||||||
if(_sink != null) {
|
|
||||||
_sink.checkSpace(len);
|
|
||||||
}
|
|
||||||
super.write(b, off, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setExitStatus(int status){ exitstatus=status; }
|
|
||||||
public int getExitStatus(){ return exitstatus; }
|
|
||||||
|
|
||||||
void setSession(Session session){
|
|
||||||
this.session=session;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Session getSession() throws JSchException{
|
|
||||||
Session _session=session;
|
|
||||||
if(_session==null){
|
|
||||||
throw new JSchException("session is not available");
|
|
||||||
}
|
|
||||||
return _session;
|
|
||||||
}
|
|
||||||
public int getId(){ return id; }
|
|
||||||
|
|
||||||
protected void sendOpenConfirmation() throws Exception{
|
|
||||||
Buffer buf=new Buffer(200);
|
|
||||||
Packet packet=new Packet(buf);
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
|
|
||||||
buf.putInt(getRecipient());
|
|
||||||
buf.putInt(id);
|
|
||||||
buf.putInt(lwsize);
|
|
||||||
buf.putInt(lmpsize);
|
|
||||||
getSession().write(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void sendOpenFailure(int reasoncode){
|
|
||||||
try{
|
|
||||||
Buffer buf=new Buffer(200);
|
|
||||||
Packet packet=new Packet(buf);
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte)SSH_MSG_CHANNEL_OPEN_FAILURE);
|
|
||||||
buf.putInt(getRecipient());
|
|
||||||
buf.putInt(reasoncode);
|
|
||||||
buf.putString(Util.str2byte("open failed"));
|
|
||||||
buf.putString(Util.empty);
|
|
||||||
getSession().write(packet);
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Packet genChannelOpenPacket(){
|
|
||||||
Buffer buf=new Buffer(200);
|
|
||||||
Packet packet=new Packet(buf);
|
|
||||||
// byte SSH_MSG_CHANNEL_OPEN(90)
|
|
||||||
// string channel type //
|
|
||||||
// uint32 sender channel // 0
|
|
||||||
// uint32 initial window size // 0x100000(65536)
|
|
||||||
// uint32 maxmum packet size // 0x4000(16384)
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte)90);
|
|
||||||
buf.putString(this.type);
|
|
||||||
buf.putInt(this.id);
|
|
||||||
buf.putInt(this.lwsize);
|
|
||||||
buf.putInt(this.lmpsize);
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void sendChannelOpen() throws Exception {
|
|
||||||
Session _session=getSession();
|
|
||||||
if(!_session.isConnected()){
|
|
||||||
throw new JSchException("session is down");
|
|
||||||
}
|
|
||||||
|
|
||||||
Packet packet = genChannelOpenPacket();
|
|
||||||
_session.write(packet);
|
|
||||||
|
|
||||||
int retry=2000;
|
|
||||||
long start=System.currentTimeMillis();
|
|
||||||
long timeout=connectTimeout;
|
|
||||||
if(timeout!=0L) retry = 1;
|
|
||||||
synchronized(this){
|
|
||||||
while(this.getRecipient()==-1 &&
|
|
||||||
_session.isConnected() &&
|
|
||||||
retry>0){
|
|
||||||
if(timeout>0L){
|
|
||||||
if((System.currentTimeMillis()-start)>timeout){
|
|
||||||
retry=0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try{
|
|
||||||
long t = timeout==0L ? 10L : timeout;
|
|
||||||
this.notifyme=1;
|
|
||||||
wait(t);
|
|
||||||
}
|
|
||||||
catch(InterruptedException e){
|
|
||||||
}
|
|
||||||
finally{
|
|
||||||
this.notifyme=0;
|
|
||||||
}
|
|
||||||
retry--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!_session.isConnected()){
|
|
||||||
throw new JSchException("session is down");
|
|
||||||
}
|
|
||||||
if(this.getRecipient()==-1){ // timeout
|
|
||||||
throw new JSchException("channel is not opened.");
|
|
||||||
}
|
|
||||||
if(this.open_confirmation==false){ // SSH_MSG_CHANNEL_OPEN_FAILURE
|
|
||||||
throw new JSchException("channel is not opened.");
|
|
||||||
}
|
|
||||||
connected=true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,287 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.*;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
class ChannelAgentForwarding extends Channel{
|
|
||||||
|
|
||||||
static private final int LOCAL_WINDOW_SIZE_MAX=0x20000;
|
|
||||||
static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000;
|
|
||||||
|
|
||||||
static private final byte SSH_AGENTC_REQUEST_RSA_IDENTITIES = 1;
|
|
||||||
static private final byte SSH_AGENT_RSA_IDENTITIES_ANSWER = 2;
|
|
||||||
static private final byte SSH_AGENTC_RSA_CHALLENGE = 3;
|
|
||||||
static private final byte SSH_AGENT_RSA_RESPONSE = 4;
|
|
||||||
static private final byte SSH_AGENT_FAILURE = 5;
|
|
||||||
static private final byte SSH_AGENT_SUCCESS = 6;
|
|
||||||
static private final byte SSH_AGENTC_ADD_RSA_IDENTITY = 7;
|
|
||||||
static private final byte SSH_AGENTC_REMOVE_RSA_IDENTITY = 8;
|
|
||||||
static private final byte SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES = 9;
|
|
||||||
|
|
||||||
static private final byte SSH2_AGENTC_REQUEST_IDENTITIES=11;
|
|
||||||
static private final byte SSH2_AGENT_IDENTITIES_ANSWER=12;
|
|
||||||
static private final byte SSH2_AGENTC_SIGN_REQUEST=13;
|
|
||||||
static private final byte SSH2_AGENT_SIGN_RESPONSE=14;
|
|
||||||
static private final byte SSH2_AGENTC_ADD_IDENTITY=17;
|
|
||||||
static private final byte SSH2_AGENTC_REMOVE_IDENTITY=18;
|
|
||||||
static private final byte SSH2_AGENTC_REMOVE_ALL_IDENTITIES=19;
|
|
||||||
static private final byte SSH2_AGENT_FAILURE=30;
|
|
||||||
|
|
||||||
//static private final int SSH_AGENT_OLD_SIGNATURE=0x1;
|
|
||||||
static private final int SSH_AGENT_RSA_SHA2_256=0x2;
|
|
||||||
static private final int SSH_AGENT_RSA_SHA2_512=0x4;
|
|
||||||
|
|
||||||
private Buffer rbuf=null;
|
|
||||||
private Buffer wbuf=null;
|
|
||||||
private Packet packet=null;
|
|
||||||
private Buffer mbuf=null;
|
|
||||||
|
|
||||||
ChannelAgentForwarding(){
|
|
||||||
super();
|
|
||||||
|
|
||||||
setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
|
|
||||||
setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
|
|
||||||
setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
|
|
||||||
|
|
||||||
type=Util.str2byte("auth-agent@openssh.com");
|
|
||||||
rbuf=new Buffer();
|
|
||||||
rbuf.reset();
|
|
||||||
//wbuf=new Buffer(rmpsize);
|
|
||||||
//packet=new Packet(wbuf);
|
|
||||||
mbuf=new Buffer();
|
|
||||||
connected=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void run(){
|
|
||||||
try{
|
|
||||||
sendOpenConfirmation();
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
close=true;
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void write(byte[] foo, int s, int l) throws IOException {
|
|
||||||
|
|
||||||
if(packet==null){
|
|
||||||
wbuf=new Buffer(rmpsize);
|
|
||||||
packet=new Packet(wbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
rbuf.shift();
|
|
||||||
if(rbuf.buffer.length<rbuf.index+l){
|
|
||||||
byte[] newbuf=new byte[rbuf.s+l];
|
|
||||||
System.arraycopy(rbuf.buffer, 0, newbuf, 0, rbuf.buffer.length);
|
|
||||||
rbuf.buffer=newbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
rbuf.putByte(foo, s, l);
|
|
||||||
|
|
||||||
int mlen=rbuf.getInt();
|
|
||||||
if(mlen>rbuf.getLength()){
|
|
||||||
rbuf.s-=4;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int typ=rbuf.getByte();
|
|
||||||
|
|
||||||
Session _session=null;
|
|
||||||
try{
|
|
||||||
_session=getSession();
|
|
||||||
}
|
|
||||||
catch(JSchException e){
|
|
||||||
throw new IOException(e.toString(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityRepository irepo = _session.getIdentityRepository();
|
|
||||||
UserInfo userinfo=_session.getUserInfo();
|
|
||||||
|
|
||||||
mbuf.reset();
|
|
||||||
|
|
||||||
if(typ==SSH2_AGENTC_REQUEST_IDENTITIES){
|
|
||||||
mbuf.putByte(SSH2_AGENT_IDENTITIES_ANSWER);
|
|
||||||
Vector<Identity> identities = irepo.getIdentities();
|
|
||||||
synchronized(identities){
|
|
||||||
int count=0;
|
|
||||||
for(int i=0; i<identities.size(); i++){
|
|
||||||
Identity identity=identities.elementAt(i);
|
|
||||||
if(identity.getPublicKeyBlob()!=null)
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
mbuf.putInt(count);
|
|
||||||
for(int i=0; i<identities.size(); i++){
|
|
||||||
Identity identity=identities.elementAt(i);
|
|
||||||
byte[] pubkeyblob=identity.getPublicKeyBlob();
|
|
||||||
if(pubkeyblob==null)
|
|
||||||
continue;
|
|
||||||
mbuf.putString(pubkeyblob);
|
|
||||||
mbuf.putString(Util.empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(typ==SSH_AGENTC_REQUEST_RSA_IDENTITIES) {
|
|
||||||
mbuf.putByte(SSH_AGENT_RSA_IDENTITIES_ANSWER);
|
|
||||||
mbuf.putInt(0);
|
|
||||||
}
|
|
||||||
else if(typ==SSH2_AGENTC_SIGN_REQUEST){
|
|
||||||
byte[] blob=rbuf.getString();
|
|
||||||
byte[] data=rbuf.getString();
|
|
||||||
int flags=rbuf.getInt();
|
|
||||||
|
|
||||||
// if((flags & SSH_AGENT_OLD_SIGNATURE)!=0){ // old OpenSSH 2.0, 2.1
|
|
||||||
// datafellows = SSH_BUG_SIGBLOB;
|
|
||||||
// }
|
|
||||||
|
|
||||||
Vector<Identity> identities = irepo.getIdentities();
|
|
||||||
Identity identity = null;
|
|
||||||
synchronized(identities){
|
|
||||||
for(int i=0; i<identities.size(); i++){
|
|
||||||
Identity _identity=identities.elementAt(i);
|
|
||||||
if(_identity.getPublicKeyBlob()==null)
|
|
||||||
continue;
|
|
||||||
if(!Util.array_equals(blob, _identity.getPublicKeyBlob())){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(_identity.isEncrypted()){
|
|
||||||
if(userinfo==null)
|
|
||||||
continue;
|
|
||||||
while(_identity.isEncrypted()){
|
|
||||||
if(!userinfo.promptPassphrase("Passphrase for "+_identity.getName())){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _passphrase=userinfo.getPassphrase();
|
|
||||||
if(_passphrase==null){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] passphrase=Util.str2byte(_passphrase);
|
|
||||||
try{
|
|
||||||
if(_identity.setPassphrase(passphrase)){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(JSchException e){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!_identity.isEncrypted()){
|
|
||||||
identity=_identity;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] signature=null;
|
|
||||||
|
|
||||||
if(identity!=null){
|
|
||||||
Buffer kbuf=new Buffer(blob);
|
|
||||||
String keytype=Util.byte2str(kbuf.getString());
|
|
||||||
if(keytype.equals("ssh-rsa")){
|
|
||||||
if((flags & SSH_AGENT_RSA_SHA2_256)!=0){
|
|
||||||
signature=identity.getSignature(data, "rsa-sha2-256");
|
|
||||||
}
|
|
||||||
else if((flags & SSH_AGENT_RSA_SHA2_512)!=0){
|
|
||||||
signature=identity.getSignature(data, "rsa-sha2-512");
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
signature=identity.getSignature(data, "ssh-rsa");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
signature=identity.getSignature(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(signature==null){
|
|
||||||
mbuf.putByte(SSH2_AGENT_FAILURE);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
mbuf.putByte(SSH2_AGENT_SIGN_RESPONSE);
|
|
||||||
mbuf.putString(signature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(typ==SSH2_AGENTC_REMOVE_IDENTITY){
|
|
||||||
byte[] blob=rbuf.getString();
|
|
||||||
irepo.remove(blob);
|
|
||||||
mbuf.putByte(SSH_AGENT_SUCCESS);
|
|
||||||
}
|
|
||||||
else if(typ==SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES){
|
|
||||||
mbuf.putByte(SSH_AGENT_SUCCESS);
|
|
||||||
}
|
|
||||||
else if(typ==SSH2_AGENTC_REMOVE_ALL_IDENTITIES){
|
|
||||||
irepo.removeAll();
|
|
||||||
mbuf.putByte(SSH_AGENT_SUCCESS);
|
|
||||||
}
|
|
||||||
else if(typ==SSH2_AGENTC_ADD_IDENTITY){
|
|
||||||
int fooo = rbuf.getLength();
|
|
||||||
byte[] tmp = new byte[fooo];
|
|
||||||
rbuf.getByte(tmp);
|
|
||||||
boolean result = irepo.add(tmp);
|
|
||||||
mbuf.putByte(result ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rbuf.skip(rbuf.getLength()-1);
|
|
||||||
mbuf.putByte(SSH_AGENT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] response = new byte[mbuf.getLength()];
|
|
||||||
mbuf.getByte(response);
|
|
||||||
send(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void send(byte[] message){
|
|
||||||
packet.reset();
|
|
||||||
wbuf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
|
|
||||||
wbuf.putInt(recipient);
|
|
||||||
wbuf.putInt(4+message.length);
|
|
||||||
wbuf.putString(message);
|
|
||||||
|
|
||||||
try{
|
|
||||||
getSession().write(packet, this, 4+message.length);
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void eof_remote(){
|
|
||||||
super.eof_remote();
|
|
||||||
eof();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import static com.jcraft.jsch.Session.SSH_MSG_CHANNEL_OPEN;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extension of {@link ChannelDirectTCPIP} to support socket forwarding.
|
|
||||||
* <p>
|
|
||||||
* https://raw.githubusercontent.com/openssh/openssh-portable/master/PROTOCOL
|
|
||||||
*/
|
|
||||||
public class ChannelDirectStreamLocal extends ChannelDirectTCPIP {
|
|
||||||
|
|
||||||
static private final int LOCAL_WINDOW_SIZE_MAX = 0x20000;
|
|
||||||
static private final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000;
|
|
||||||
static private final byte[] _type = Util.str2byte("direct-streamlocal@openssh.com");
|
|
||||||
|
|
||||||
private String socketPath;
|
|
||||||
|
|
||||||
ChannelDirectStreamLocal() {
|
|
||||||
super();
|
|
||||||
type = _type;
|
|
||||||
setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
|
|
||||||
setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
|
|
||||||
setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Packet genChannelOpenPacket() {
|
|
||||||
|
|
||||||
if (socketPath == null) {
|
|
||||||
session.getLogger().log(Logger.FATAL, "socketPath must be set");
|
|
||||||
throw new RuntimeException("socketPath must be set");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Similar to direct-tcpip, direct-streamlocal is sent by the client
|
|
||||||
to request that the server make a connection to a Unix domain socket.
|
|
||||||
|
|
||||||
byte SSH_MSG_CHANNEL_OPEN
|
|
||||||
string "direct-streamlocal@openssh.com"
|
|
||||||
uint32 sender channel
|
|
||||||
uint32 initial window size
|
|
||||||
uint32 maximum packet size
|
|
||||||
string socket path
|
|
||||||
string reserved
|
|
||||||
uint32 reserved
|
|
||||||
*/
|
|
||||||
|
|
||||||
Buffer buf = new Buffer(50 +
|
|
||||||
socketPath.length() +
|
|
||||||
Session.buffer_margin);
|
|
||||||
Packet packet = new Packet(buf);
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte) SSH_MSG_CHANNEL_OPEN);
|
|
||||||
buf.putString(this.type);
|
|
||||||
buf.putInt(id);
|
|
||||||
buf.putInt(lwsize);
|
|
||||||
buf.putInt(lmpsize);
|
|
||||||
buf.putString(Util.str2byte(socketPath));
|
|
||||||
buf.putString(Util.str2byte(originator_IP_address));
|
|
||||||
buf.putInt(originator_port);
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSocketPath() {
|
|
||||||
return socketPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSocketPath(String socketPath) {
|
|
||||||
this.socketPath = socketPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
public class ChannelDirectTCPIP extends Channel{
|
|
||||||
|
|
||||||
static private final int LOCAL_WINDOW_SIZE_MAX=0x20000;
|
|
||||||
static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000;
|
|
||||||
static private final byte[] _type = Util.str2byte("direct-tcpip");
|
|
||||||
String host;
|
|
||||||
int port;
|
|
||||||
|
|
||||||
String originator_IP_address="127.0.0.1";
|
|
||||||
int originator_port=0;
|
|
||||||
|
|
||||||
ChannelDirectTCPIP(){
|
|
||||||
super();
|
|
||||||
type = _type;
|
|
||||||
setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
|
|
||||||
setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
|
|
||||||
setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void init (){
|
|
||||||
io=new IO();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connect(int connectTimeout) throws JSchException{
|
|
||||||
this.connectTimeout=connectTimeout;
|
|
||||||
try{
|
|
||||||
Session _session=getSession();
|
|
||||||
if(!_session.isConnected()){
|
|
||||||
throw new JSchException("session is down");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.in!=null){
|
|
||||||
thread=new Thread(this::run);
|
|
||||||
thread.setName("DirectTCPIP thread "+_session.getHost());
|
|
||||||
if(_session.daemon_thread){
|
|
||||||
thread.setDaemon(_session.daemon_thread);
|
|
||||||
}
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sendChannelOpen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
io.close();
|
|
||||||
io=null;
|
|
||||||
Channel.del(this);
|
|
||||||
if (e instanceof JSchException) {
|
|
||||||
throw (JSchException) e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void run(){
|
|
||||||
|
|
||||||
try{
|
|
||||||
sendChannelOpen();
|
|
||||||
|
|
||||||
Buffer buf=new Buffer(rmpsize);
|
|
||||||
Packet packet=new Packet(buf);
|
|
||||||
Session _session=getSession();
|
|
||||||
int i=0;
|
|
||||||
|
|
||||||
while(isConnected() &&
|
|
||||||
thread!=null &&
|
|
||||||
io!=null &&
|
|
||||||
io.in!=null){
|
|
||||||
i=io.in.read(buf.buffer,
|
|
||||||
14,
|
|
||||||
buf.buffer.length-14
|
|
||||||
-Session.buffer_margin
|
|
||||||
);
|
|
||||||
if(i<=0){
|
|
||||||
eof();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
|
|
||||||
buf.putInt(recipient);
|
|
||||||
buf.putInt(i);
|
|
||||||
buf.skip(i);
|
|
||||||
synchronized(this){
|
|
||||||
if(close)
|
|
||||||
break;
|
|
||||||
_session.write(packet, this, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
// Whenever an exception is thrown by sendChannelOpen(),
|
|
||||||
// 'connected' is false.
|
|
||||||
if(!connected){
|
|
||||||
connected=true;
|
|
||||||
}
|
|
||||||
disconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
eof();
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setInputStream(InputStream in){
|
|
||||||
io.setInputStream(in);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void setOutputStream(OutputStream out){
|
|
||||||
io.setOutputStream(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHost(String host){this.host=host;}
|
|
||||||
public void setPort(int port){this.port=port;}
|
|
||||||
public void setOrgIPAddress(String foo){this.originator_IP_address=foo;}
|
|
||||||
public void setOrgPort(int foo){this.originator_port=foo;}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Packet genChannelOpenPacket(){
|
|
||||||
Buffer buf = new Buffer(50 + // 6 + 4*8 + 12
|
|
||||||
host.length() + originator_IP_address.length() +
|
|
||||||
Session.buffer_margin);
|
|
||||||
Packet packet = new Packet(buf);
|
|
||||||
// byte SSH_MSG_CHANNEL_OPEN(90)
|
|
||||||
// string channel type //
|
|
||||||
// uint32 sender channel // 0
|
|
||||||
// uint32 initial window size // 0x100000(65536)
|
|
||||||
// uint32 maxmum packet size // 0x4000(16384)
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte)90);
|
|
||||||
buf.putString(this.type);
|
|
||||||
buf.putInt(id);
|
|
||||||
buf.putInt(lwsize);
|
|
||||||
buf.putInt(lmpsize);
|
|
||||||
buf.putString(Util.str2byte(host));
|
|
||||||
buf.putInt(port);
|
|
||||||
buf.putString(Util.str2byte(originator_IP_address));
|
|
||||||
buf.putInt(originator_port);
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class ChannelExec extends ChannelSession{
|
|
||||||
|
|
||||||
byte[] command=new byte[0];
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() throws JSchException{
|
|
||||||
Session _session=getSession();
|
|
||||||
try{
|
|
||||||
sendRequests();
|
|
||||||
Request request=new RequestExec(command);
|
|
||||||
request.request(_session, this);
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
if(e instanceof JSchException) throw (JSchException)e;
|
|
||||||
throw new JSchException("ChannelExec", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.in!=null){
|
|
||||||
thread=new Thread(this::run);
|
|
||||||
thread.setName("Exec thread "+_session.getHost());
|
|
||||||
if(_session.daemon_thread){
|
|
||||||
thread.setDaemon(_session.daemon_thread);
|
|
||||||
}
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCommand(String command){
|
|
||||||
this.command=Util.str2byte(command);
|
|
||||||
}
|
|
||||||
public void setCommand(byte[] command){
|
|
||||||
this.command=command;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void init() throws JSchException {
|
|
||||||
io.setInputStream(getSession().in);
|
|
||||||
io.setOutputStream(getSession().out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setErrStream(OutputStream out){
|
|
||||||
setExtOutputStream(out);
|
|
||||||
}
|
|
||||||
public void setErrStream(OutputStream out, boolean dontclose){
|
|
||||||
setExtOutputStream(out, dontclose);
|
|
||||||
}
|
|
||||||
public InputStream getErrStream() throws IOException {
|
|
||||||
return getExtInputStream();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,335 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.net.*;
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
public class ChannelForwardedTCPIP extends Channel{
|
|
||||||
|
|
||||||
private static Vector<Config> pool = new Vector<>();
|
|
||||||
|
|
||||||
static private final int LOCAL_WINDOW_SIZE_MAX=0x20000;
|
|
||||||
//static private final int LOCAL_WINDOW_SIZE_MAX=0x100000;
|
|
||||||
static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000;
|
|
||||||
|
|
||||||
static private final int TIMEOUT=10*1000;
|
|
||||||
|
|
||||||
private Socket socket=null;
|
|
||||||
private ForwardedTCPIPDaemon daemon=null;
|
|
||||||
private Config config = null;
|
|
||||||
|
|
||||||
ChannelForwardedTCPIP(){
|
|
||||||
super();
|
|
||||||
setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
|
|
||||||
setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
|
|
||||||
setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
|
|
||||||
io=new IO();
|
|
||||||
connected=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run(){
|
|
||||||
try{
|
|
||||||
if(config instanceof ConfigDaemon){
|
|
||||||
ConfigDaemon _config = (ConfigDaemon)config;
|
|
||||||
Class<? extends ForwardedTCPIPDaemon> c=Class.forName(_config.target).asSubclass(ForwardedTCPIPDaemon.class);
|
|
||||||
daemon=c.getDeclaredConstructor().newInstance();
|
|
||||||
|
|
||||||
PipedOutputStream out=new PipedOutputStream();
|
|
||||||
io.setInputStream(new PassiveInputStream(out
|
|
||||||
, 32*1024
|
|
||||||
), false);
|
|
||||||
|
|
||||||
daemon.setChannel(this, getInputStream(), out);
|
|
||||||
daemon.setArg(_config.arg);
|
|
||||||
new Thread(daemon).start();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
ConfigLHost _config = (ConfigLHost)config;
|
|
||||||
socket=(_config.factory==null) ?
|
|
||||||
Util.createSocket(_config.target, _config.lport, TIMEOUT) :
|
|
||||||
_config.factory.createSocket(_config.target, _config.lport);
|
|
||||||
socket.setTcpNoDelay(true);
|
|
||||||
io.setInputStream(socket.getInputStream());
|
|
||||||
io.setOutputStream(socket.getOutputStream());
|
|
||||||
}
|
|
||||||
sendOpenConfirmation();
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
sendOpenFailure(SSH_OPEN_ADMINISTRATIVELY_PROHIBITED);
|
|
||||||
close=true;
|
|
||||||
disconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread=Thread.currentThread();
|
|
||||||
Buffer buf=new Buffer(rmpsize);
|
|
||||||
Packet packet=new Packet(buf);
|
|
||||||
int i=0;
|
|
||||||
try{
|
|
||||||
Session _session = getSession();
|
|
||||||
while(thread!=null &&
|
|
||||||
io!=null &&
|
|
||||||
io.in!=null){
|
|
||||||
i=io.in.read(buf.buffer,
|
|
||||||
14,
|
|
||||||
buf.buffer.length-14
|
|
||||||
-Session.buffer_margin
|
|
||||||
);
|
|
||||||
if(i<=0){
|
|
||||||
eof();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
|
|
||||||
buf.putInt(recipient);
|
|
||||||
buf.putInt(i);
|
|
||||||
buf.skip(i);
|
|
||||||
synchronized(this){
|
|
||||||
if(close)
|
|
||||||
break;
|
|
||||||
_session.write(packet, this, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
//System.err.println(e);
|
|
||||||
}
|
|
||||||
//thread=null;
|
|
||||||
//eof();
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void getData(Buffer buf){
|
|
||||||
setRecipient(buf.getInt());
|
|
||||||
setRemoteWindowSize(buf.getUInt());
|
|
||||||
setRemotePacketSize(buf.getInt());
|
|
||||||
byte[] addr=buf.getString();
|
|
||||||
int port=buf.getInt();
|
|
||||||
byte[] orgaddr=buf.getString();
|
|
||||||
int orgport=buf.getInt();
|
|
||||||
|
|
||||||
/*
|
|
||||||
System.err.println("addr: "+Util.byte2str(addr));
|
|
||||||
System.err.println("port: "+port);
|
|
||||||
System.err.println("orgaddr: "+Util.byte2str(orgaddr));
|
|
||||||
System.err.println("orgport: "+orgport);
|
|
||||||
*/
|
|
||||||
|
|
||||||
Session _session=null;
|
|
||||||
try{
|
|
||||||
_session=getSession();
|
|
||||||
}
|
|
||||||
catch(JSchException e){
|
|
||||||
// session has been already down.
|
|
||||||
}
|
|
||||||
|
|
||||||
this.config = getPort(_session, Util.byte2str(addr), port);
|
|
||||||
if(this.config == null)
|
|
||||||
this.config = getPort(_session, null, port);
|
|
||||||
|
|
||||||
if(this.config == null){
|
|
||||||
if(_session.getLogger().isEnabled(Logger.ERROR)){
|
|
||||||
_session.getLogger().log(Logger.ERROR,
|
|
||||||
"ChannelForwardedTCPIP: "+Util.byte2str(addr)+":"+port+" is not registered.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Config getPort(Session session, String address_to_bind, int rport){
|
|
||||||
synchronized(pool){
|
|
||||||
for(int i=0; i<pool.size(); i++){
|
|
||||||
Config bar = pool.elementAt(i);
|
|
||||||
if(bar.session != session) continue;
|
|
||||||
if(bar.rport != rport) {
|
|
||||||
if(bar.rport != 0 || bar.allocated_rport != rport)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(address_to_bind != null &&
|
|
||||||
!bar.address_to_bind.equals(address_to_bind)) continue;
|
|
||||||
return bar;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String[] getPortForwarding(Session session){
|
|
||||||
Vector<String> foo = new Vector<>();
|
|
||||||
synchronized(pool){
|
|
||||||
for(int i=0; i<pool.size(); i++){
|
|
||||||
Config config = pool.elementAt(i);
|
|
||||||
if(config.session==session){
|
|
||||||
if(config instanceof ConfigDaemon)
|
|
||||||
foo.addElement(config.allocated_rport+":"+config.target+":");
|
|
||||||
else
|
|
||||||
foo.addElement(config.allocated_rport+":"+config.target+":"+((ConfigLHost)config).lport);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String[] bar=new String[foo.size()];
|
|
||||||
for(int i=0; i<foo.size(); i++){
|
|
||||||
bar[i]=foo.elementAt(i);
|
|
||||||
}
|
|
||||||
return bar;
|
|
||||||
}
|
|
||||||
|
|
||||||
static String normalize(String address){
|
|
||||||
if(address==null){ return "localhost"; }
|
|
||||||
else if(address.length()==0 || address.equals("*")){ return ""; }
|
|
||||||
else{ return address; }
|
|
||||||
}
|
|
||||||
|
|
||||||
static void addPort(Session session, String _address_to_bind,
|
|
||||||
int port, int allocated_port, String target, int lport, SocketFactory factory) throws JSchException{
|
|
||||||
String address_to_bind=normalize(_address_to_bind);
|
|
||||||
synchronized(pool){
|
|
||||||
if(getPort(session, address_to_bind, port)!=null){
|
|
||||||
throw new JSchException("PortForwardingR: remote port "+port+" is already registered.");
|
|
||||||
}
|
|
||||||
ConfigLHost config = new ConfigLHost();
|
|
||||||
config.session = session;
|
|
||||||
config.rport = port;
|
|
||||||
config.allocated_rport = allocated_port;
|
|
||||||
config.target = target;
|
|
||||||
config.lport =lport;
|
|
||||||
config.address_to_bind = address_to_bind;
|
|
||||||
config.factory = factory;
|
|
||||||
pool.addElement(config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void addPort(Session session, String _address_to_bind,
|
|
||||||
int port, int allocated_port, String daemon, Object[] arg) throws JSchException{
|
|
||||||
String address_to_bind=normalize(_address_to_bind);
|
|
||||||
synchronized(pool){
|
|
||||||
if(getPort(session, address_to_bind, port)!=null){
|
|
||||||
throw new JSchException("PortForwardingR: remote port "+port+" is already registered.");
|
|
||||||
}
|
|
||||||
ConfigDaemon config = new ConfigDaemon();
|
|
||||||
config.session = session;
|
|
||||||
config.rport = port;
|
|
||||||
config.allocated_rport = port;
|
|
||||||
config.target = daemon;
|
|
||||||
config.arg = arg;
|
|
||||||
config.address_to_bind = address_to_bind;
|
|
||||||
pool.addElement(config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void delPort(ChannelForwardedTCPIP c){
|
|
||||||
Session _session=null;
|
|
||||||
try{
|
|
||||||
_session=c.getSession();
|
|
||||||
}
|
|
||||||
catch(JSchException e){
|
|
||||||
// session has been already down.
|
|
||||||
}
|
|
||||||
if(_session!=null && c.config!=null)
|
|
||||||
delPort(_session, c.config.rport);
|
|
||||||
}
|
|
||||||
static void delPort(Session session, int rport){
|
|
||||||
delPort(session, null, rport);
|
|
||||||
}
|
|
||||||
static void delPort(Session session, String address_to_bind, int rport){
|
|
||||||
synchronized(pool){
|
|
||||||
Config foo = getPort(session, normalize(address_to_bind), rport);
|
|
||||||
if(foo == null)
|
|
||||||
foo = getPort(session, null, rport);
|
|
||||||
if(foo==null) return;
|
|
||||||
pool.removeElement(foo);
|
|
||||||
if(address_to_bind==null){
|
|
||||||
address_to_bind=foo.address_to_bind;
|
|
||||||
}
|
|
||||||
if(address_to_bind==null){
|
|
||||||
address_to_bind="0.0.0.0";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer buf=new Buffer(200); // ??
|
|
||||||
Packet packet=new Packet(buf);
|
|
||||||
|
|
||||||
try{
|
|
||||||
// byte SSH_MSG_GLOBAL_REQUEST 80
|
|
||||||
// string "cancel-tcpip-forward"
|
|
||||||
// boolean want_reply
|
|
||||||
// string address_to_bind (e.g. "127.0.0.1")
|
|
||||||
// uint32 port number to bind
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte) 80/*SSH_MSG_GLOBAL_REQUEST*/);
|
|
||||||
buf.putString(Util.str2byte("cancel-tcpip-forward"));
|
|
||||||
buf.putByte((byte)0);
|
|
||||||
buf.putString(Util.str2byte(address_to_bind));
|
|
||||||
buf.putInt(rport);
|
|
||||||
session.write(packet);
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
// throw new JSchException(e.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void delPort(Session session){
|
|
||||||
int[] rport=null;
|
|
||||||
int count=0;
|
|
||||||
synchronized(pool){
|
|
||||||
rport=new int[pool.size()];
|
|
||||||
for(int i=0; i<pool.size(); i++){
|
|
||||||
Config config = pool.elementAt(i);
|
|
||||||
if(config.session == session) {
|
|
||||||
rport[count++]=config.rport; // ((Integer)bar[1]).intValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(int i=0; i<count; i++){
|
|
||||||
delPort(session, rport[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRemotePort(){return (config!=null ? config.rport: 0);}
|
|
||||||
private void setSocketFactory(SocketFactory factory){
|
|
||||||
if(config!=null && (config instanceof ConfigLHost) )
|
|
||||||
((ConfigLHost)config).factory = factory;
|
|
||||||
}
|
|
||||||
static abstract class Config {
|
|
||||||
Session session;
|
|
||||||
int rport;
|
|
||||||
int allocated_rport;
|
|
||||||
String address_to_bind;
|
|
||||||
String target;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ConfigDaemon extends Config {
|
|
||||||
Object[] arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ConfigLHost extends Config {
|
|
||||||
int lport;
|
|
||||||
SocketFactory factory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,279 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
class ChannelSession extends Channel{
|
|
||||||
private static byte[] _session=Util.str2byte("session");
|
|
||||||
|
|
||||||
protected boolean agent_forwarding=false;
|
|
||||||
protected boolean xforwading=false;
|
|
||||||
protected Hashtable<byte[], byte[]> env=null;
|
|
||||||
|
|
||||||
protected boolean pty=false;
|
|
||||||
|
|
||||||
protected String ttype="vt100";
|
|
||||||
protected int tcol=80;
|
|
||||||
protected int trow=24;
|
|
||||||
protected int twp=640;
|
|
||||||
protected int thp=480;
|
|
||||||
protected byte[] terminal_mode=null;
|
|
||||||
|
|
||||||
ChannelSession(){
|
|
||||||
super();
|
|
||||||
type=_session;
|
|
||||||
io=new IO();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable the agent forwarding.
|
|
||||||
*
|
|
||||||
* @param enable
|
|
||||||
*/
|
|
||||||
public void setAgentForwarding(boolean enable){
|
|
||||||
agent_forwarding=enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable the X11 forwarding.
|
|
||||||
* Refer to RFC4254 6.3.1. Requesting X11 Forwarding.
|
|
||||||
*
|
|
||||||
* @param enable
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setXForwarding(boolean enable){
|
|
||||||
xforwading=enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use #setEnv(String, String) or #setEnv(byte[], byte[]) instead.
|
|
||||||
* @see #setEnv(String, String)
|
|
||||||
* @see #setEnv(byte[], byte[])
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setEnv(Hashtable<byte[], byte[]> env){
|
|
||||||
synchronized(this){
|
|
||||||
this.env=env;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the environment variable.
|
|
||||||
* If <code>name</code> and <code>value</code> are needed to be passed
|
|
||||||
* to the remote in your favorite encoding,
|
|
||||||
* use {@link #setEnv(byte[], byte[])}.
|
|
||||||
* Refer to RFC4254 6.4 Environment Variable Passing.
|
|
||||||
*
|
|
||||||
* @param name A name for environment variable.
|
|
||||||
* @param value A value for environment variable.
|
|
||||||
*/
|
|
||||||
public void setEnv(String name, String value){
|
|
||||||
setEnv(Util.str2byte(name), Util.str2byte(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the environment variable.
|
|
||||||
* Refer to RFC4254 6.4 Environment Variable Passing.
|
|
||||||
*
|
|
||||||
* @param name A name of environment variable.
|
|
||||||
* @param value A value of environment variable.
|
|
||||||
* @see #setEnv(String, String)
|
|
||||||
*/
|
|
||||||
public void setEnv(byte[] name, byte[] value){
|
|
||||||
synchronized(this){
|
|
||||||
getEnv().put(name, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Hashtable<byte[], byte[]> getEnv(){
|
|
||||||
if(env==null)
|
|
||||||
env=new Hashtable<>();
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allocate a Pseudo-Terminal.
|
|
||||||
* Refer to RFC4254 6.2. Requesting a Pseudo-Terminal.
|
|
||||||
*
|
|
||||||
* @param enable
|
|
||||||
*/
|
|
||||||
public void setPty(boolean enable){
|
|
||||||
pty=enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the terminal mode.
|
|
||||||
*
|
|
||||||
* @param terminal_mode
|
|
||||||
*/
|
|
||||||
public void setTerminalMode(byte[] terminal_mode){
|
|
||||||
this.terminal_mode=terminal_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the window dimension interactively.
|
|
||||||
* Refer to RFC4254 6.7. Window Dimension Change Message.
|
|
||||||
*
|
|
||||||
* @param col terminal width, columns
|
|
||||||
* @param row terminal height, rows
|
|
||||||
* @param wp terminal width, pixels
|
|
||||||
* @param hp terminal height, pixels
|
|
||||||
*/
|
|
||||||
public void setPtySize(int col, int row, int wp, int hp){
|
|
||||||
setPtyType(this.ttype, col, row, wp, hp);
|
|
||||||
if(!pty || !isConnected()){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try{
|
|
||||||
RequestWindowChange request=new RequestWindowChange();
|
|
||||||
request.setSize(col, row, wp, hp);
|
|
||||||
request.request(getSession(), this);
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
//System.err.println("ChannelSessio.setPtySize: "+e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the terminal type.
|
|
||||||
* This method is not effective after Channel#connect().
|
|
||||||
*
|
|
||||||
* @param ttype terminal type(for example, "vt100")
|
|
||||||
* @see #setPtyType(String, int, int, int, int)
|
|
||||||
*/
|
|
||||||
public void setPtyType(String ttype){
|
|
||||||
setPtyType(ttype, 80, 24, 640, 480);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the terminal type.
|
|
||||||
* This method is not effective after Channel#connect().
|
|
||||||
*
|
|
||||||
* @param ttype terminal type(for example, "vt100")
|
|
||||||
* @param col terminal width, columns
|
|
||||||
* @param row terminal height, rows
|
|
||||||
* @param wp terminal width, pixels
|
|
||||||
* @param hp terminal height, pixels
|
|
||||||
*/
|
|
||||||
public void setPtyType(String ttype, int col, int row, int wp, int hp){
|
|
||||||
this.ttype=ttype;
|
|
||||||
this.tcol=col;
|
|
||||||
this.trow=row;
|
|
||||||
this.twp=wp;
|
|
||||||
this.thp=hp;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void sendRequests() throws Exception{
|
|
||||||
Session _session=getSession();
|
|
||||||
Request request;
|
|
||||||
if(agent_forwarding){
|
|
||||||
request=new RequestAgentForwarding();
|
|
||||||
request.request(_session, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(xforwading){
|
|
||||||
request=new RequestX11();
|
|
||||||
request.request(_session, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(pty){
|
|
||||||
request=new RequestPtyReq();
|
|
||||||
((RequestPtyReq)request).setTType(ttype);
|
|
||||||
((RequestPtyReq)request).setTSize(tcol, trow, twp, thp);
|
|
||||||
if(terminal_mode!=null){
|
|
||||||
((RequestPtyReq)request).setTerminalMode(terminal_mode);
|
|
||||||
}
|
|
||||||
request.request(_session, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(env!=null){
|
|
||||||
for(Enumeration<byte[]> _env=env.keys(); _env.hasMoreElements();){
|
|
||||||
byte[] name=_env.nextElement();
|
|
||||||
byte[] value=env.get(name);
|
|
||||||
request=new RequestEnv();
|
|
||||||
((RequestEnv)request).setEnv(toByteArray(name),
|
|
||||||
toByteArray(value));
|
|
||||||
request.request(_session, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] toByteArray(Object o){
|
|
||||||
if(o instanceof String){
|
|
||||||
return Util.str2byte((String)o);
|
|
||||||
}
|
|
||||||
return (byte[])o;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void run(){
|
|
||||||
//System.err.println(this+":run >");
|
|
||||||
|
|
||||||
Buffer buf=new Buffer(rmpsize);
|
|
||||||
Packet packet=new Packet(buf);
|
|
||||||
int i=-1;
|
|
||||||
try{
|
|
||||||
while(isConnected() &&
|
|
||||||
thread!=null &&
|
|
||||||
io!=null &&
|
|
||||||
io.in!=null){
|
|
||||||
i=io.in.read(buf.buffer,
|
|
||||||
14,
|
|
||||||
buf.buffer.length-14
|
|
||||||
-Session.buffer_margin
|
|
||||||
);
|
|
||||||
if(i==0)continue;
|
|
||||||
if(i==-1){
|
|
||||||
eof();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(close)break;
|
|
||||||
//System.out.println("write: "+i);
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
|
|
||||||
buf.putInt(recipient);
|
|
||||||
buf.putInt(i);
|
|
||||||
buf.skip(i);
|
|
||||||
getSession().write(packet, this, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
//System.err.println("# ChannelExec.run");
|
|
||||||
//e.printStackTrace();
|
|
||||||
}
|
|
||||||
Thread _thread=thread;
|
|
||||||
if(_thread!=null){
|
|
||||||
synchronized(_thread){ _thread.notifyAll(); }
|
|
||||||
}
|
|
||||||
thread=null;
|
|
||||||
//System.err.println(this+":run <");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,70 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class ChannelShell extends ChannelSession{
|
|
||||||
|
|
||||||
ChannelShell(){
|
|
||||||
super();
|
|
||||||
pty=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() throws JSchException{
|
|
||||||
Session _session=getSession();
|
|
||||||
try{
|
|
||||||
sendRequests();
|
|
||||||
|
|
||||||
Request request=new RequestShell();
|
|
||||||
request.request(_session, this);
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
if(e instanceof JSchException) throw (JSchException)e;
|
|
||||||
throw new JSchException("ChannelShell", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(io.in!=null){
|
|
||||||
thread=new Thread(this::run);
|
|
||||||
thread.setName("Shell for "+_session.host);
|
|
||||||
if(_session.daemon_thread){
|
|
||||||
thread.setDaemon(_session.daemon_thread);
|
|
||||||
}
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void init() throws JSchException {
|
|
||||||
io.setInputStream(getSession().in);
|
|
||||||
io.setOutputStream(getSession().out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
public class ChannelSubsystem extends ChannelSession{
|
|
||||||
boolean want_reply=true;
|
|
||||||
String subsystem="";
|
|
||||||
public void setWantReply(boolean foo){ want_reply=foo; }
|
|
||||||
public void setSubsystem(String foo){ subsystem=foo; }
|
|
||||||
@Override
|
|
||||||
public void start() throws JSchException{
|
|
||||||
Session _session=getSession();
|
|
||||||
try{
|
|
||||||
Request request;
|
|
||||||
if(xforwading){
|
|
||||||
request=new RequestX11();
|
|
||||||
request.request(_session, this);
|
|
||||||
}
|
|
||||||
if(pty){
|
|
||||||
request=new RequestPtyReq();
|
|
||||||
request.request(_session, this);
|
|
||||||
}
|
|
||||||
request=new RequestSubsystem();
|
|
||||||
((RequestSubsystem)request).request(_session, this, subsystem, want_reply);
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
if(e instanceof JSchException){ throw (JSchException)e; }
|
|
||||||
throw new JSchException("ChannelSubsystem", e);
|
|
||||||
}
|
|
||||||
if(io.in!=null){
|
|
||||||
thread=new Thread(this::run);
|
|
||||||
thread.setName("Subsystem for "+_session.host);
|
|
||||||
if(_session.daemon_thread){
|
|
||||||
thread.setDaemon(_session.daemon_thread);
|
|
||||||
}
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void init() throws JSchException {
|
|
||||||
io.setInputStream(getSession().in);
|
|
||||||
io.setOutputStream(getSession().out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setErrStream(OutputStream out){
|
|
||||||
setExtOutputStream(out);
|
|
||||||
}
|
|
||||||
public InputStream getErrStream() throws IOException {
|
|
||||||
return getExtInputStream();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,277 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.*;
|
|
||||||
import java.util.Hashtable;
|
|
||||||
|
|
||||||
class ChannelX11 extends Channel{
|
|
||||||
|
|
||||||
static private final int LOCAL_WINDOW_SIZE_MAX=0x20000;
|
|
||||||
static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000;
|
|
||||||
|
|
||||||
static private final int TIMEOUT=10*1000;
|
|
||||||
|
|
||||||
private static String host="127.0.0.1";
|
|
||||||
private static int port=6000;
|
|
||||||
|
|
||||||
private boolean init=true;
|
|
||||||
|
|
||||||
static byte[] cookie=null;
|
|
||||||
private static byte[] cookie_hex=null;
|
|
||||||
|
|
||||||
private static Hashtable<Session, byte[]> faked_cookie_pool=new Hashtable<>();
|
|
||||||
private static Hashtable<Session, byte[]> faked_cookie_hex_pool=new Hashtable<>();
|
|
||||||
|
|
||||||
private static byte[] table={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,
|
|
||||||
0x61,0x62,0x63,0x64,0x65,0x66};
|
|
||||||
|
|
||||||
private Socket socket = null;
|
|
||||||
|
|
||||||
static int revtable(byte foo){
|
|
||||||
for(int i=0; i<table.length; i++){
|
|
||||||
if(table[i]==foo)return i;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static void setCookie(String foo){
|
|
||||||
cookie_hex=Util.str2byte(foo);
|
|
||||||
cookie=new byte[16];
|
|
||||||
for(int i=0; i<16; i++){
|
|
||||||
cookie[i]=(byte)(((revtable(cookie_hex[i*2])<<4)&0xf0) |
|
|
||||||
((revtable(cookie_hex[i*2+1]))&0xf));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void setHost(String foo){ host=foo; }
|
|
||||||
static void setPort(int foo){ port=foo; }
|
|
||||||
static byte[] getFakedCookie(Session session){
|
|
||||||
synchronized(faked_cookie_hex_pool){
|
|
||||||
byte[] foo=faked_cookie_hex_pool.get(session);
|
|
||||||
if(foo==null){
|
|
||||||
Random random=Session.random;
|
|
||||||
foo=new byte[16];
|
|
||||||
synchronized(random){
|
|
||||||
random.fill(foo, 0, 16);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
System.err.print("faked_cookie: ");
|
|
||||||
for(int i=0; i<foo.length; i++){
|
|
||||||
System.err.print(Integer.toHexString(foo[i]&0xff)+":");
|
|
||||||
}
|
|
||||||
System.err.println("");
|
|
||||||
*/
|
|
||||||
faked_cookie_pool.put(session, foo);
|
|
||||||
byte[] bar=new byte[32];
|
|
||||||
for(int i=0; i<16; i++){
|
|
||||||
bar[2*i]=table[(foo[i]>>>4)&0xf];
|
|
||||||
bar[2*i+1]=table[(foo[i])&0xf];
|
|
||||||
}
|
|
||||||
faked_cookie_hex_pool.put(session, bar);
|
|
||||||
foo=bar;
|
|
||||||
}
|
|
||||||
return foo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void removeFakedCookie(Session session){
|
|
||||||
synchronized(faked_cookie_hex_pool){
|
|
||||||
faked_cookie_hex_pool.remove(session);
|
|
||||||
faked_cookie_pool.remove(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelX11(){
|
|
||||||
super();
|
|
||||||
|
|
||||||
setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
|
|
||||||
setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
|
|
||||||
setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
|
|
||||||
|
|
||||||
type=Util.str2byte("x11");
|
|
||||||
|
|
||||||
connected=true;
|
|
||||||
/*
|
|
||||||
try{
|
|
||||||
socket=Util.createSocket(host, port, TIMEOUT);
|
|
||||||
socket.setTcpNoDelay(true);
|
|
||||||
io=new IO();
|
|
||||||
io.setInputStream(socket.getInputStream());
|
|
||||||
io.setOutputStream(socket.getOutputStream());
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
//System.err.println(e);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void run(){
|
|
||||||
|
|
||||||
try{
|
|
||||||
socket=Util.createSocket(host, port, TIMEOUT);
|
|
||||||
socket.setTcpNoDelay(true);
|
|
||||||
io=new IO();
|
|
||||||
io.setInputStream(socket.getInputStream());
|
|
||||||
io.setOutputStream(socket.getOutputStream());
|
|
||||||
sendOpenConfirmation();
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
sendOpenFailure(SSH_OPEN_ADMINISTRATIVELY_PROHIBITED);
|
|
||||||
close=true;
|
|
||||||
disconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread=Thread.currentThread();
|
|
||||||
Buffer buf=new Buffer(rmpsize);
|
|
||||||
Packet packet=new Packet(buf);
|
|
||||||
int i=0;
|
|
||||||
try{
|
|
||||||
while(thread!=null &&
|
|
||||||
io!=null &&
|
|
||||||
io.in!=null){
|
|
||||||
i=io.in.read(buf.buffer,
|
|
||||||
14,
|
|
||||||
buf.buffer.length-14-Session.buffer_margin);
|
|
||||||
if(i<=0){
|
|
||||||
eof();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(close)break;
|
|
||||||
packet.reset();
|
|
||||||
buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
|
|
||||||
buf.putInt(recipient);
|
|
||||||
buf.putInt(i);
|
|
||||||
buf.skip(i);
|
|
||||||
getSession().write(packet, this, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e){
|
|
||||||
//System.err.println(e);
|
|
||||||
}
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] cache=new byte[0];
|
|
||||||
private byte[] addCache(byte[] foo, int s, int l){
|
|
||||||
byte[] bar=new byte[cache.length+l];
|
|
||||||
System.arraycopy(foo, s, bar, cache.length, l);
|
|
||||||
if(cache.length>0)
|
|
||||||
System.arraycopy(cache, 0, bar, 0, cache.length);
|
|
||||||
cache=bar;
|
|
||||||
return cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void write(byte[] foo, int s, int l) throws IOException {
|
|
||||||
//if(eof_local)return;
|
|
||||||
|
|
||||||
if(init){
|
|
||||||
|
|
||||||
Session _session=null;
|
|
||||||
try{
|
|
||||||
_session=getSession();
|
|
||||||
}
|
|
||||||
catch(JSchException e){
|
|
||||||
throw new IOException(e.toString(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
foo=addCache(foo, s, l);
|
|
||||||
s=0;
|
|
||||||
l=foo.length;
|
|
||||||
|
|
||||||
if(l<9)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int plen=(foo[s+6]&0xff)*256+(foo[s+7]&0xff);
|
|
||||||
int dlen=(foo[s+8]&0xff)*256+(foo[s+9]&0xff);
|
|
||||||
|
|
||||||
if((foo[s]&0xff)==0x42){
|
|
||||||
}
|
|
||||||
else if((foo[s]&0xff)==0x6c){
|
|
||||||
plen=((plen>>>8)&0xff)|((plen<<8)&0xff00);
|
|
||||||
dlen=((dlen>>>8)&0xff)|((dlen<<8)&0xff00);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
// ??
|
|
||||||
}
|
|
||||||
|
|
||||||
if(l<12+plen+((-plen)&3)+dlen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
byte[] bar=new byte[dlen];
|
|
||||||
System.arraycopy(foo, s+12+plen+((-plen)&3), bar, 0, dlen);
|
|
||||||
byte[] faked_cookie=null;
|
|
||||||
|
|
||||||
synchronized(faked_cookie_pool){
|
|
||||||
faked_cookie=faked_cookie_pool.get(_session);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
System.err.print("faked_cookie: ");
|
|
||||||
for(int i=0; i<faked_cookie.length; i++){
|
|
||||||
System.err.print(Integer.toHexString(faked_cookie[i]&0xff)+":");
|
|
||||||
}
|
|
||||||
System.err.println("");
|
|
||||||
System.err.print("bar: ");
|
|
||||||
for(int i=0; i<bar.length; i++){
|
|
||||||
System.err.print(Integer.toHexString(bar[i]&0xff)+":");
|
|
||||||
}
|
|
||||||
System.err.println("");
|
|
||||||
*/
|
|
||||||
|
|
||||||
if(equals(bar, faked_cookie)){
|
|
||||||
if(cookie!=null)
|
|
||||||
System.arraycopy(cookie, 0, foo, s+12+plen+((-plen)&3), dlen);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
//System.err.println("wrong cookie");
|
|
||||||
thread=null;
|
|
||||||
eof();
|
|
||||||
io.close();
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
init=false;
|
|
||||||
io.put(foo, s, l);
|
|
||||||
cache=null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
io.put(foo, s, l);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean equals(byte[] foo, byte[] bar){
|
|
||||||
if(foo.length!=bar.length)return false;
|
|
||||||
for(int i=0; i<foo.length; i++){
|
|
||||||
if(foo[i]!=bar[i])return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
public interface Cipher{
|
|
||||||
static int ENCRYPT_MODE=0;
|
|
||||||
static int DECRYPT_MODE=1;
|
|
||||||
int getIVSize();
|
|
||||||
int getBlockSize();
|
|
||||||
default int getTagSize() {return 0;}
|
|
||||||
void init(int mode, byte[] key, byte[] iv) throws Exception;
|
|
||||||
default void update(int foo) throws Exception {}
|
|
||||||
void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception;
|
|
||||||
default void updateAAD(byte[] foo, int s1, int len) throws Exception {}
|
|
||||||
default void doFinal(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception {}
|
|
||||||
boolean isCBC();
|
|
||||||
default boolean isAEAD() {return false;}
|
|
||||||
default boolean isChaCha20() {return false;}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
class CipherNone implements Cipher{
|
|
||||||
private static final int ivsize=8;
|
|
||||||
private static final int bsize=16;
|
|
||||||
@Override
|
|
||||||
public int getIVSize(){return ivsize;}
|
|
||||||
@Override
|
|
||||||
public int getBlockSize(){return bsize;}
|
|
||||||
@Override
|
|
||||||
public void init(int mode, byte[] key, byte[] iv) throws Exception{
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public boolean isCBC(){return false; }
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
public interface Compression{
|
|
||||||
static public final int INFLATER=0;
|
|
||||||
static public final int DEFLATER=1;
|
|
||||||
|
|
||||||
default void init(int type, int level, Session session) {
|
|
||||||
init(type, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void end() {}
|
|
||||||
|
|
||||||
void init(int type, int level);
|
|
||||||
byte[] compress(byte[] buf, int start, int[] len);
|
|
||||||
byte[] uncompress(byte[] buf, int start, int[] len);
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
public interface ConfigRepository {
|
|
||||||
|
|
||||||
public Config getConfig(String host);
|
|
||||||
|
|
||||||
public interface Config {
|
|
||||||
public String getHostname();
|
|
||||||
public String getUser();
|
|
||||||
public int getPort();
|
|
||||||
public String getValue(String key);
|
|
||||||
public String[] getValues(String key);
|
|
||||||
}
|
|
||||||
|
|
||||||
static final Config defaultConfig = new Config() {
|
|
||||||
@Override
|
|
||||||
public String getHostname() {return null;}
|
|
||||||
@Override
|
|
||||||
public String getUser() {return null;}
|
|
||||||
@Override
|
|
||||||
public int getPort() {return -1;}
|
|
||||||
@Override
|
|
||||||
public String getValue(String key) {return null;}
|
|
||||||
@Override
|
|
||||||
public String[] getValues(String key) {return null;}
|
|
||||||
};
|
|
||||||
|
|
||||||
static final ConfigRepository nullConfig = new ConfigRepository(){
|
|
||||||
@Override
|
|
||||||
public Config getConfig(String host) { return defaultConfig; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
|
||||||
/*
|
|
||||||
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
||||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
||||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jcraft.jsch;
|
|
||||||
|
|
||||||
public interface DH{
|
|
||||||
void init() throws Exception;
|
|
||||||
void setP(byte[] p);
|
|
||||||
void setG(byte[] g);
|
|
||||||
byte[] getE() throws Exception;
|
|
||||||
void setF(byte[] f);
|
|
||||||
byte[] getK() throws Exception;
|
|
||||||
|
|
||||||
// checkRange() will check if e and f are in [1,p-1]
|
|
||||||
// as defined at https://tools.ietf.org/html/rfc4253#section-8
|
|
||||||
void checkRange() throws Exception;
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user