Compare commits

...

156 Commits

Author SHA1 Message Date
Philipp Crocoll
30d45e086c refactor SftpFileStorage creation 2023-11-13 09:18:39 +01:00
Rick Brown
c19b8d2238 Fix nonet compilation 2023-11-06 18:09:19 -05:00
Rick Brown
141d2f3ddb Persist FTP/SFTP debug log preference
-Commit/apply FtpDebug_key state to persistent preferences
-Configure SFTP/JSch logging based on FtpDebug_key/LogFilename
 state on app startup
2023-11-06 16:55:36 -05:00
PhilippC
4dbd33ba97 Merge pull request #2455 from PhilippC/l10n_master2
New Crowdin updates
2023-11-06 10:34:07 +01:00
PhilippC
f8f18152c3 Merge pull request #2435 from hyproman/upgrade-fluentftp
Update to latest FluentFTP version
2023-11-06 10:33:41 +01:00
PhilippC
dbf5e46e94 New translations strings.xml (Croatian) 2023-11-06 09:24:19 +01:00
PhilippC
a23c1a2360 New translations strings.xml (Persian) 2023-11-06 09:24:18 +01:00
PhilippC
d2bd91ba6a New translations strings.xml (Portuguese, Brazilian) 2023-11-06 09:24:16 +01:00
PhilippC
95352ef0ee New translations strings.xml (Chinese Traditional) 2023-11-06 09:24:14 +01:00
PhilippC
24b8c27d26 New translations strings.xml (Chinese Simplified) 2023-11-06 09:24:13 +01:00
PhilippC
191b90d974 New translations strings.xml (Ukrainian) 2023-11-06 09:24:12 +01:00
PhilippC
21db4b612b New translations strings.xml (Turkish) 2023-11-06 09:24:10 +01:00
PhilippC
a23101b812 New translations strings.xml (Swedish) 2023-11-06 09:24:09 +01:00
PhilippC
8042470488 New translations strings.xml (Slovenian) 2023-11-06 09:24:07 +01:00
PhilippC
4bbec4367f New translations strings.xml (Slovak) 2023-11-06 09:24:06 +01:00
PhilippC
4b583cc0c0 New translations strings.xml (Russian) 2023-11-06 09:24:05 +01:00
PhilippC
cd07de56df New translations strings.xml (Portuguese) 2023-11-06 09:24:04 +01:00
PhilippC
962c4dbf63 New translations strings.xml (Polish) 2023-11-06 09:24:03 +01:00
PhilippC
09b56d85cf New translations strings.xml (Dutch) 2023-11-06 09:24:01 +01:00
PhilippC
8281888608 New translations strings.xml (Korean) 2023-11-06 09:24:00 +01:00
PhilippC
44d9456e20 New translations strings.xml (Japanese) 2023-11-06 09:23:59 +01:00
PhilippC
7b01e4494f New translations strings.xml (Italian) 2023-11-06 09:23:58 +01:00
PhilippC
fda68a1114 New translations strings.xml (Hungarian) 2023-11-06 09:23:57 +01:00
PhilippC
4849c089b3 New translations strings.xml (Finnish) 2023-11-06 09:23:55 +01:00
PhilippC
580668c5cb New translations strings.xml (Basque) 2023-11-06 09:23:54 +01:00
PhilippC
da8f1122e8 New translations strings.xml (Greek) 2023-11-06 09:23:53 +01:00
PhilippC
7d44518ac7 New translations strings.xml (German) 2023-11-06 09:23:52 +01:00
PhilippC
1a2c1267c4 New translations strings.xml (Danish) 2023-11-06 09:23:50 +01:00
PhilippC
85e0fe487f New translations strings.xml (Czech) 2023-11-06 09:23:49 +01:00
PhilippC
fa9a9f2602 New translations strings.xml (Catalan) 2023-11-06 09:23:48 +01:00
PhilippC
5c7d626f4b New translations strings.xml (Belarusian) 2023-11-06 09:23:46 +01:00
PhilippC
b3ce9c64b1 New translations strings.xml (Arabic) 2023-11-06 09:23:45 +01:00
PhilippC
dc809941e8 New translations strings.xml (Spanish) 2023-11-06 09:23:43 +01:00
PhilippC
b7d69c33c8 New translations strings.xml (French) 2023-11-06 09:23:42 +01:00
PhilippC
de765f3451 New translations strings.xml (Romanian) 2023-11-06 09:23:41 +01:00
Philipp Crocoll
1581d79666 Merge branch 'master' of https://github.com/PhilippC/keepass2android into iansw246/master 2023-11-06 09:02:01 +01:00
Philipp Crocoll
297fa267e5 fix bug in shared preference handling
fix issue with receiving meta data: previous implementation was repeatedly listing the full contents of pCloud recursively which is slow and might not work for large contents (https://github.com/pCloud/pcloud-sdk-java/issues/42)
2023-11-06 09:01:13 +01:00
PhilippC
77e2d67b6c Merge pull request #2299 from PhilippC/l10n_master2
New Crowdin updates
2023-11-04 18:23:55 +01:00
PhilippC
a3ba2d8367 Merge pull request #2344 from iansw246/master
Hide progress dialogs when user input dialog is showing
2023-11-04 18:23:44 +01:00
PhilippC
48d59aa0f6 New translations strings.xml (Spanish) 2023-11-02 21:37:26 +01:00
PhilippC
cff6595b79 New translations strings.xml (Spanish) 2023-11-02 20:29:56 +01:00
Rick Brown
798f633af7 Merge branch 'master' into upgrade-fluentftp 2023-11-02 15:19:28 -04:00
Rick Brown
f5681c4e62 Integrate FTP debug logging into UI toggle 2023-11-02 15:11:54 -04:00
PhilippC
690de2761c New translations strings.xml (Slovenian) 2023-11-02 18:29:48 +01:00
PhilippC
92238436d5 New translations strings.xml (Slovak) 2023-11-02 17:10:41 +01:00
PhilippC
9282e80938 New translations strings.xml (French) 2023-11-02 17:10:40 +01:00
PhilippC
588e203442 New translations strings.xml (Czech) 2023-11-02 15:24:59 +01:00
PhilippC
7e96055e0b New translations strings.xml (Portuguese, Brazilian) 2023-11-02 12:19:16 +01:00
Philipp Crocoll
c90d623d15 Merge branch 'bug-2378-pcloud-sdk-upgrade' into iansw246/master 2023-11-01 18:06:16 +01:00
PhilippC
86a03d8b9a New translations strings.xml (Czech) 2023-11-01 16:18:25 +01:00
Philipp Crocoll
17f7d1b8eb Merge branch 'target-sdk33' into iansw246/master 2023-11-01 12:24:34 +01:00
Philipp Crocoll
d3fecaf4e3 Merge branch 'master' into iansw246/master 2023-11-01 12:09:04 +01:00
PhilippC
03dee4f262 New translations strings.xml (Chinese Simplified) 2023-11-01 02:26:14 +01:00
PhilippC
2b502df566 Merge pull request #2434 from hyproman/bug-2423-ftp-contents-invisible
Bugfix for issue #2423 - FTP contents invisible
2023-10-31 09:14:42 +01:00
PhilippC
9ea064108c Merge pull request #2451 from PhilippC/target-sdk33
Update to target sdk33
2023-10-31 09:13:18 +01:00
Philipp Crocoll
682736d119 implement info text for notifications permission in GroupBaseActivity 2023-10-31 07:27:27 +01:00
Philipp Crocoll
150bd336d8 update to targetSdkVersion=33 again, this time with handling of notification permissions (https://developer.android.com/develop/ui/views/notifications/notification-permission)
For now, this is only implemented for entry activity, unlocked/QuickUnlock notifications not tested/implemented yet.
2023-10-31 06:46:13 +01:00
PhilippC
d13ee3d2ca New translations strings.xml (Slovenian) 2023-10-26 18:51:08 +02:00
PhilippC
99b5df4c94 New translations strings.xml (Portuguese, Brazilian) 2023-10-24 11:30:43 +02:00
PhilippC
9a70442d69 New translations strings.xml (Chinese Simplified) 2023-10-24 02:23:38 +02:00
Rick Brown
cd04050e57 Merge branch 'master' into upgrade-fluentftp 2023-10-23 17:54:59 -04:00
PhilippC
c13bb15fc0 New translations strings.xml (Norwegian Bokmal) 2023-10-23 11:20:35 +02:00
PhilippC
84939c70e1 New translations strings.xml (Malayalam) 2023-10-23 11:20:33 +02:00
PhilippC
2b108d9818 New translations strings.xml (Azerbaijani) 2023-10-23 11:20:31 +02:00
PhilippC
06f338fdd5 New translations strings.xml (Norwegian Nynorsk) 2023-10-23 11:20:30 +02:00
PhilippC
fa5e8c1656 New translations strings.xml (Croatian) 2023-10-23 11:20:29 +02:00
PhilippC
3652e2ee25 New translations strings.xml (Persian) 2023-10-23 11:20:28 +02:00
PhilippC
e8f3eb1bc8 New translations strings.xml (Indonesian) 2023-10-23 11:20:27 +02:00
PhilippC
233f612479 New translations strings.xml (Portuguese, Brazilian) 2023-10-23 11:20:25 +02:00
PhilippC
dc1e790ab5 New translations strings.xml (Galician) 2023-10-23 11:20:24 +02:00
PhilippC
bab77538c9 New translations strings.xml (Vietnamese) 2023-10-23 11:20:22 +02:00
PhilippC
09165af0a8 New translations strings.xml (Chinese Traditional) 2023-10-23 11:20:21 +02:00
PhilippC
4502d3d2bf New translations strings.xml (Chinese Simplified) 2023-10-23 11:20:20 +02:00
PhilippC
eb03d448d8 New translations strings.xml (Ukrainian) 2023-10-23 11:20:19 +02:00
PhilippC
7798ec8454 New translations strings.xml (Turkish) 2023-10-23 11:20:17 +02:00
PhilippC
7424bb324f New translations strings.xml (Swedish) 2023-10-23 11:20:16 +02:00
PhilippC
b18432add6 New translations strings.xml (Serbian (Cyrillic)) 2023-10-23 11:20:15 +02:00
PhilippC
e9a66d688c New translations strings.xml (Slovenian) 2023-10-23 11:20:14 +02:00
PhilippC
d9add0d5f6 New translations strings.xml (Slovak) 2023-10-23 11:20:12 +02:00
PhilippC
aed00420fc New translations strings.xml (Russian) 2023-10-23 11:20:11 +02:00
PhilippC
8dc546e640 New translations strings.xml (Portuguese) 2023-10-23 11:20:09 +02:00
PhilippC
c8f3d5f3e2 New translations strings.xml (Polish) 2023-10-23 11:20:08 +02:00
PhilippC
1f3786189b New translations strings.xml (Dutch) 2023-10-23 11:20:07 +02:00
PhilippC
d7bdde0585 New translations strings.xml (Korean) 2023-10-23 11:20:05 +02:00
PhilippC
f213f05477 New translations strings.xml (Japanese) 2023-10-23 11:20:04 +02:00
PhilippC
cb73144da7 New translations strings.xml (Italian) 2023-10-23 11:20:02 +02:00
PhilippC
689a1710c4 New translations strings.xml (Hungarian) 2023-10-23 11:20:01 +02:00
PhilippC
da116bbb4d New translations strings.xml (Hebrew) 2023-10-23 11:20:00 +02:00
PhilippC
2fd76ad28f New translations strings.xml (Finnish) 2023-10-23 11:19:58 +02:00
PhilippC
bdc7bf9cf6 New translations strings.xml (Basque) 2023-10-23 11:19:57 +02:00
PhilippC
b3ef4f817a New translations strings.xml (Greek) 2023-10-23 11:19:56 +02:00
PhilippC
3fb2f2e858 New translations strings.xml (German) 2023-10-23 11:19:54 +02:00
PhilippC
d8f60aa7f1 New translations strings.xml (Danish) 2023-10-23 11:19:53 +02:00
PhilippC
31f3a30a54 New translations strings.xml (Czech) 2023-10-23 11:19:52 +02:00
PhilippC
474b90f331 New translations strings.xml (Catalan) 2023-10-23 11:19:51 +02:00
PhilippC
fa0a52b328 New translations strings.xml (Bulgarian) 2023-10-23 11:19:50 +02:00
PhilippC
ccb6ece463 New translations strings.xml (Belarusian) 2023-10-23 11:19:48 +02:00
PhilippC
ffa33ed190 New translations strings.xml (Arabic) 2023-10-23 11:19:47 +02:00
PhilippC
c4923c57bf New translations strings.xml (Spanish) 2023-10-23 11:19:46 +02:00
PhilippC
2602bf7bee New translations strings.xml (French) 2023-10-23 11:19:45 +02:00
PhilippC
7df86fd134 New translations strings.xml (Romanian) 2023-10-23 11:19:43 +02:00
PhilippC
cf2f57b372 Merge pull request #2345 from AlexCherrypi/patch-1
remember keyprovider for "Password + Key file + Challenge-Response for KeePass XC"
2023-10-23 11:18:12 +02:00
Philipp Crocoll
7c2500af63 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2023-10-23 11:14:28 +02:00
PhilippC
748a71bc03 Merge pull request #2386 from hyproman/bug-2366-WIP-ssh-custom-alg-cfg
Bug 2366 SSH Custom Algorithms Configuration
2023-10-23 11:03:02 +02:00
Rick Brown
c8abb4d76a Update to latest FluentFTP version 2023-10-08 18:23:11 -04:00
PhilippC
18f81e6927 New translations strings.xml (Chinese Traditional) 2023-10-07 23:45:25 +02:00
PhilippC
b8c094554a New translations strings.xml (Chinese Simplified) 2023-10-07 23:45:22 +02:00
PhilippC
1c6831bb78 New translations strings.xml (Greek) 2023-10-07 23:44:46 +02:00
PhilippC
a5e7bbc081 New translations strings.xml (Danish) 2023-10-07 23:44:42 +02:00
Rick Brown
be2218afcc Bugfix for issue #2423 - FTP contents invisible
Change working directory to target path and call GetListing on
current directory instead of calling GetListing on explicit path.

For some reason GetListing(path) does not always return the contents of
the directory. However, calling SetWorkingDirectory(path) followed by
GetListing(null, options) to list the contents of the working directory
does consistently work.

Similar behavior was confirmed using ncftp client. I suspect this is a
strange bug/nuance in the server's implementation of the LIST command?
2023-10-07 17:30:32 -04:00
PhilippC
32c1d2a379 New translations strings.xml (Finnish) 2023-09-15 21:45:50 +02:00
PhilippC
9c7182f85a New translations strings.xml (German) 2023-08-27 16:49:51 +02:00
PhilippC
31abf68031 New translations strings.xml (German) 2023-08-27 15:46:35 +02:00
Rick Brown
489ed8e2b4 Convert literals to constants, add javadoc to resolver class 2023-08-09 20:58:06 -04:00
Rick Brown
d63e11b307 Add SFTP credentials documentation 2023-08-09 17:38:37 -04:00
Rick Brown
0e9da69f47 Minor ssh debug logging changes
-Refactor the logger implementation to make creation more intuitive
-Remove SSH debug logging preference persistence (didn't work properly
 anyway, and probably not worth trying to fix)
2023-07-23 22:29:13 -04:00
Rick Brown
18ecfd5396 Integrate KEx/SHK functionality into JavaFileStorageTest-AS
-Re-organize SFTP Credentials dialog to be more space-efficient
-Add KEX and SHK algorithm spec fields (these get used to build the SFTP
 URI when connecting)
-Add CSV test fields/buttons for standalone testing of spec/config
 resolution
2023-07-23 22:29:13 -04:00
PhilippC
0fef5f0f8c New translations strings.xml (Spanish) 2023-07-22 08:25:42 +02:00
Rick Brown
83529dd3b5 Modify/specify KEX/SHK algorithms
-Implemented ability to manipulate server_host_key (SHK) via SFTP
 Credentials dialog (like KEX)
-Implemented a few basic wildcard/relative algorithm list manipulation
features:
   - Prepend to existing list: +alg_name
   - Append to end of existing list: alg_name+
   - Remove a specific value: -alg_name
   - Remove values matching prefix: -alg_prefix*
   - Remove values matching suffix: -*alg_suffix
   - Remove values matching substring: -*alg_substring*
   - Remove values matching prefix and suffix: -alg*name
   - Otherwise CSV of values completely replace original config values
2023-07-20 19:48:49 -04:00
Rick Brown
9204c4ca8f Add ssh config options to display URI 2023-07-19 22:11:34 -04:00
Rick Brown
46fdba1bfa SSH/SFTP: Allow kex algorithms to be explicitly set
-kex config overload, set via database connection settings
2023-07-19 19:38:00 -04:00
Rick Brown
006f5497e5 Merge branch 'bug-2366-ssh-debug-logging_master' into custom-sftp-private-key_patches 2023-07-19 17:12:53 -04:00
Rick Brown
da3665c25b Fix NoNet compilation error 2023-07-12 18:40:57 -04:00
Rick Brown
464fe43323 Add JSch (SFTP) debug logging
-App Settings->Log-File for Debugging->SFTP debug logging
-Logs to android log (logcat) if log file is not enabled
-Logs to Kp2a log file if it is enabled
-Logs are tagged as "KP2AJFS[JSch]"
-When enabled, logs ALL levels (DEBUG+).

NOTE: Sensitive SSH connection information may be logged!!
2023-07-12 17:03:39 -04:00
PhilippC
bded2394bb New translations strings.xml (Russian) 2023-07-10 00:19:43 +02:00
PhilippC
0fe2ca8238 New translations strings.xml (Russian) 2023-07-09 23:15:32 +02:00
PhilippC
ae33ca219f New translations strings.xml (Hungarian) 2023-07-08 15:24:04 +02:00
AlexCherrypi
fb0f83c37a Update PasswordActivity.cs 2023-06-18 21:49:26 +02:00
Rick Brown
da5533ef3b Modified impl of bugfix #2350
URL encode/decode host parameter in SFTP URI

This version is slightly different than the original PR, given
this branch's changes to SftpStorage.buildFullPath().
2023-06-16 19:40:01 -04:00
PhilippC
681dfb6ded New translations strings.xml (Russian) 2023-06-10 23:46:19 +02:00
PhilippC
20f334f0d3 New translations strings.xml (Danish) 2023-06-09 10:00:23 +02:00
PhilippC
d8268d4f0f New translations strings.xml (Russian) 2023-06-02 23:03:27 +02:00
PhilippC
325e8a8e32 New translations strings.xml (Russian) 2023-06-02 22:05:45 +02:00
PhilippC
7e9e91da05 New translations strings.xml (Russian) 2023-06-02 20:52:43 +02:00
PhilippC
80eaf39f04 New translations strings.xml (Russian) 2023-06-02 19:56:15 +02:00
PhilippC
ddffdb48aa New translations strings.xml (Czech) 2023-06-01 11:20:16 +02:00
PhilippC
5a406fe5df New translations strings.xml (German) 2023-05-31 14:05:45 +02:00
AlexCherrypi
4e2603ae27 remember keyprovider for "Password + Key file + Challenge-Response for KeePass XC" "
extended "SetKeyProviderFromString()" to set _keyFile for "ChallengeXCKeyFile";
extended "InitializePasswordModeSpinner()" case 7 to remember key file location
2023-05-21 15:53:31 +02:00
ianjazz246
bcf980eed5 Make _activeProgressDialogs readonly 2023-05-20 10:58:19 -07:00
ianjazz246
05c94a3af8 Fix a few more tabs 2023-05-20 10:48:32 -07:00
ianjazz246
3526aa1889 Fix more tabs 2023-05-20 10:46:29 -07:00
ianjazz246
72a3b55341 Fix tab indentation 2023-05-20 10:42:09 -07:00
ianjazz246
b11d5e667e Hide progress dialogs when dialog requesting user input is showing 2023-05-20 10:19:14 -07:00
PhilippC
93a4529fe9 New translations strings.xml (Indonesian) 2023-05-10 14:15:38 +02:00
PhilippC
7582274903 New translations strings.xml (Catalan) 2023-05-08 21:28:00 +02:00
Philipp Crocoll
158349c005 mark camera as optional feature to make the app compatible with non-camera devices again, closes https://github.com/PhilippC/keepass2android/issues/2316 2023-04-21 04:44:08 +02:00
PhilippC
2fffe5988c New translations strings.xml (German) 2023-04-16 11:34:09 +02:00
PhilippC
14f7e17fa4 New translations strings.xml (Czech) 2023-04-10 17:51:09 +02:00
PhilippC
05acba4309 New translations strings.xml (Czech) 2023-04-10 16:55:36 +02:00
PhilippC
542984ca2f New translations strings.xml (Japanese) 2023-04-08 18:26:37 +02:00
PhilippC
f8746f69f8 New translations strings.xml (Slovak) 2023-04-08 11:16:05 +02:00
PhilippC
53913e66ab New translations strings.xml (Polish) 2023-04-07 16:17:52 +02:00
Rick Brown
5e265d1816 Backend:
-Generalize SFTP query param option map building
-Add "key" and "phrase" as SFTP query params
  key: custom private key name
  phrase: passphrase used to unlock key
-Add CRUD support for custom private keys
  Key files are stored in "user_keys" subdirectory
  File names are constructed by (sanitized) key name
  Basic support for private key content validation
-Existing and new key-related functionality moved into
  SftpPublicPrivateKeyUtils class

UI:
-Add custom private key support to SFTP Credentials dialog
  Add a new auth mode item (authModeSpinner)
  Add Spinner showing saved private key names, with an option
   to create a new one (top).
  Add Delete Private Key button; deletes the selected key
   in Spinner

Testing:
-Add custom private key CRUD support to JavaFileStorageTest app
 via file chooser SFTP Credentials panel
2023-02-19 20:26:39 -05:00
Rick Brown
83e77b2a31 Bugfix for #2223 - crash after import database by SFTP
Add FLAG_MUTABLE flag to PendingIntent call for API >= 31 to fix an
issue where trying to open an SFTP database (transition to choose a
remote database file) crashes and returns to the Open/New database
screen.
2023-02-19 19:52:05 -05:00
Rick Brown
893cf2b3c8 Get JavaFileStorage working in Android Studio
Resolve issue where AS would fail to import Android API jar
2023-02-19 19:52:05 -05:00
Rick Brown
15b3b76b27 Squashed commit of not-yet-approved PR #2038
https://github.com/PhilippC/keepass2android/pull/2038
User-defined SFTP connection timeout

The addition of SFTP query parameter options are needed
to support custom private key functionality.

Squashed commits from hyproman:sftp-conn-timeout:

commit 9c6b96e8198f1b912acdc1248af775f8fed58e1c
commit ebe59d9bc337a46bf0646677eb38b13ddde21f14
commit 69eb0bfd1a7010a2e442c36d10a16d1710c958de
commit 9394947c12bedb8667b7b94d0b1457f9e0451e18
2023-02-15 19:08:14 -05:00
79 changed files with 2635 additions and 546 deletions

72
docs/SFTP-Credentials.md Normal file
View File

@@ -0,0 +1,72 @@
# SFTP Open/Create Database Credentials Documentation
## Basic Settings
* **Host** -- the hostname or IP address of the SFTP server to connect to
* **Port** -- the listening TCP port of the SFTP server to connect to (default: 22)
* **Username** -- the user/account name on the SFTP server that has access to the database
* **Initial directory** -- The path on the SFTP server that will be used as a starting point when choosing the remote database file
### Authentication Modes
#### Password
Authenticate using a password
* **Password** -- the password associated with **username** used to log into the SFTP server
#### K2A Private/Public Key
Authenticate using a private/public key pair that is generated internally by KP2A
* **SEND PUBLIC KEY...** -- Opens a standard Android "Share" screen containing the KP2A public key content. This allows for the public key to be sent via email, SMS, etc. This public key will need to be added to the SFTP server's user's "authorized keys" to allow private/public key authentication.
#### Custom Private Key
Authenticate using an existing private/public key pair. Use this option instead of *K2A Private/Public Key* if you wish to use a key pair that is already set up for this **username** on the SFTP server.
* **Selected private key** -- a combo-box containing a list of custom private keys that KP2A knows about, and a special `[Add new...]` option.
##### Add A New Private Key
* Select `[Add new...]`
* Enter a name for the new key in **New key name**
* Enter the private key contents (text) into **New key content**. **TIP:** The easiest way to accomplish this is to open the private key file in a text editor on the device, **Select All**, **Copy** to the clipboard, and paste it into **New key content**.
* Tap **SAVE PRIVATE KEY** to add the new key to the known list.
##### Use An Existing Private Key
* To use a private key that has already been imported into KP2A, simply select it from the list of keys.
##### Remove An Existing Key
* To remove a private that has been imported into KP2A, select it from the list and tap **DELETE PRIVATE KEY**.
A **key passphrase** can be supplied (if the key pair requires it)
## Advanced Settings
* **Connection timeout seconds** -- the number of seconds to wait for a connection to the server before giving up and considering the server as unavailable/unreachable
### Key Algorithm Manipulation
**NOTE: It is very rare that these fields need to be (or should be) specified. Use at your own risk!**
* **Key Exchange (KEX) Algorithm(s)** -- Explicitly set or modify the ordered list of Key Exchange algorithms that the SSH/SFTP client library will try to use
* **Server Host Key Algorithm(s)** -- Explicitly set or modify the ordered list of Server Host Key algorithms that the SSH/SFTP client library will try to use
#### How It Works
The SSH/SFTP client has a pre-defined ordered list of algorithm names that it will use to negotiate with the server to handle key exchange. In rare cases there are compatibility issues where Android OS has not properly implemented full support for algorithms listed. This can result in a connection failure, even if there is a suitable algorithm available (of lesser priority in the list).
The fields listed above allow these lists to be manipulated in the following ways to overcome/workaround such problems. The value is a comma-separated list of "algorithm spec" entries. Specs can be one of:
* Direct replacement of values -- Ex: `primary_alg,secondary_alg`
* Prepend to values -- Ex: `+try_first_alg`
* Append to values -- Ex: `try_last_alg+`
* Remove a specific value -- Ex: `-bad_alg`
* Remove values matching prefix -- Ex: `-bad_starting_with*`
* Remove values matching suffix -- Ex: `-*bad_ending_with`
* Remove values matching substring -- Ex: `-*bad_middle*`
* Remove values matching prefix and suffix -- Ex: `-alg_begin*end`
For example, assume the system's KEX algorithm list is:
`ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256`
These are various outcomes (user KEX field -> result):
* Prefix removal: `-ec*` --> `diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256`
* Suffix removal, appending: `-*256,+first_alg,almost_last_alg+,last_alg+` --> `first_alg,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,almost_last_alg,last_alg`
* Direct replacement: `first_alg,middle_alg,last_alg` --> `first_alg,middle_alg,last_alg`
## Selecting A Database
Once all applicable fields have been entered and/or options selected, tapping **OK** will attempt to connect to the SFTP server. First time connections may pop up a dialog window asking to accept the host's authenticity (tap **yes** if the host is trusted), as well as potentially creating a new `known_hosts` file (tap **yes** to do so). If the connection is successful, a remote file browser screen will open. Navigate and select the Keepass database to open.

View File

@@ -58,12 +58,12 @@ namespace keepass2android
}
private static string LogFilename
public static string LogFilename
{
get { return Application.Context.FilesDir.CanonicalPath +"/keepass2android.log"; }
}
private static bool LogToFile
public static bool LogToFile
{
get
{

View File

@@ -25,7 +25,7 @@ namespace keepass2android.Io
public abstract bool UserShouldBackup { get; }
private readonly IJavaFileStorage _jfs;
protected readonly IJavaFileStorage _jfs;
private readonly IKp2aApp _app;
public JavaFileStorage(IJavaFileStorage jfs, IKp2aApp app)

View File

@@ -1,18 +1,18 @@
#if !NoNet
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading;
using Android.Content;
using Android.OS;
using Android.Preferences;
using FluentFTP;
using FluentFTP.Exceptions;
using KeePassLib;
using KeePassLib.Serialization;
using KeePassLib.Utility;
namespace keepass2android.Io
{
public class NetFtpFileStorage: IFileStorage
@@ -75,14 +75,15 @@ namespace keepass2android.Io
}
private readonly ICertificateValidationHandler _app;
private readonly Func<bool> _debugLogPrefGetter;
public MemoryStream traceStream;
public NetFtpFileStorage(Context context, ICertificateValidationHandler app)
public NetFtpFileStorage(Context context, ICertificateValidationHandler app, Func<bool> debugLogPrefGetter)
{
_app = app;
traceStream = new MemoryStream();
_debugLogPrefGetter = debugLogPrefGetter;
traceStream = new MemoryStream();
}
public IEnumerable<string> SupportedProtocols
@@ -138,7 +139,7 @@ namespace keepass2android.Io
var settings = ConnectionSettings.FromIoc(ioc);
FtpClient client = new FtpClient();
client.RetryAttempts = 3;
client.Config.RetryAttempts = 3;
if ((settings.Username.Length > 0) || (settings.Password.Length > 0))
client.Credentials = new NetworkCredential(settings.Username, settings.Password);
else
@@ -154,9 +155,12 @@ namespace keepass2android.Io
args.Accept = _app.CertificateValidationCallback(control, args.Certificate, args.Chain, args.PolicyErrors);
};
client.EncryptionMode = settings.EncryptionMode;
client.Connect();
client.Config.EncryptionMode = settings.EncryptionMode;
if (_debugLogPrefGetter())
client.Logger = new Kp2aLogFTPLogger();
client.Connect();
return client;
}
@@ -284,42 +288,55 @@ namespace keepass2android.Io
public IEnumerable<FileDescription> ListContents(IOConnectionInfo ioc)
{
try
try
{
using (var client = GetClient(ioc))
{
/*
* For some reason GetListing(path) does not always return the contents of the directory.
* However, calling SetWorkingDirectory(path) followed by GetListing(null, options) to
* list the contents of the working directory does consistently work.
*
* Similar behavior was confirmed using ncftp client. I suspect this is a strange
* bug/nuance in the server's implementation of the LIST command?
*
* [bug #2423]
*/
client.SetWorkingDirectory(IocToLocalPath(ioc));
List<FileDescription> files = new List<FileDescription>();
foreach (FtpListItem item in client.GetListing(IocToLocalPath(ioc),
FtpListOption.Modify | FtpListOption.Size | FtpListOption.DerefLinks))
foreach (FtpListItem item in client.GetListing(null,
FtpListOption.SizeModify | FtpListOption.AllFiles))
{
switch (item.Type)
switch (item.Type)
{
case FtpFileSystemObjectType.Directory:
case FtpObjectType.Directory:
files.Add(new FileDescription()
{
CanRead = true,
CanWrite = true,
DisplayName = item.Name,
IsDirectory = true,
LastModified = item.Modified,
Path = IocPathFromUri(ioc, item.FullName)
});
break;
case FtpFileSystemObjectType.File:
{
CanRead = true,
CanWrite = true,
DisplayName = item.Name,
IsDirectory = true,
LastModified = item.Modified,
Path = IocPathFromUri(ioc, item.FullName)
});
break;
case FtpObjectType.File:
files.Add(new FileDescription()
{
CanRead = true,
CanWrite = true,
DisplayName = item.Name,
IsDirectory = false,
LastModified = item.Modified,
Path = IocPathFromUri(ioc, item.FullName),
SizeInBytes = item.Size
});
{
CanRead = true,
CanWrite = true,
DisplayName = item.Name,
IsDirectory = false,
LastModified = item.Modified,
Path = IocPathFromUri(ioc, item.FullName),
SizeInBytes = item.Size
});
break;
default:
Kp2aLog.Log("FTP: ListContents item skipped: " + IocToUri(ioc) + ": " + item.FullName + ", type=" + item.Type);
break;
}
}
}
return files;
}
@@ -329,7 +346,6 @@ namespace keepass2android.Io
throw ConvertException(ex);
}
}
public FileDescription GetFileDescription(IOConnectionInfo ioc)
{
@@ -466,7 +482,9 @@ namespace keepass2android.Io
public static int GetDefaultPort(FtpEncryptionMode encryption)
{
return new FtpClient() { EncryptionMode = encryption}.Port;
var client = new FtpClient();
client.Config.EncryptionMode = encryption;
return client.Port;
}
public string BuildFullPath(string host, int port, string initialPath, string user, string password, FtpEncryptionMode encryption)
@@ -582,5 +600,13 @@ namespace keepass2android.Io
_stream.Close();
}
}
class Kp2aLogFTPLogger : IFtpLogger
{
public void Log(FtpLogEntry entry)
{
Kp2aLog.Log("[FluentFTP] " + entry.Message);
}
}
}
#endif

View File

@@ -1,17 +1,39 @@
using Android.Content;
using Java.Nio.FileNio;
#if !EXCLUDE_JAVAFILESTORAGE
namespace keepass2android.Io
{
public class SftpFileStorage: JavaFileStorage
{
public SftpFileStorage(Context ctx, IKp2aApp app) :
public SftpFileStorage(Context ctx, IKp2aApp app, bool debugEnabled) :
base(new Keepass2android.Javafilestorage.SftpStorage(ctx.ApplicationContext), app)
{
}
var storage = BaseStorage;
if (debugEnabled)
{
string? logFilename = null;
if (Kp2aLog.LogToFile)
{
logFilename = Kp2aLog.LogFilename;
}
storage.SetJschLogging(true, logFilename);
}
else
{
storage.SetJschLogging(false, null);
}
}
private Keepass2android.Javafilestorage.SftpStorage BaseStorage
{
get
{
return _jfs as Keepass2android.Javafilestorage.SftpStorage;
}
}
public override bool UserShouldBackup
public override bool UserShouldBackup
{
get { return true; }
}

View File

@@ -182,7 +182,7 @@
</ItemGroup>
<ItemGroup Condition=" '$(Flavor)'!='NoNet' ">
<PackageReference Include="FluentFTP">
<Version>31.3.1</Version>
<Version>48.0.0</Version>
</PackageReference>
<PackageReference Include="MegaApiClient">
<Version>1.10.3</Version>
@@ -312,4 +312,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@@ -97,7 +97,28 @@ public class FileEntry {
return false;
return true;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder("JavaFileStorage.FileEntry{").append(displayName).append("|")
.append("path=").append(path).append(",sz=").append(sizeInBytes)
.append(",").append(isDirectory ? "dir" : "file")
.append(",lastMod=").append(lastModifiedTime);
StringBuilder perms = new StringBuilder();
if (canRead)
perms.append("r");
if (canWrite)
perms.append("w");
if (perms.length() > 0) {
s.append(",").append(perms);
}
if (userData != null && userData.length() > 0)
s.append(",userData=").append(userData);
return s.append("}").toString();
}
}

View File

@@ -0,0 +1,101 @@
package keepass2android.javafilestorage;
import android.util.Log;
import com.jcraft.jsch.Logger;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.Map;
public class Kp2aJSchLogger implements Logger {
private static final String PREFIX = "KP2AJFS[JSch]";
private interface ILogger {
void log(String message);
}
private interface EntryToLogFactory {
ILogger create(LogEntry e);
}
private static final EntryToLogFactory ANDROID_FACTORY = e -> e.logger;
private static final class LogEntry {
private final String levelTag;
private final ILogger logger;
LogEntry(String levelTag, ILogger logger) {
this.levelTag = levelTag;
this.logger = logger;
}
}
private static final ILogger DEBUG = msg -> Log.d(PREFIX, msg);
private static final LogEntry DEBUG_ENTRY = new LogEntry("D", DEBUG);
private static final ILogger ERROR = msg -> Log.e(PREFIX, msg);
private static final LogEntry DEFAULT_ENTRY = DEBUG_ENTRY;
private static final Map<Integer, LogEntry> loggers = Map.of(
Logger.DEBUG, DEBUG_ENTRY,
Logger.INFO, new LogEntry("I", msg -> Log.i(PREFIX, msg)),
Logger.WARN, new LogEntry("W", msg -> Log.w(PREFIX, msg)),
Logger.ERROR, new LogEntry("E", ERROR),
Logger.FATAL, new LogEntry("F", msg -> Log.wtf(PREFIX, msg))
);
private final EntryToLogFactory logFactory;
static Kp2aJSchLogger createAndroidLogger() {
return new Kp2aJSchLogger(ANDROID_FACTORY);
}
static Kp2aJSchLogger createFileLogger(String logFilename) {
final String fName = logFilename;
return new Kp2aJSchLogger(e -> createFileLogger(e, fName));
}
private Kp2aJSchLogger(EntryToLogFactory logFactory) {
this.logFactory = logFactory;
}
@Override
public boolean isEnabled(int level) {
return true;
}
@Override
public void log(int level, String message) {
if (isEnabled(level))
getLogger(level).log(message);
}
private ILogger getLogger(int level) {
LogEntry entry = loggers.get(level);
if (entry == null)
entry = DEFAULT_ENTRY;
return logFactory.create(entry);
}
private static ILogger createFileLogger(LogEntry entry, String fName) {
try {
final PrintWriter p = new PrintWriter(new FileWriter(fName, true));
return msg -> {
try {
String fullMsg = String.join(" ", entry.levelTag, PREFIX, msg);
p.println(fullMsg);
} catch (Exception e) {
ERROR.log(e.getMessage());
} finally {
p.close();
}
};
} catch (Exception e) {
ERROR.log(e.getMessage());
return entry.logger;
}
}
}

View File

@@ -146,7 +146,7 @@ public class PCloudFileStorage extends JavaFileStorageBase
DataSource dataSource = DataSource.create(data);
String filename = path.substring(path.lastIndexOf("/") + 1);
String filePath = path.substring(0, path.lastIndexOf("/") + 1);
String filePath = path.substring(0, path.lastIndexOf("/"));
RemoteFolder remoteFolder = this.getRemoteFolderByPath(filePath);
try {
@@ -175,11 +175,14 @@ public class PCloudFileStorage extends JavaFileStorageBase
@Override
public String createFilePath(String parentPath, String newFileName) throws Exception {
String cleanpath = this.cleanPath(parentPath);
String filepath = this.getProtocolId() + "://";
return (
this.getProtocolId() + "://" +
this.cleanPath(parentPath) +
("".equals(newFileName) ? "" : "/") +
newFileName
filepath
+cleanpath
+("".equals(newFileName) || "/".equals(cleanpath) ? "" : "/") +newFileName
);
}
@@ -201,7 +204,7 @@ public class PCloudFileStorage extends JavaFileStorageBase
@Override
public FileEntry getFileEntry(String path) throws Exception {
path = this.cleanPath(path);
//do not call getRemoteFileByPath because path could represent a file or folder, we don't know here
RemoteEntry remoteEntry = this.getRemoteEntryByPath(path);
return this.convertRemoteEntryToFileEntry(
@@ -214,10 +217,13 @@ public class PCloudFileStorage extends JavaFileStorageBase
public void delete(String path) throws Exception {
path = this.cleanPath(path);
RemoteEntry remoteEntry = this.getRemoteFileByPath(path);
RemoteEntry remoteEntry = this.getRemoteEntryByPath(path);
try {
this.apiClient.delete(remoteEntry).execute();
if (remoteEntry.isFolder())
this.apiClient.deleteFolder(remoteEntry.asFolder(), true).execute();
else
this.apiClient.delete(remoteEntry).execute();
} catch (ApiError e) {
throw convertApiError(e);
}
@@ -289,7 +295,7 @@ public class PCloudFileStorage extends JavaFileStorageBase
}
private ApiClient createApiClientFromSharedPrefs() {
SharedPreferences prefs = this.ctx.getSharedPreferences(sharedPrefPrefix + SHARED_PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences prefs = getPrefs();
String authToken = prefs.getString(SHARED_PREF_AUTH_TOKEN, null);
String apiHost = prefs.getString(SHARED_PREF_API_HOST, null);
return this.createApiClient(authToken, apiHost);
@@ -313,15 +319,20 @@ public class PCloudFileStorage extends JavaFileStorageBase
private void clearAuthToken() {
this.apiClient = null;
SharedPreferences prefs = this.ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences prefs = getPrefs();
SharedPreferences.Editor edit = prefs.edit();
edit.clear();
edit.apply();
}
private SharedPreferences getPrefs()
{
return this.ctx.getSharedPreferences(sharedPrefPrefix + SHARED_PREF_NAME, Context.MODE_PRIVATE);
}
private void setAuthToken(String authToken, String apiHost) {
this.apiClient = this.createApiClient(authToken, apiHost);
SharedPreferences prefs = this.ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
SharedPreferences prefs = getPrefs();
SharedPreferences.Editor edit = prefs.edit();
edit.putString(SHARED_PREF_AUTH_TOKEN, authToken);
edit.putString(SHARED_PREF_API_HOST, apiHost);
@@ -335,27 +346,47 @@ public class PCloudFileStorage extends JavaFileStorageBase
}
private RemoteFile getRemoteFileByPath(String path) throws Exception {
RemoteEntry remoteEntry = this.getRemoteEntryByPath(path);
Call<RemoteFile> call = this.apiClient.loadFile(path);
try {
return remoteEntry.asFile();
} catch (IllegalStateException e) {
throw new FileNotFoundException(e.toString());
return call.execute();
} catch (ApiError apiError) {
throw convertApiError(apiError);
}
}
private RemoteFolder getRemoteFolderByPath(String path) throws Exception {
RemoteEntry remoteEntry = this.getRemoteEntryByPath(path);
Call<RemoteFolder> call;
if ("".equals(path))
call = this.apiClient.listFolder(RemoteFolder.ROOT_FOLDER_ID, false);
else
call = this.apiClient.listFolder(path, false);
try {
return remoteEntry.asFolder();
} catch (IllegalStateException e) {
throw new FileNotFoundException(e.toString());
return call.execute();
} catch (ApiError apiError) {
throw convertApiError(apiError);
}
}
private RemoteEntry getRemoteEntryByPath(String path) throws Exception {
Call<RemoteFolder> call = this.apiClient.listFolder(RemoteFolder.ROOT_FOLDER_ID, true);
if ("/".equals(path)) {
try {
return this.apiClient.listFolder(RemoteFolder.ROOT_FOLDER_ID, false).execute();
} catch (ApiError apiError) {
throw convertApiError(apiError);
}
}
String filename = path.substring(path.lastIndexOf("/") + 1);
String parentPath = path.substring(0, path.lastIndexOf("/"));
Call<RemoteFolder> call;
if ("".equals(parentPath))
call = this.apiClient.listFolder(RemoteFolder.ROOT_FOLDER_ID, false);
else
call = this.apiClient.listFolder(parentPath, false);
RemoteFolder folder;
try {
@@ -364,40 +395,12 @@ public class PCloudFileStorage extends JavaFileStorageBase
throw convertApiError(apiError);
}
if ("/".equals(path)) {
return folder;
for (RemoteEntry remoteEntry : folder.children()) {
if (remoteEntry.name() != null && remoteEntry.name().equals(filename))
return remoteEntry;
}
throw new FileNotFoundException("did not find " + path);
String[] fileNames = path.substring(1).split("/");
RemoteFolder currentFolder = folder;
Iterator<String> fileNamesIterator = Arrays.asList(fileNames).iterator();
while (true) {
String fileName = fileNamesIterator.next();
Iterator<RemoteEntry> entryIterator = currentFolder.children().iterator();
while (true) {
RemoteEntry remoteEntry;
try {
remoteEntry = entryIterator.next();
} catch (NoSuchElementException e) {
throw new FileNotFoundException(e.toString());
}
if (currentFolder.folderId() == remoteEntry.parentFolderId() && fileName.equals(remoteEntry.name())) {
if (!fileNamesIterator.hasNext()) {
return remoteEntry;
}
try {
currentFolder = remoteEntry.asFolder();
} catch (IllegalStateException e) {
throw new FileNotFoundException(e.toString());
}
break;
}
}
}
}
private Exception convertApiError(ApiError e) {

View File

@@ -0,0 +1,216 @@
package keepass2android.javafilestorage;
import android.util.Pair;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.util.regex.Pattern;
import androidx.annotation.Nullable;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.KeyPair;
class SftpPublicPrivateKeyUtils {
private enum Validity {
NOT_ATTEMPTED, VALID, NOT_VALID;
}
private static final String SFTP_CUSTOM_KEY_DIRNAME = "user_keys";
private static final String KP2A_PRIVATE_KEY_FILENAME = "id_kp2a_rsa";
private final File appBaseDir;
/**
* Do NOT access this variable directly! Use {@link #baseDir()} instead.
*/
private final File customKeyBaseDir;
private volatile Validity validDir = Validity.NOT_ATTEMPTED;
SftpPublicPrivateKeyUtils(String appBaseDir) {
// Assume app base directory exists already
this.appBaseDir = new File(appBaseDir);
// Intentionally skipping existence/creation checking in constructor
// See baseDir()
this.customKeyBaseDir = new File(appBaseDir, SFTP_CUSTOM_KEY_DIRNAME);
}
private Pair<File, Boolean> baseDir() {
if (validDir == Validity.NOT_ATTEMPTED) {
synchronized (this) {
if (!customKeyBaseDir.exists()) {
customKeyBaseDir.mkdirs();
}
if (customKeyBaseDir.exists() && customKeyBaseDir.isDirectory()) {
validDir = Validity.VALID;
} else {
validDir = Validity.NOT_VALID;
}
}
}
return new Pair<>(customKeyBaseDir, validDir == Validity.VALID);
}
boolean deleteCustomKey(String keyName) throws FileNotFoundException {
File f = getCustomKeyFile(keyName);
return f.isFile() && f.delete();
}
String[] getCustomKeyNames() {
Pair<File, Boolean> base = baseDir();
if (!base.second) {
// Log it?
return new String[]{};
}
return base.first.list();
}
void savePrivateKeyContent(String keyName, String keyContent) throws IOException, Exception {
keyContent = PrivateKeyValidator.ensureValidContent(keyContent);
File f = getCustomKeyFile(keyName);
try (BufferedWriter w = new BufferedWriter(new FileWriter(f))) {
w.write(keyContent);
}
}
String getCustomKeyFilePath(String customKeyName) throws FileNotFoundException {
return getCustomKeyFile(customKeyName).getAbsolutePath();
}
String resolveKeyFilePath(JSch jschInst, @Nullable String customKeyName) {
// Custom private key configured
if (customKeyName != null) {
try {
return getCustomKeyFilePath(customKeyName);
} catch (FileNotFoundException e) {
System.out.println(e);
}
}
// Use KP2A's public/private key
String keyFilePath = getAppKeyFileName();
try{
createKeyPair(jschInst, keyFilePath);
} catch (Exception ex) {
System.out.println(ex);
}
return keyFilePath;
}
String createKeyPair(JSch jschInst) throws IOException, JSchException {
return createKeyPair(jschInst, getAppKeyFileName());
}
/**
* Exposed for testing purposes only
* @param keyName
* @return
*/
String getSanitizedCustomKeyName(String keyName) {
return PrivateKeyValidator.sanitizeKeyAsFilename(keyName);
}
/**
* Exposed for testing purposes only.
* @param keyContent
* @return
* @throws Exception
*/
String getValidatedCustomKeyContent(String keyContent) throws Exception {
return PrivateKeyValidator.ensureValidContent(keyContent);
}
private String createKeyPair(JSch jschInst, String key_filename) throws JSchException, IOException {
String public_key_filename = key_filename + ".pub";
File file = new File(key_filename);
if (file.exists())
return public_key_filename;
int type = KeyPair.RSA;
KeyPair kpair = KeyPair.genKeyPair(jschInst, type, 4096);
kpair.writePrivateKey(key_filename);
kpair.writePublicKey(public_key_filename, "generated by Keepass2Android");
//ret = "Fingerprint: " + kpair.getFingerPrint();
kpair.dispose();
return public_key_filename;
}
private String getAppKeyFileName() {
return new File(appBaseDir, KP2A_PRIVATE_KEY_FILENAME).getAbsolutePath();
}
private File getCustomKeyFile(String customKeyName) throws FileNotFoundException {
Pair<File, Boolean> base = baseDir();
if (!base.second) {
throw new FileNotFoundException("Custom key directory");
}
String keyFileName = PrivateKeyValidator.sanitizeKeyAsFilename(customKeyName);
if (!keyFileName.isEmpty()) {
File keyFile = new File(base.first, keyFileName);
// Protect against bad actors trying to navigate away from the base directory.
// This is probably overkill, given sanitizeKeyAsFilename(...) but better safe than sorry.
if (base.first.equals(keyFile.getParentFile())) {
return keyFile;
}
}
// The key was sanitized to nothing, or the parent check above failed.
throw new FileNotFoundException("Malformed key name");
}
private static class PrivateKeyValidator {
private static final Pattern CONTENT_FIRST_LINE = Pattern.compile("^-+BEGIN\\s[^\\s]+\\sPRIVATE\\sKEY-+$");
private static final Pattern CONTENT_LAST_LINE = Pattern.compile("^-+END\\s[^\\s]+\\sPRIVATE\\sKEY-+$");
/**
* Key-to-filename sanitizer solution sourced from:
* <a href="https://www.b4x.com/android/forum/threads/sanitize-filename.82558/" />
*/
private static final Pattern KEY_SANITIZER = Pattern.compile("([^\\p{L}\\s\\d\\-_~,;:\\[\\]\\(\\).'])",
Pattern.CASE_INSENSITIVE);
static String sanitizeKeyAsFilename(String key) {
return KEY_SANITIZER.matcher(key.trim()).replaceAll("");
}
static String ensureValidContent(String content) throws Exception {
content = content.trim();
boolean isValid = true;
try (BufferedReader r = new BufferedReader(new StringReader(content))) {
boolean validFirst = false;
String line;
String last = null;
while ((line = r.readLine()) != null) {
if (!validFirst) {
if (CONTENT_FIRST_LINE.matcher(line).matches()) {
validFirst = true;
} else {
isValid = false;
break;
}
}
last = line;
}
if (!isValid || last == null || !CONTENT_LAST_LINE.matcher(last).matches()) {
throw new RuntimeException("Malformed private key content");
}
} catch (Exception e) {
android.util.Log.d(SftpStorage.class.getName(), "Invalid key content", e);
throw e;
}
return content;
}
}
}

View File

@@ -2,20 +2,24 @@ package keepass2android.javafilestorage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.KeyPair;
import com.jcraft.jsch.Logger;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
@@ -26,10 +30,38 @@ import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@SuppressWarnings("unused") // Exposed by JavaFileStorageBindings
public class SftpStorage extends JavaFileStorageBase {
@FunctionalInterface
interface ValueResolver<T> {
/**
* Takes a raw value and resolves it to either a String containing the String representation
* of that value, or null. The latter signifying that the raw value could not be "resolved".
*
* @param value
* @return String, or null if not resolvable
*/
String resolve(T value);
}
public static final int DEFAULT_SFTP_PORT = 22;
JSch jsch;
public static final int UNSET_SFTP_CONNECT_TIMEOUT = -1;
private static final String SFTP_CONNECT_TIMEOUT_OPTION_NAME = "connectTimeout";
private static final String SFTP_KEYNAME_OPTION_NAME = "key";
private static final String SFTP_KEYPASSPHRASE_OPTION_NAME = "phrase";
public static final String SSH_CFG_KEX = "kex";
public static final String SSH_CFG_SERVER_HOST_KEY = "server_host_key";
private static final Set<String> SSH_CFG_CSV_EXPANDABLE = Set.of(SSH_CFG_KEX, SSH_CFG_SERVER_HOST_KEY);
private static final ValueResolver<Integer> cTimeoutResolver = c ->
c == null || c == UNSET_SFTP_CONNECT_TIMEOUT ? null : String.valueOf(c);
private static final ValueResolver<String> nonBlankStringResolver = s ->
s == null || s.isBlank() ? null : s;
private static final String TAG = "KP2AJFS";
private static final String THREAD_TAG = TAG + "[thread]";
private JSch jsch;
public class ConnectionInfo
{
@@ -37,13 +69,42 @@ public class SftpStorage extends JavaFileStorageBase {
public String username;
public String password;
public String localPath;
public String keyName;
public String keyPassphrase;
public int port;
public int connectTimeoutSec = UNSET_SFTP_CONNECT_TIMEOUT;
public final Map<String, String> configOpts = new HashMap<>();
public String toString() {
return "ConnectionInfo{host=" + host + ",port=" + port + ",user=" + username +
",pwd=<hidden>,localPath=" + localPath + ",key=" + keyName +
",phrase=<hidden>,connectTimeout=" + connectTimeoutSec +
",cfgOpts=" + configOpts +
"}";
}
}
private static Map<String, String> buildOptionMap(ConnectionInfo ci, boolean includeSensitive) {
OptionMapBuilder b = new OptionMapBuilder()
.addOption(SFTP_CONNECT_TIMEOUT_OPTION_NAME, ci.connectTimeoutSec, cTimeoutResolver)
.addOption(SFTP_KEYNAME_OPTION_NAME, ci.keyName, nonBlankStringResolver);
// Assume all config options are not sensitive and use the same resolver...
for (Map.Entry<String, String> entry : ci.configOpts.entrySet()) {
b.addOption(entry.getKey(), entry.getValue(), nonBlankStringResolver);
}
if (includeSensitive) {
b.addOption(SFTP_KEYPASSPHRASE_OPTION_NAME, ci.keyPassphrase, nonBlankStringResolver);
}
return b.build();
}
Context _appContext;
private final SftpPublicPrivateKeyUtils _keyUtils;
public SftpStorage(Context appContext) {
_appContext = appContext;
_keyUtils = new SftpPublicPrivateKeyUtils(getBaseDir());
}
private static final String SFTP_PROTOCOL_ID = "sftp";
@@ -65,15 +126,15 @@ public class SftpStorage extends JavaFileStorageBase {
@Override
public InputStream openFileForRead(String path) throws Exception {
ChannelSftp c = init(path);
ConnectionInfo cInfo = splitStringToConnectionInfo(path);
ChannelSftp c = init(cInfo);
try {
byte[] buff = new byte[8000];
int bytesRead = 0;
InputStream in = c.get(extractSessionPath(path));
InputStream in = c.get(cInfo.localPath);
ByteArrayOutputStream bao = new ByteArrayOutputStream();
while ((bytesRead = in.read(buff)) != -1) {
@@ -105,14 +166,15 @@ public class SftpStorage extends JavaFileStorageBase {
public void uploadFile(String path, byte[] data, boolean writeTransactional)
throws Exception {
ChannelSftp c = init(path);
ConnectionInfo cInfo = splitStringToConnectionInfo(path);
ChannelSftp c = init(cInfo);
try {
InputStream in = new ByteArrayInputStream(data);
String targetPath = extractSessionPath(path);
String targetPath = cInfo.localPath;
if (writeTransactional)
{
//upload to temporary location:
String tmpPath = targetPath+".tmp";
String tmpPath = targetPath + ".tmp";
c.put(in, tmpPath);
//remove previous file:
try
@@ -128,9 +190,9 @@ public class SftpStorage extends JavaFileStorageBase {
}
else
{
c.put(in, targetPath);
c.put(in, targetPath);
}
tryDisconnect(c);
} catch (Exception e) {
tryDisconnect(c);
@@ -142,53 +204,98 @@ public class SftpStorage extends JavaFileStorageBase {
@Override
public String createFolder(String parentPath, String newDirName)
throws Exception {
ConnectionInfo cInfo = splitStringToConnectionInfo(parentPath);
try {
ChannelSftp c = init(parentPath);
String newPath = concatPaths(parentPath, newDirName);
c.mkdir(extractSessionPath(newPath));
ChannelSftp c = init(cInfo);
String newPath = concatPaths(cInfo.localPath, newDirName);
c.mkdir(newPath);
tryDisconnect(c);
return newPath;
return buildFullPath(cInfo.host, cInfo.port, newPath,
cInfo.username, cInfo.password, cInfo.connectTimeoutSec,
cInfo.keyName, cInfo.keyPassphrase,
cInfo.configOpts.get(SSH_CFG_KEX),
cInfo.configOpts.get(SSH_CFG_SERVER_HOST_KEY));
} catch (Exception e) {
throw convertException(e);
}
}
private String extractUserPwdHostPort(String path) {
String withoutProtocol = path
.substring(getProtocolPrefix().length());
return withoutProtocol.substring(0, withoutProtocol.indexOf("/"));
}
private String extractSessionPath(String newPath) {
String withoutProtocol = newPath
.substring(getProtocolPrefix().length());
return withoutProtocol.substring(withoutProtocol.indexOf("/"));
int pathStartIdx = withoutProtocol.indexOf("/");
int pathEndIdx = withoutProtocol.indexOf("?");
if (pathEndIdx < 0) {
pathEndIdx = withoutProtocol.length();
}
return withoutProtocol.substring(pathStartIdx, pathEndIdx);
}
private String extractUserPwdHost(String path) {
private Map<String, String> extractOptionsMap(String path) throws UnsupportedEncodingException {
String withoutProtocol = path
.substring(getProtocolPrefix().length());
return withoutProtocol.substring(0,withoutProtocol.indexOf("/"));
Map<String, String> options = new HashMap<>();
int extraOptsIdx = withoutProtocol.indexOf("?");
if (extraOptsIdx > 0 && extraOptsIdx + 1 < withoutProtocol.length()) {
String optsString = withoutProtocol.substring(extraOptsIdx + 1);
String[] parts = optsString.split("&");
for (String p : parts) {
int sepIdx = p.indexOf('=');
if (sepIdx > 0) {
String key = decode(p.substring(0, sepIdx));
String value = decode(p.substring(sepIdx + 1));
options.put(key, value);
} else {
options.put(decode(p), "true");
}
}
}
return options;
}
private String concatPaths(String parentPath, String newDirName) {
String res = parentPath;
if (!res.endsWith("/"))
res += "/";
res += newDirName;
return res;
StringBuilder fp = new StringBuilder(parentPath);
if (!parentPath.endsWith("/"))
fp.append("/");
return fp.append(newDirName).toString();
}
@Override
public String createFilePath(String parentPath, String newFileName)
public String createFilePath(final String parentUri, String newFileName)
throws Exception {
if (parentPath.endsWith("/") == false)
parentPath += "/";
return parentPath + newFileName;
String parentPath = parentUri;
String params = null;
int paramsIdx = parentUri.lastIndexOf("?");
if (paramsIdx > 0) {
params = parentUri.substring(paramsIdx);
parentPath = parentPath.substring(0, paramsIdx);
}
String newPath = concatPaths(parentPath, newFileName);
if (params != null) {
newPath += params;
}
return newPath;
}
@Override
public List<FileEntry> listFiles(String parentPath) throws Exception {
ConnectionInfo cInfo = splitStringToConnectionInfo(parentPath);
ChannelSftp c = init(cInfo);
ChannelSftp c = init(parentPath);
return listFiles(parentPath, c);
}
private void setFromAttrs(FileEntry fileEntry, SftpATTRS attrs) {
@@ -212,23 +319,27 @@ public class SftpStorage extends JavaFileStorageBase {
if (sftpEx.id == ChannelSftp.SSH_FX_NO_SUCH_FILE)
return new FileNotFoundException(sftpEx.getMessage());
}
return e;
}
@Override
public FileEntry getFileEntry(String filename) throws Exception {
ChannelSftp c = init(filename);
ConnectionInfo cInfo = splitStringToConnectionInfo(filename);
ChannelSftp c = init(cInfo);
try {
FileEntry fileEntry = new FileEntry();
String sessionPath = extractSessionPath(filename);
SftpATTRS attr = c.stat(sessionPath);
SftpATTRS attr = c.stat(cInfo.localPath);
setFromAttrs(fileEntry, attr);
// Full URI
fileEntry.path = filename;
fileEntry.displayName = getFilename(sessionPath);
fileEntry.displayName = getFilename(cInfo.localPath);
tryDisconnect(c);
return fileEntry;
} catch (Exception e) {
logDebug("Exception in getFileEntry! " + e);
@@ -239,8 +350,9 @@ public class SftpStorage extends JavaFileStorageBase {
@Override
public void delete(String path) throws Exception {
ConnectionInfo cInfo = splitStringToConnectionInfo(path);
ChannelSftp c = init(cInfo);
ChannelSftp c = init(path);
delete(path, c);
}
@@ -264,10 +376,11 @@ public class SftpStorage extends JavaFileStorageBase {
tryDisconnect(c);
throw convertException(e);
}
}
private List<FileEntry> listFiles(String path, ChannelSftp c) throws Exception {
try {
List<FileEntry> res = new ArrayList<FileEntry>();
@SuppressWarnings("rawtypes")
@@ -283,7 +396,7 @@ public class SftpStorage extends JavaFileStorageBase {
||(lsEntry.getFilename().equals(".."))
)
continue;
FileEntry fileEntry = new FileEntry();
fileEntry.displayName = lsEntry.getFilename();
fileEntry.path = createFilePath(path, fileEntry.displayName);
@@ -313,97 +426,161 @@ public class SftpStorage extends JavaFileStorageBase {
throws UnsupportedEncodingException {
return java.net.URLDecoder.decode(encodedString, UTF_8);
}
@Override
protected String encode(final String unencoded)
throws UnsupportedEncodingException {
return java.net.URLEncoder.encode(unencoded, UTF_8);
}
ChannelSftp init(String filename) throws JSchException, UnsupportedEncodingException {
jsch = new JSch();
ConnectionInfo ci = splitStringToConnectionInfo(filename);
Log.d("KP2AJFS", "init SFTP");
ChannelSftp init(ConnectionInfo cInfo) throws JSchException, UnsupportedEncodingException {
jsch = new JSch();
Log.d(TAG, "init SFTP");
String base_dir = getBaseDir();
jsch.setKnownHosts(base_dir + "/known_hosts");
String key_filename = getKeyFileName();
try{
createKeyPair(key_filename);
} catch (Exception ex) {
System.out.println(ex);
}
String key_filepath = _keyUtils.resolveKeyFilePath(jsch, cInfo.keyName);
try {
jsch.addIdentity(key_filename);
} catch (java.lang.Exception e)
{
jsch.addIdentity(key_filepath);
} catch (java.lang.Exception e) {
}
Log.e("KP2AJFS[thread]", "getting session...");
Session session = jsch.getSession(ci.username, ci.host, ci.port);
Log.e("KP2AJFS", "creating SftpUserInfo");
UserInfo ui = new SftpUserInfo(ci.password,_appContext);
session.setUserInfo(ui);
Log.e(THREAD_TAG, "getting session...");
Session session = jsch.getSession(cInfo.username, cInfo.host, cInfo.port);
session.setConfig("PreferredAuthentications", "publickey,password");
session.connect();
sessionConfigure(session, cInfo);
sessionConnect(session, cInfo);
Channel channel = session.openChannel("sftp");
channel.connect();
ChannelSftp c = (ChannelSftp) channel;
logDebug("success: init Sftp");
return c;
}
private void sessionConnect(Session session, ConnectionInfo cInfo) throws JSchException {
if (cInfo.connectTimeoutSec != UNSET_SFTP_CONNECT_TIMEOUT) {
session.connect(cInfo.connectTimeoutSec * 1000);
} else {
session.connect();
}
}
private void sessionConfigure(Session session, ConnectionInfo cInfo) {
Log.e(TAG, "creating SftpUserInfo");
UserInfo ui = new SftpUserInfo(cInfo.password, cInfo.keyPassphrase, _appContext);
session.setUserInfo(ui);
session.setConfig("PreferredAuthentications", "publickey,password");
for (Map.Entry<String, String> e : cInfo.configOpts.entrySet()) {
String cfgKey = e.getKey();
String before = session.getConfig(cfgKey);
String after = e.getValue();
if (SSH_CFG_CSV_EXPANDABLE.contains(cfgKey)) {
SshConfigCsvValueResolver resolver = new SshConfigCsvValueResolver(cfgKey, after);
after = resolver.resolve(before);
}
session.setConfig(cfgKey, after);
}
}
private String getBaseDir() {
return _appContext.getFilesDir().getAbsolutePath();
}
private String getKeyFileName() {
return getBaseDir() + "/id_kp2a_rsa";
public boolean deleteCustomKey(String keyName) throws FileNotFoundException {
return _keyUtils.deleteCustomKey(keyName);
}
public String[] getCustomKeyNames() {
return _keyUtils.getCustomKeyNames();
}
@SuppressWarnings("unused") // Exposed by JavaFileStorageBindings
public String createKeyPair() throws IOException, JSchException {
return createKeyPair(getKeyFileName());
return _keyUtils.createKeyPair(jsch);
}
private String createKeyPair(String key_filename) throws JSchException, IOException {
String public_key_filename = key_filename + ".pub";
File file = new File(key_filename);
if (file.exists())
return public_key_filename;
int type = KeyPair.RSA;
KeyPair kpair = KeyPair.genKeyPair(jsch, type, 4096);
kpair.writePrivateKey(key_filename);
@SuppressWarnings("unused") // Exposed by JavaFileStorageBindings
public void savePrivateKeyContent(String keyName, String keyContent) throws IOException, Exception {
_keyUtils.savePrivateKeyContent(keyName, keyContent);
}
kpair.writePublicKey(public_key_filename, "generated by Keepass2Android");
//ret = "Fingerprint: " + kpair.getFingerPrint();
kpair.dispose();
return public_key_filename;
@SuppressWarnings("unused") // Exposed by JavaFileStorageBindings
public void setJschLogging(boolean enabled, String logFilename) {
Logger impl = null;
if (enabled) {
if (logFilename != null) {
impl = Kp2aJSchLogger.createFileLogger(logFilename);
} else {
impl = Kp2aJSchLogger.createAndroidLogger();
}
}
JSch.setLogger(impl);
}
/**
* Exposed for testing purposes only.
* @param keyName
* @return
*/
public String sanitizeCustomKeyName(String keyName) {
return _keyUtils.getSanitizedCustomKeyName(keyName);
}
/**
* Exposed for testing purposes only.
* @param keyContent
* @return
* @throws Exception
*/
public String getValidatedCustomKeyContent(String keyContent) throws Exception {
return _keyUtils.getValidatedCustomKeyContent(keyContent);
}
/**
* Exposed for testing purposes only.
* @param currentValues
* @param spec
* @return
* @throws Exception
*/
public String resolveCsvValues(String currentValues, String spec) {
return new SshConfigCsvValueResolver("test", spec)
.resolve(currentValues);
}
public ConnectionInfo splitStringToConnectionInfo(String filename)
throws UnsupportedEncodingException {
ConnectionInfo ci = new ConnectionInfo();
ci.host = extractUserPwdHost(filename);
ci.host = extractUserPwdHostPort(filename);
String userPwd = ci.host.substring(0, ci.host.indexOf('@'));
ci.username = decode(userPwd.substring(0, userPwd.indexOf(":")));
ci.password = decode(userPwd.substring(userPwd.indexOf(":")+1));
int sepIdx = userPwd.indexOf(":");
if (sepIdx > 0) {
ci.username = decode(userPwd.substring(0, sepIdx));
ci.password = decode(userPwd.substring(sepIdx + 1));
} else {
ci.username = userPwd;
ci.password = null;
}
ci.host = ci.host.substring(ci.host.indexOf('@') + 1);
ci.port = DEFAULT_SFTP_PORT;
int portSeparatorIndex = ci.host.lastIndexOf(":");
int portSeparatorIndex = ci.host.lastIndexOf(':');
if (portSeparatorIndex >= 0)
{
ci.port = Integer.parseInt(ci.host.substring(portSeparatorIndex+1));
ci.port = Integer.parseInt(ci.host.substring(portSeparatorIndex + 1));
ci.host = ci.host.substring(0, portSeparatorIndex);
}
// Encode/decode required to support IPv6 (colons break host:port parse logic)
@@ -411,6 +588,30 @@ public class SftpStorage extends JavaFileStorageBase {
ci.host = decode(ci.host);
ci.localPath = extractSessionPath(filename);
Map<String, String> options = extractOptionsMap(filename);
if (options.containsKey(SFTP_CONNECT_TIMEOUT_OPTION_NAME)) {
String optVal = options.get(SFTP_CONNECT_TIMEOUT_OPTION_NAME);
try {
ci.connectTimeoutSec = Integer.parseInt(optVal);
} catch (NumberFormatException nan) {
logDebug(SFTP_CONNECT_TIMEOUT_OPTION_NAME + " option not a number: " + optVal);
}
}
if (options.containsKey(SFTP_KEYNAME_OPTION_NAME)) {
ci.keyName = options.get(SFTP_KEYNAME_OPTION_NAME);
}
if (options.containsKey(SFTP_KEYPASSPHRASE_OPTION_NAME)) {
ci.keyPassphrase = options.get(SFTP_KEYPASSPHRASE_OPTION_NAME);
}
for (String cfgKey : SSH_CFG_CSV_EXPANDABLE) {
if (options.containsKey(cfgKey)) {
ci.configOpts.put(cfgKey, options.get(cfgKey));
}
}
return ci;
}
@@ -447,12 +648,18 @@ public class SftpStorage extends JavaFileStorageBase {
try
{
ConnectionInfo ci = splitStringToConnectionInfo(path);
return getProtocolPrefix()+ci.username+"@"+ci.host+ci.localPath;
StringBuilder dName = new StringBuilder(getProtocolPrefix())
.append(ci.username)
.append("@")
.append(ci.host)
.append(ci.localPath);
appendOptions(dName, buildOptionMap(ci, false));
return dName.toString();
}
catch (Exception e)
{
return extractSessionPath(path);
}
}
}
@Override
@@ -474,26 +681,105 @@ public class SftpStorage extends JavaFileStorageBase {
@Override
public void onActivityResult(FileStorageSetupActivity activity,
int requestCode, int resultCode, Intent data) {
}
public String buildFullPath( String host, int port, String localPath, String username, String password) throws UnsupportedEncodingException
{
public String buildFullPath(String host, int port, String localPath,
String username, String password,
int connectTimeoutSec,
String keyName, String keyPassphrase,
String kexAlgorithms, String shkAlgorithms)
throws UnsupportedEncodingException {
StringBuilder uri = new StringBuilder(getProtocolPrefix()).append(encode(username));
if (password != null) {
uri.append(":").append(encode(password));
}
uri.append("@");
// Encode/decode required to support IPv6 (colons break host:port parse logic)
// See Bug #2350
host = encode(host);
uri.append(encode(host));
if (port != DEFAULT_SFTP_PORT)
host += ":"+String.valueOf(port);
return getProtocolPrefix()+encode(username)+":"+encode(password)+"@"+host+localPath;
if (port != DEFAULT_SFTP_PORT) {
uri.append(":").append(port);
}
if (localPath != null && localPath.startsWith("/")) {
uri.append(localPath);
}
appendOptions(uri, new OptionMapBuilder()
.addOption(SFTP_CONNECT_TIMEOUT_OPTION_NAME, connectTimeoutSec, cTimeoutResolver)
.addOption(SFTP_KEYNAME_OPTION_NAME, keyName, nonBlankStringResolver)
.addOption(SFTP_KEYPASSPHRASE_OPTION_NAME, keyPassphrase, nonBlankStringResolver)
.addOption(SSH_CFG_KEX, kexAlgorithms, nonBlankStringResolver)
.addOption(SSH_CFG_SERVER_HOST_KEY, shkAlgorithms, nonBlankStringResolver)
.build());
return uri.toString();
}
private void appendOptions(StringBuilder uri, Map<String, String> opts)
throws UnsupportedEncodingException {
boolean first = true;
// Sort for stability/consistency
Set<Map.Entry<String, String>> sortedEntries = new TreeSet<>(new EntryComparator<>());
sortedEntries.addAll(opts.entrySet());
for (Map.Entry<String, String> me : sortedEntries) {
if (first) {
uri.append("?");
first = false;
} else {
uri.append("&");
}
uri.append(encode(me.getKey())).append("=").append(encode(me.getValue()));
}
}
@Override
public void prepareFileUsage(Context appContext, String path) {
//nothing to do
}
/**
* A comparator that compares Map.Entry objects by their keys, via natural ordering.
*
* @param <T> the Map.Entry key type, that must implement Comparable.
*/
private static class EntryComparator<T extends Comparable<T>> implements Comparator<Map.Entry<T, ?>> {
@Override
public int compare(Map.Entry<T, ?> o1, Map.Entry<T, ?> o2) {
return o1.getKey().compareTo(o2.getKey());
}
}
private static class OptionMapBuilder {
private final Map<String, String> options = new HashMap<>();
/**
* Attempts to add a raw value <code>oVal</code> to the underlying option map with key <code>oName</code>
* iff the <code>resolver</code> produces a non-null output when invoked using the raw value.
*
* @param oName the name/key associated with the value, if added
* @param oVal the raw value attempting to be added
* @param resolver the resolver that determines if the value will be added
*
* @return OptionMapBuilder (updated)
* @param <T> the raw value type
*/
<T> OptionMapBuilder addOption(final String oName, T oVal, ValueResolver<T> resolver) {
String resolved = resolver.resolve(oVal);
if (resolved != null) {
options.put(oName, resolved);
}
return this;
}
Map<String, String> build() {
return new HashMap<>(options);
}
}
}

View File

@@ -116,17 +116,19 @@ public class SftpUserInfo implements UserInfo {
Context _appContext;
String _password;
String _passphrase;
public SftpUserInfo(String password, Context appContext)
public SftpUserInfo(String password, String passphrase, Context appContext)
{
_password = password;
_passphrase = passphrase;
_appContext = appContext;
}
@Override
public String getPassphrase() {
return null;
return _passphrase;
}
@Override
@@ -137,12 +139,12 @@ public class SftpUserInfo implements UserInfo {
@Override
public boolean promptPassword(String message) {
return true;
return _password != null;
}
@Override
public boolean promptPassphrase(String message) {
return false; //passphrase not supported
return _passphrase != null;
}
@Override

View File

@@ -0,0 +1,178 @@
package keepass2android.javafilestorage;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A class that manipulates CSV String values based on a list of CSV "spec" definitions, where each definition
* can describe one of the following:
*
* - Prepend to existing list: +something
* - Append to end of existing list: something+
* - Remove a specific value: -something
* - Remove values matching prefix: -something*
* - Remove values matching suffix: -*something
* - Remove values matching substring: -*something*
* - Remove values matching prefix and suffix: -some*thing
*
* Otherwise CSV of values completely replace original config values
*
* Examples:
* <code>
* var r = new SshConfigCsvValueResolver("foo", "addToEnd+,-remove*,+addToBeginning,-*del*");
* r.resolve("one,removeTwo,three,removeThree,four") --> "addToBeginning,one,three,four,addToEnd"
* r.resolve("one,my-del,del-me,two,foodelbar,three") --> "addToBeginning,one,two,three,addToEnd"
*
* r = new SshConfigCsvValueResolver("foo", "replace,the,config");
* r.resolve("one,two,three,four") --> "replace,the,config"
* </code>
*
*/
class SshConfigCsvValueResolver {
interface Matcher {
boolean matches(String s);
}
private final String cfgKey;
private static final String TAG = "KP2AJFS[sshcfg]";
private static final String DELIM = ",";
private static final char ADD = '+';
private static final char REMOVE = '-';
private static final char WILD = '*';
private final List<String> prepends;
private final List<String> appends;
private final List<Matcher> removes;
private final List<String> replaces;
/**
* Creates a new resolver.
*
* @param cfgKey - configuration key name (used for logging)
* @param incomingSpec - A CSV String of "spec" definitions that will be used to
* (potentially) modify incoming CSV String values.
*/
SshConfigCsvValueResolver(String cfgKey, String incomingSpec) {
List<String> prepends = new ArrayList<>();
List<String> appends = new ArrayList<>();
List<Matcher> removes = new ArrayList<>();
List<String> replaces = new ArrayList<>();
for (String iVal : incomingSpec.split(DELIM)) {
if (iVal.isBlank()) {
continue;
}
int evLen = iVal.length();
if (iVal.charAt(0) == ADD && evLen > 1) {
prepends.add(iVal.substring(1));
} else if (iVal.charAt(iVal.length() - 1) == ADD && evLen > 1) {
appends.add(iVal.substring(0, evLen - 1));
} else if (iVal.charAt(iVal.length() - 1) == REMOVE && evLen > 1) {
removes.add(createMatcher(iVal.substring(1)));
} else {
// This looks like a straight replace
replaces.add(iVal);
}
}
this.cfgKey = cfgKey;
this.prepends = Collections.unmodifiableList(prepends);
this.appends = Collections.unmodifiableList(appends);
this.removes = Collections.unmodifiableList(removes);
this.replaces = Collections.unmodifiableList(replaces);
}
/**
* Takes a CSV String and (potentially) modifies it according to the "spec" entries of this resolver.
*
* @param existingValues - the original CSV String
* @return an updated representation of <code>existingValues</code>, based on the defined "spec"
* entries of this resolver.
*/
public String resolve(String existingValues) {
List<String> newValues;
// If there's even one replace, it wins over everything and the rest is thrown out
if (!replaces.isEmpty()) {
if (!(prepends.isEmpty() || appends.isEmpty() || removes.isEmpty())) {
Log.w(TAG, "Discarded SSH cfg parts: key=" + cfgKey +
", prepends=" + prepends + ", appends=" + appends +
", removes=" + removes);
}
newValues = replaces;
} else {
// Otherwise we rebuild from existing and incoming values
newValues = createResolvedValues(existingValues);
}
return String.join(DELIM, newValues);
}
private List<String> createResolvedValues(String existingValues) {
List<String> newValues = new ArrayList<>(prepends);
for (String a : existingValues.split(DELIM)) {
if (!shouldRemove(a)) {
newValues.add(a);
}
}
newValues.addAll(appends);
return newValues;
}
private boolean shouldRemove(String s) {
s = normalize(s);
for (Matcher m : removes) {
if (m.matches(s)) {
return true;
}
}
return false;
}
private Matcher createMatcher(String val) {
final String v = normalize(val);
Matcher impl = s -> v.equals(s);
int wildcardIdx = v.indexOf(WILD);
if (wildcardIdx < 0) {
return impl;
}
// *blah *blah* blah* some*thing
// endsWith substring startsWith startsWith && endsWith
String subStr = null;
String suffix = null;
String prefix = null;
int vLen = v.length();
if (v.charAt(0) == WILD && vLen > 1) {
if (vLen > 2 && v.charAt(vLen - 1) == WILD) {
//substring
subStr = v.substring(1, vLen - 1);
} else {
// endsWith
suffix = v.substring(1);
}
} else if (v.charAt(vLen - 1) == WILD && vLen > 1) {
// beginsWith
prefix = v.substring(0, v.length() - 1);
} else if (wildcardIdx > 0) {
// startsWith && endsWith
prefix = v.substring(0, wildcardIdx);
suffix = v.substring(wildcardIdx + 1);
}
if (subStr != null) {
final String sub = subStr;
impl = s -> s.contains(sub);
} else if (prefix != null || suffix != null) {
final String pre = prefix;
final String suf = suffix;
impl = s -> (pre == null || s.startsWith(pre)) && (suf == null || s.endsWith(suf));
}
return impl;
}
private static String normalize(String s) {
return s == null ? null : s.toLowerCase();
}
}

View File

@@ -148,6 +148,7 @@ import java.util.List;
import keepass2android.javafilestorage.GoogleDriveAppDataFileStorage;
import keepass2android.javafilestorage.JavaFileStorage;
import keepass2android.javafilestorage.JavaFileStorage.FileEntry;
import keepass2android.javafilestorage.PCloudFileStorage;
import keepass2android.javafilestorage.SftpStorage;
import keepass2android.javafilestorage.UserInteractionRequiredException;
import keepass2android.javafilestorage.WebDavStorage;
@@ -539,11 +540,11 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
static JavaFileStorage createStorageToTest(Context ctx, Context appContext, boolean simulateRestart) {
//storageToTest = new SftpStorage(ctx.getApplicationContext());
//storageToTest = new PCloudFileStorage(ctx, "yCeH59Ffgtm");
storageToTest = new PCloudFileStorage(ctx, "FLm22de7bdS", "pcloud", "pcloudtest");
//storageToTest = new SkyDriveFileStorage("000000004010C234", appContext);
storageToTest = new GoogleDriveAppDataFileStorage();
//storageToTest = new GoogleDriveAppDataFileStorage();
/*storageToTest = new WebDavStorage(new ICertificateErrorHandler() {
@Override
public boolean onValidationError(String error) {
@@ -690,6 +691,13 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
return sb.toString();
}
private void populateCsvMockValues(View view) {
EditText etSpecs = view.findViewById(R.id.mock_csv_specs);
etSpecs.setText("-bar,+first,-*d*");
EditText etCfgs = view.findViewById(R.id.mock_csv_cfg);
etCfgs.setText("foo,del1,bar,del2");
}
@Override
public void performManualFileSelect(boolean isForSave, final int requestCode,
String protocolId)
@@ -697,12 +705,13 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
if (protocolId.equals("sftp"))
{
final View view = getLayoutInflater().inflate(R.layout.sftp_credentials, null);
final SftpStorage sftpStorage = (SftpStorage)storageToTest;
populateCsvMockValues(view);
view.findViewById(R.id.send_public_key).setOnClickListener(v -> {
Intent sendIntent = new Intent();
SftpStorage sftpStorage = (SftpStorage)storageToTest;
try {
String pub_filename = sftpStorage.createKeyPair();
@@ -715,39 +724,140 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
}
catch (Exception ex)
{
Toast.makeText(this,"Failed to create key pair: " + ex.getMessage(), Toast.LENGTH_LONG);
return;
Toast.makeText(this,"Failed to create key pair: " + ex.getMessage(), Toast.LENGTH_LONG).show();
}
});
view.findViewById(R.id.list_private_keys).setOnClickListener(v -> {
String[] keys = sftpStorage.getCustomKeyNames();
Toast.makeText(this, "keys: " + String.join(",", keys), Toast.LENGTH_LONG).show();
});
view.findViewById(R.id.add_private_key).setOnClickListener(v -> {
EditText etKeyName = view.findViewById(R.id.private_key_name);
String keyName = etKeyName.getText().toString();
EditText etKeyContent = view.findViewById(R.id.private_key_content);
String keyContent = etKeyContent.getText().toString();
try {
sftpStorage.savePrivateKeyContent(keyName, keyContent);
Toast.makeText(this, "Add successful", Toast.LENGTH_LONG).show();
}
catch (Exception e) {
Toast.makeText(this, "Add failed: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
});
view.findViewById(R.id.delete_private_key).setOnClickListener(v -> {
EditText etKeyName = view.findViewById(R.id.private_key_name);
String keyName = etKeyName.getText().toString();
String exMessage = null;
boolean success = false;
try {
success = sftpStorage.deleteCustomKey(keyName);
}
catch (Exception e) {
exMessage = e.getMessage();
}
StringBuilder msg = new StringBuilder("Delete ");
msg.append(success ? "succeeded" : "FAILED");
if (exMessage != null) {
msg.append(" (").append(exMessage).append(")");
}
Toast.makeText(this, msg.toString(), Toast.LENGTH_LONG).show();
});
view.findViewById(R.id.validate_private_key).setOnClickListener(v -> {
EditText etKeyName = view.findViewById(R.id.private_key_name);
String inKeyName = etKeyName.getText().toString();
if (!inKeyName.isEmpty()) {
String keyResponse;
try {
keyResponse = sftpStorage.sanitizeCustomKeyName(inKeyName);
} catch (Exception e) {
keyResponse = "EX:" + e.getMessage();
}
String msg = "key: [" + inKeyName + "] -> [" + keyResponse + "]";
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
EditText etKeyContent = view.findViewById(R.id.private_key_content);
String inKeyContent = etKeyContent.getText().toString();
String msg;
if (!inKeyContent.isEmpty()) {
try {
// We could print the key, but I don't it's that helpful
sftpStorage.getValidatedCustomKeyContent(inKeyContent);
msg = "Key content is valid";
} catch (Exception e) {
msg = "Invalid key content: " + e.getMessage();
}
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
});
view.findViewById(R.id.resolve_mock_csv).setOnClickListener(v -> {
EditText etSpecs = view.findViewById(R.id.mock_csv_specs);
String specs = etSpecs.getText().toString();
EditText etCfg = view.findViewById(R.id.mock_csv_cfg);
String cfg = etCfg.getText().toString();
if (!specs.isBlank() && !cfg.isBlank()) {
String result = sftpStorage.resolveCsvValues(cfg, specs);
Toast.makeText(this, result, Toast.LENGTH_LONG).show();
}
});
view.findViewById(R.id.reset_mock_csv).setOnClickListener(v -> {
populateCsvMockValues(view);
});
new AlertDialog.Builder(this)
.setView(view)
.setTitle("Enter SFTP credentials")
.setPositiveButton("OK",new DialogInterface.OnClickListener() {
.setPositiveButton("OK", (dialog, which) -> {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "Hey", Toast.LENGTH_LONG).show();
Toast.makeText(MainActivity.this, "Hey", Toast.LENGTH_LONG).show();
SftpStorage sftpStorage = (SftpStorage)storageToTest;
try {
EditText etHost = ((EditText)view.findViewById(R.id.sftp_host));
String host = etHost.getText().toString();
EditText etUser = ((EditText)view.findViewById(R.id.sftp_user));
String user = etUser.getText().toString();
EditText etPwd = ((EditText)view.findViewById(R.id.sftp_password));
String pwd = etPwd.getText().toString();
EditText etPort = ((EditText)view.findViewById(R.id.sftp_port));
int port = Integer.parseInt(etPort.getText().toString());
EditText etInitDir = ((EditText)view.findViewById(R.id.sftp_initial_dir));
String initialDir = etInitDir.getText().toString();
onReceivePathForFileSelect(requestCode, sftpStorage.buildFullPath( host, port, initialDir, user, pwd));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
SftpStorage sftpStorage1 = (SftpStorage)storageToTest;
try {
EditText etHost = view.findViewById(R.id.sftp_host);
String host = etHost.getText().toString();
EditText etUser = view.findViewById(R.id.sftp_user);
String user = etUser.getText().toString();
EditText etPwd = view.findViewById(R.id.sftp_password);
String pwd = etPwd.getText().toString();
EditText etPort = view.findViewById(R.id.sftp_port);
int port = Integer.parseInt(etPort.getText().toString());
EditText etInitDir = view.findViewById(R.id.sftp_initial_dir);
String initialDir = etInitDir.getText().toString();
EditText etConnectTimeout = view.findViewById(R.id.sftp_connect_timeout);
int connectTimeout = SftpStorage.UNSET_SFTP_CONNECT_TIMEOUT;
String ctStr = etConnectTimeout.getText().toString();
if (!ctStr.isEmpty()) {
try {
int ct = Integer.parseInt(ctStr);
if (connectTimeout != ct) {
connectTimeout = ct;
}
} catch (NumberFormatException parseEx) {
}
}
EditText etKeyName = view.findViewById(R.id.private_key_name);
String keyName = etKeyName.getText().toString();
EditText etKeyPassphrase = view.findViewById(R.id.private_key_passphrase);
String keyPassphrase = etKeyPassphrase.getText().toString();
EditText etKex = view.findViewById(R.id.kex);
String kex = etKex.getText().toString();
EditText etShk = view.findViewById(R.id.shk);
String shk = etShk.getText().toString();
onReceivePathForFileSelect(requestCode, sftpStorage1.buildFullPath(
host, port, initialDir, user, pwd, connectTimeout,
keyName, keyPassphrase, kex, shk));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
})
.create()

View File

@@ -3,69 +3,217 @@
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="12dip"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText
android:layout_margin="12dip">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/sftp_host"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="10"
android:singleLine="true"
android:inputType="textNoSuggestions"
android:text=""
android:hint="@string/hint_sftp_host" />
<TextView
android:hint="@string/hint_sftp_host" />
<TextView
android:id="@+id/portsep"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=":" />
<EditText
<EditText
android:id="@+id/sftp_port"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="15"
android:singleLine="true"
android:inputType="number"
android:text="22"
android:hint="@string/hint_sftp_port" />
</LinearLayout>
<EditText
android:inputType="number"
android:text="22"
android:hint="@string/hint_sftp_port" />
<EditText
android:id="@+id/sftp_connect_timeout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="14"
android:singleLine="true"
android:inputType="number"
android:text=""
android:hint="@string/hint_sftp_connect_timeout" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/sftp_user"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:text=""
android:hint="@string/hint_username" />
<EditText
android:hint="@string/hint_username" />
<EditText
android:id="@+id/sftp_password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textPassword"
android:singleLine="true"
android:text=""
android:hint="@string/hint_pass"
android:importantForAccessibility="no" />
<TextView android:id="@+id/initial_dir"
android:hint="@string/hint_pass"
android:importantForAccessibility="no" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/initial_dir"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dip"
android:layout_marginTop="4dip"
android:text="@string/initial_directory" />
<EditText
<EditText
android:id="@+id/sftp_initial_dir"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dip"
android:singleLine="true"
android:text="/home/philipp"
/>
<Button android:id="@+id/send_public_key"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="send public key" />
android:text="/home/philipp"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText android:id="@+id/kex"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:inputType="textNoSuggestions"
android:text=""
android:hint="KEX Algs" />
<EditText android:id="@+id/shk"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:inputType="textNoSuggestions"
android:text=""
android:hint="Server Host Key Algs" />
</LinearLayout>
<Button android:id="@+id/send_public_key"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="send public key" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_marginTop="15dp"
android:textStyle="bold"
android:text="Private Keys Functions" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button android:id="@+id/list_private_keys"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="List" />
<Button android:id="@+id/add_private_key"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Add" />
<Button android:id="@+id/delete_private_key"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Delete" />
<Button android:id="@+id/validate_private_key"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Validate" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText android:id="@+id/private_key_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:inputType="textNoSuggestions"
android:text=""
android:hint="key name" />
<EditText android:id="@+id/private_key_passphrase"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:inputType="textNoSuggestions"
android:text=""
android:hint="passphrase (optional)" />
</LinearLayout>
<EditText android:id="@+id/private_key_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:lines="4"
android:text=""
android:hint="key content" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_marginTop="15dp"
android:textStyle="bold"
android:text="CSV Resolver Functions" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText android:id="@+id/mock_csv_specs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:inputType="textNoSuggestions"
android:text=""
android:hint="Test specs" />
<EditText android:id="@+id/mock_csv_cfg"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:inputType="textNoSuggestions"
android:text=""
android:hint="Test config" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button android:id="@+id/reset_mock_csv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="50dp"
android:layout_marginRight="5dp"
android:text="Reset" />
<Button android:id="@+id/resolve_mock_csv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="50dp"
android:layout_marginLeft="5dp"
android:text="Resolve" />
</LinearLayout>
</LinearLayout>

View File

@@ -333,6 +333,7 @@
<string name="hint_sftp_host">host</string>
<string name="hint_sftp_port">port</string>
<string name="hint_sftp_connect_timeout">timeout sec</string>
<string name="select_storage_type">Select the storage type:</string>
@@ -524,6 +525,6 @@ Initial public release
<item>Do not accept invalid certificates</item>
</string-array>
<string name="initial_directory">Initial directory (optional):</string>
<string name="initial_directory">Initial dir (optional):</string>
</resources>

View File

@@ -221,7 +221,6 @@ public class FileChooserActivity extends FragmentActivity {
public static final String EXTRA_RESULT_FILE_EXISTS = CLASSNAME + ".result_file_exists";
/*
* CONTROLS

View File

@@ -269,7 +269,6 @@ public class FragmentFiles extends Fragment implements
FileChooserActivity.EXTRA_MAX_FILE_COUNT, 1000);
mFileAdapter = new BaseFileAdapter(getActivity(), mFilterMode,
mIsMultiSelection);
/*
* History.
@@ -2268,12 +2267,15 @@ public class FragmentFiles extends Fragment implements
}
if (mIsSaveDialog) {
mTextSaveas.setText(BaseFileProviderUtils.getFileName(cursor));
String fileName = BaseFileProviderUtils.getFileName(cursor);
Uri uri = BaseFileProviderUtils.getUri(cursor);
mTextSaveas.setText(fileName);
/*
* Always set tag after setting text, or tag will be reset to
* null.
*/
mTextSaveas.setTag(BaseFileProviderUtils.getUri(cursor));
mTextSaveas.setTag(uri);
}
if (mDoubleTapToChooseFiles) {
@@ -2286,10 +2288,12 @@ public class FragmentFiles extends Fragment implements
if (mIsMultiSelection)
return;
if (mIsSaveDialog)
if (mIsSaveDialog) {
checkSaveasFilenameAndFinish();
else
}
else {
finish(BaseFileProviderUtils.getUri(cursor));
}
}// single tap to choose files
}// onItemClick()
};// mViewFilesOnItemClickListener

View File

@@ -15,4 +15,24 @@ public class FileEntry {
isDirectory = false;
canRead = canWrite = true;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder("kp2afilechooser.FileEntry{")
.append(displayName).append("|")
.append("path=").append(path).append(",sz=").append(sizeInBytes)
.append(",").append(isDirectory ? "dir" : "file")
.append(",lastMod=").append(lastModifiedTime);
StringBuilder perms = new StringBuilder();
if (canRead)
perms.append("r");
if (canWrite)
perms.append("w");
if (perms.length() > 0) {
s.append(",").append(perms);
}
return s.append("}").toString();
}
}

View File

@@ -20,6 +20,7 @@ public class Kp2aFileChooserBridge {
.buildUpon()
.appendPath(defaultPath)
.build());
return intent;
}
}

View File

@@ -306,10 +306,9 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
String parentPath = getParentPath(path);
if (parentPath == null)
{
if (parentPath == null) {
if (Utils.doLog())
Log.d(CLASSNAME, "parent file is null");
Log.d(CLASSNAME, "parent file is null");
return null;
}
FileEntry e;
@@ -501,10 +500,10 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
RowBuilder newRow = matrixCursor.newRow();
newRow.add(id);// _ID
newRow.add(BaseFile
.genContentIdUriBase(
getAuthority())
.buildUpon().appendPath(f.path)
.build().toString());
.genContentIdUriBase(
getAuthority())
.buildUpon().appendPath(f.path)
.build().toString());
newRow.add(f.path);
if (f.displayName == null)
Log.w("KP2AJ", "displayName is null for " + f.path);
@@ -549,7 +548,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
//puts the file entry in the cache for later reuse with retrieveFileInfo
private void updateFileEntryCache(FileEntry f) {
if (f != null)
fileEntryMap.put(f.path, f);
fileEntryMap.put(f.path, f);
}
//removes the file entry from the cache (if cached). Should be called whenever the file changes
private void removeFromCache(String filename, boolean recursive) {
@@ -584,7 +583,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
//returns the file entry from the cache if present or queries the concrete provider method to return the file info
private FileEntry getFileEntryCached(String filename) {
//check if enry is cached:
//check if entry is cached:
FileEntry cachedEntry = fileEntryMap.get(filename);
if (cachedEntry != null)
{
@@ -728,7 +727,7 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
if (targetParent != null && targetParent.startsWith(source))
{
if (Utils.doLog())
Log.d("KP2A_FC_P", source+" is parent of "+target);
Log.d("KP2A_FC_P", source + " is parent of " + target);
return BaseFileProviderUtils.newClosedCursor();
}
if (Utils.doLog())
@@ -768,28 +767,37 @@ public abstract class Kp2aFileProvider extends BaseFileProvider {
private String getParentPath(String path)
{
path = removeTrailingSlash(path);
if (path.indexOf("://") == -1)
{
Log.d("KP2A_FC_P", "invalid path: " + path);
return null;
}
String pathWithoutProtocol = path.substring(path.indexOf("://")+3);
int lastSlashPos = path.lastIndexOf("/");
if (pathWithoutProtocol.indexOf("/") == -1)
{
Log.d("KP2A_FC_P", "parent of " + path +" is null");
return null;
}
else
{
String parent = path.substring(0, lastSlashPos)+"/";
Log.d("KP2A_FC_P", "parent of " + path +" is "+parent);
return parent;
}
String params = null;
int paramsIdx = path.lastIndexOf("?");
if (paramsIdx > 0) {
params = path.substring(paramsIdx);
path = path.substring(0, paramsIdx);
}
path = removeTrailingSlash(path);
if (path.indexOf("://") == -1)
{
Log.d("KP2A_FC_P", "invalid path: " + path);
return null;
}
String pathWithoutProtocol = path.substring(path.indexOf("://") + 3);
int lastSlashPos = path.lastIndexOf("/");
if (pathWithoutProtocol.indexOf("/") == -1)
{
Log.d("KP2A_FC_P", "parent of " + path + " is null");
return null;
}
else
{
String parent = path.substring(0, lastSlashPos) + "/";
if (params != null) {
parent += params;
}
Log.d("KP2A_FC_P", "parent of " + path +" is " + parent);
return parent;
}
}
protected abstract FileEntry getFileEntry(String path, StringBuilder errorMessageBuilder) throws Exception;

View File

@@ -33,11 +33,11 @@
<string name="afc_msg_failed_please_try_again">Mislykkedes. Forsøg igen.</string>
<string name="afc_msg_loading">Indlæser…</string>
<string name="afc_phone">Telefon</string>
<string name="afc_pmsg_cannot_access_dir">Kan ikke til \"%1$s\"</string>
<string name="afc_pmsg_cannot_access_dir">Kan ikke få adgang til \"%1$s\"</string>
<string name="afc_pmsg_cannot_create_folder">Kan ikke oprette mappen \"%1$s\"</string>
<string name="afc_pmsg_cannot_delete_file">Kan ikke slette %1$s \"%2$s\"</string>
<string name="afc_pmsg_confirm_delete_file">Sikker på, at du vil slette denne %1$s \"%2$s\"?</string>
<string name="afc_pmsg_confirm_replace_file">Denne fil \"%1$s\" findes allerede.\n\nSkal den erstattes?</string>
<string name="afc_pmsg_confirm_delete_file">Er du sikker på at du vil slette denne %1$s \"%2$s\"?</string>
<string name="afc_pmsg_confirm_replace_file">Filen \"%1$s\" eksisterer allerede.\n\nØnsker du at overskrive den?</string>
<string name="afc_pmsg_deleting_file">Sletter %1$s \"%2$s\"…</string>
<string name="afc_pmsg_file_has_been_deleted">%1$s \"%2$s\" er slettet</string>
<string name="afc_pmsg_filename_is_directory">\"%1$s\" er en mappe</string>
@@ -53,7 +53,7 @@
<string name="afc_title_name">Navn</string>
<string name="afc_title_save_as">Gem som…</string>
<string name="afc_title_size">Størrelse</string>
<string name="afc_title_sort_by">Sortér efter…</string>
<string name="afc_title_sort_by">Sorter efter…</string>
<string name="afc_yesterday">I går</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">Vælg mappe&#8230;</item>

View File

@@ -34,13 +34,13 @@
<string name="afc_msg_loading">Lade…</string>
<string name="afc_phone">Gerät</string>
<string name="afc_pmsg_cannot_access_dir">Kann nicht auf \"%1$s\" zugreifen</string>
<string name="afc_pmsg_cannot_create_folder">Kann Verzeichnis \"%1$s\" nicht anlegen</string>
<string name="afc_pmsg_cannot_create_folder">Ordner „%1$s“ kann nicht erstellt werden</string>
<string name="afc_pmsg_cannot_delete_file">Kann %1$s \"%2$s\" nicht löschen</string>
<string name="afc_pmsg_confirm_delete_file">Bist du sicher, dass du \"%2$s\" (%1$s) löschen möchtest?</string>
<string name="afc_pmsg_confirm_replace_file">Die Datei \"%1$s\" existiert bereits.\n\nSoll sie ersetzt werden?</string>
<string name="afc_pmsg_confirm_replace_file">Die Datei %1$s existiert bereits.\n\nSoll sie ersetzt werden?</string>
<string name="afc_pmsg_deleting_file">Lösche %1$s \"%2$s\"…</string>
<string name="afc_pmsg_file_has_been_deleted">%1$s \"%2$s\" wurde gelöscht</string>
<string name="afc_pmsg_filename_is_directory">\"%1$s\" ist ein Verzeichnis</string>
<string name="afc_pmsg_filename_is_directory">\"%1$s\" ist ein Ordner</string>
<string name="afc_pmsg_filename_is_invalid">Dateiname \"%1$s\" ist ungültig</string>
<string name="afc_pmsg_max_file_count_allowed">… hat mehr Dateien, maximal erlaubt: %1$d</string>
<string name="afc_pmsg_unknown_error">Unbekannter Fehler: %1$s</string>
@@ -60,7 +60,7 @@
<item quantity="other">Verzeichnisse wählen&#8230;</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="one">Datei wählen&#8230;</item>
<item quantity="one">Datei wählen</item>
<item quantity="other">Dateien wählen …</item>
</plurals>
<plurals name="afc_title_choose_files_directories">

View File

@@ -7,7 +7,7 @@
-->
<resources>
<string name="afc_cmd_advanced_selection_all">Όλα</string>
<string name="afc_cmd_advanced_selection_invert">Αντιστροφή επιλογής</string>
<string name="afc_cmd_advanced_selection_invert">Αναστροφή επιλογής</string>
<string name="afc_cmd_advanced_selection_none">Κανένα</string>
<string name="afc_cmd_grid_view">Προβολή πλέγματος</string>
<string name="afc_cmd_home">Κεντρική</string>

View File

@@ -39,12 +39,12 @@
<string name="afc_pmsg_confirm_delete_file">確定刪除 %1$s \"%2$s\"?</string>
<string name="afc_pmsg_confirm_replace_file">此檔 \"%1$s\" 已經存在。\n\n是否要覆蓋</string>
<string name="afc_pmsg_deleting_file">正在刪除 %1$s「%2$s」…</string>
<string name="afc_pmsg_file_has_been_deleted">%1$s\"%2$s\"已被刪除</string>
<string name="afc_pmsg_filename_is_directory">\"%1$s\"是一個資料夾</string>
<string name="afc_pmsg_filename_is_invalid">檔案名稱\"%1$s\"不正確</string>
<string name="afc_pmsg_file_has_been_deleted">已刪除 %1$s%2$s</string>
<string name="afc_pmsg_filename_is_directory">%1$s」是資料夾</string>
<string name="afc_pmsg_filename_is_invalid">檔案名稱%1$s」無效</string>
<string name="afc_pmsg_max_file_count_allowed">…有更多檔案,最多允許:%1$,d</string>
<string name="afc_pmsg_unknown_error">未知錯誤: %1$s</string>
<string name="afc_root">Root</string>
<string name="afc_pmsg_unknown_error">未知錯誤%1$s</string>
<string name="afc_root">根目錄</string>
<string name="afc_title_advanced_selection">請選擇...</string>
<string name="afc_title_confirmation">確認</string>
<string name="afc_title_date">日期</string>

View File

@@ -48,6 +48,8 @@ using KeePassLib.Serialization;
using PluginTOTP;
using File = Java.IO.File;
using Uri = Android.Net.Uri;
using keepass2android.fileselect;
using Boolean = Java.Lang.Boolean;
namespace keepass2android
{
@@ -554,21 +556,90 @@ namespace keepass2android
}
}
internal void StartNotificationsService(bool activateKeyboard)
{
Intent showNotIntent = new Intent(this, typeof (CopyToClipboardService));
showNotIntent.SetAction(Intents.ShowNotification);
showNotIntent.PutExtra(KeyEntry, new ElementAndDatabaseId(App.Kp2a.CurrentDb, Entry).FullId);
AppTask.PopulatePasswordAccessServiceIntent(showNotIntent);
showNotIntent.PutExtra(KeyActivateKeyboard, activateKeyboard);
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
if (permissions.Length == 1 && permissions.First() == Android.Manifest.Permission.PostNotifications &&
grantResults.First() == Permission.Granted)
{
StartNotificationsServiceAfterPermissionsCheck(requestCode == 1 /*requestCode is used to transfer this flag*/);
}
StartService(showNotIntent);
}
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
internal void StartNotificationsService(bool activateKeyboard)
{
if (PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(
GetString(Resource.String.CopyToClipboardNotification_key),
Resources.GetBoolean(Resource.Boolean.CopyToClipboardNotification_default)) == false
&& PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(
GetString(Resource.String.UseKp2aKeyboard_key),
Resources.GetBoolean(Resource.Boolean.UseKp2aKeyboard_default)) == false)
{
//notifications are disabled
return;
}
if ((int)Build.VERSION.SdkInt < 33 || CheckSelfPermission(Android.Manifest.Permission.PostNotifications) ==
Permission.Granted)
{
StartNotificationsServiceAfterPermissionsCheck(activateKeyboard);
return;
}
//user has not yet granted Android 13's POST_NOTIFICATONS permission for the app.
//check if we should ask them to grant:
if (!ShouldShowRequestPermissionRationale(Android.Manifest.Permission.PostNotifications) //this menthod returns false if we haven't asked yet or if the user has denied permission too often
&& PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean("RequestedPostNotificationsPermission", false))//use a preference to tell the difference between "haven't asked yet" and "have asked too often"
{
//user has denied permission before. Do not show the dialog. User must give permission in the Android App settings.
return;
}
new AlertDialog.Builder(this)
.SetTitle(Resource.String.post_notifications_dialog_title)
.SetMessage(Resource.String.post_notifications_dialog_message)
.SetNegativeButton(Resource.String.post_notifications_dialog_disable, (sender, args) =>
{
//disable this dialog for the future by disabling the notification preferences
var edit= PreferenceManager.GetDefaultSharedPreferences(this).Edit();
edit.PutBoolean(GetString(Resource.String.CopyToClipboardNotification_key), false);
edit.PutBoolean(GetString(Resource.String.UseKp2aKeyboard_key), false);
edit.Commit();
})
.SetPositiveButton(Resource.String.post_notifications_dialog_allow, (sender, args) =>
{
//remember that we did ask for permission at least once:
var edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
edit.PutBoolean("RequestedPostNotificationsPermission", true);
edit.Commit();
//request permission. user must grant, we'll show notifications in the OnRequestPermissionResults() callback
Android.Support.V4.App.ActivityCompat.RequestPermissions(this, new[] { Android.Manifest.Permission.PostNotifications }, activateKeyboard ? 1 : 0 /*use requestCode to transfer the flag*/);
private String getDateTime(DateTime dt)
})
.SetNeutralButton(Resource.String.post_notifications_dialog_notnow, (sender, args) => { })
.Show();
}
private void StartNotificationsServiceAfterPermissionsCheck(bool activateKeyboard)
{
Intent showNotIntent = new Intent(this, typeof(CopyToClipboardService));
showNotIntent.SetAction(Intents.ShowNotification);
showNotIntent.PutExtra(KeyEntry, new ElementAndDatabaseId(App.Kp2a.CurrentDb, Entry).FullId);
AppTask.PopulatePasswordAccessServiceIntent(showNotIntent);
showNotIntent.PutExtra(KeyActivateKeyboard, activateKeyboard);
StartService(showNotIntent);
}
private String getDateTime(DateTime dt)
{
return dt.ToLocalTime().ToString("g", CultureInfo.CurrentUICulture);
}

View File

@@ -42,6 +42,12 @@ namespace keepass2android
private readonly string _schemeSeparator = "://";
private bool _tryGetPermanentAccess;
private const int SftpModeSpinnerPasswd = 0;
private const int SftpModeSpinnerPubKey = 1;
private const int SftpModeSpinnerCustomKey = 2;
private const int SftpKeySpinnerCreateNewIdx = 0;
public string DefaultExtension { get; set; }
public FileSelectHelper(Activity activity, bool isForSave, bool tryGetPermanentAccess, int requestCode)
@@ -57,71 +63,210 @@ namespace keepass2android
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
View dlgContents = activity.LayoutInflater.Inflate(Resource.Layout.sftpcredentials, null);
var ctx = activity.ApplicationContext;
var fileStorage = new Keepass2android.Javafilestorage.SftpStorage(ctx);
var spinner = dlgContents.FindViewById<Spinner>(Resource.Id.sftp_auth_mode_spinner);
dlgContents.FindViewById<Button>(Resource.Id.send_public_key_button).Click += (sender, args) =>
{
var fileStorage = new Keepass2android.Javafilestorage.SftpStorage(activity.ApplicationContext);
string pub_filename = fileStorage.CreateKeyPair();
LinearLayout addNewBtn = dlgContents.FindViewById<LinearLayout>(Resource.Id.sftp_add_key_group);
Button deleteBtn = dlgContents.FindViewById<Button>(Resource.Id.sftp_delete_key_button);
EditText keyNameTxt = dlgContents.FindViewById<EditText>(Resource.Id.sftp_key_name);
EditText keyContentTxt = dlgContents.FindViewById<EditText>(Resource.Id.sftp_key_content);
Intent sendIntent = new Intent();
sendIntent.SetAction(Intent.ActionSend);
sendIntent.PutExtra(Intent.ExtraText, System.IO.File.ReadAllText(pub_filename));
var keySpinner = dlgContents.FindViewById<Spinner>(Resource.Id.sftp_key_names);
var keyNamesAdapter = new ArrayAdapter(ctx, Android.Resource.Layout.SimpleSpinnerDropDownItem, new List<string>());
UpdatePrivateKeyNames(keyNamesAdapter, fileStorage, ctx);
keyNamesAdapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
keySpinner.Adapter = keyNamesAdapter;
keySpinner.SetSelection(SftpKeySpinnerCreateNewIdx);
sendIntent.PutExtra(Intent.ExtraSubject, "Keepass2Android sftp public key");
sendIntent.SetType("text/plain");
activity.StartActivity(Intent.CreateChooser(sendIntent, "Send public key to..."));
};
keySpinner.ItemSelected += (sender, args) =>
{
if (keySpinner.SelectedItemPosition == SftpKeySpinnerCreateNewIdx)
{
keyNameTxt.Text = "";
keyContentTxt.Text = "";
addNewBtn.Visibility = ViewStates.Visible;
deleteBtn.Visibility = ViewStates.Gone;
}
else
{
addNewBtn.Visibility = ViewStates.Gone;
deleteBtn.Visibility = ViewStates.Visible;
}
};
var authModeSpinner = dlgContents.FindViewById<Spinner>(Resource.Id.sftp_auth_mode_spinner);
dlgContents.FindViewById<Button>(Resource.Id.send_public_key_button).Click += (sender, args) =>
{
string pub_filename = fileStorage.CreateKeyPair();
spinner.ItemSelected += (sender, args) =>
{
if (spinner.SelectedItemPosition == 0)
{
dlgContents.FindViewById<EditText>(Resource.Id.sftp_password).Visibility = ViewStates.Visible;
dlgContents.FindViewById<Button>(Resource.Id.send_public_key_button).Visibility = ViewStates.Gone;
}
else
{
dlgContents.FindViewById<EditText>(Resource.Id.sftp_password).Visibility = ViewStates.Gone;
dlgContents.FindViewById<Button>(Resource.Id.send_public_key_button).Visibility = ViewStates.Visible;
Intent sendIntent = new Intent();
sendIntent.SetAction(Intent.ActionSend);
sendIntent.PutExtra(Intent.ExtraText, System.IO.File.ReadAllText(pub_filename));
sendIntent.PutExtra(Intent.ExtraSubject, "Keepass2Android sftp public key");
sendIntent.SetType("text/plain");
activity.StartActivity(Intent.CreateChooser(sendIntent, "Send public key to..."));
};
dlgContents.FindViewById<Button>(Resource.Id.sftp_save_key_button).Click += (sender, args) =>
{
string keyName = keyNameTxt.Text;
string keyContent = keyContentTxt.Text;
string toastMsg = null;
if (!string.IsNullOrEmpty(keyName) && !string.IsNullOrEmpty(keyContent))
{
try
{
fileStorage.SavePrivateKeyContent(keyName, keyContent);
keyNameTxt.Text = "";
keyContentTxt.Text = "";
toastMsg = ctx.GetString(Resource.String.private_key_saved);
}
catch (Exception e)
{
toastMsg = ctx.GetString(Resource.String.private_key_save_failed,
new Java.Lang.Object[] { e.Message });
}
}
else
{
toastMsg = ctx.GetString(Resource.String.private_key_info);
}
if (toastMsg!= null) {
Toast.MakeText(_activity, toastMsg, ToastLength.Long).Show();
}
UpdatePrivateKeyNames(keyNamesAdapter, fileStorage, ctx);
keySpinner.SetSelection(ResolveKeySpinnerSelection(keyNamesAdapter, keyName));
};
dlgContents.FindViewById<Button>(Resource.Id.sftp_delete_key_button).Click += (sender, args) =>
{
int selectedKey = dlgContents.FindViewById<Spinner>(Resource.Id.sftp_key_names).SelectedItemPosition;
string keyName = ResolveSelectedKeyName(keyNamesAdapter, selectedKey);
if (!string.IsNullOrEmpty(keyName))
{
bool deleted = fileStorage.DeleteCustomKey(keyName);
int msgId = deleted ? Resource.String.private_key_delete : Resource.String.private_key_delete_failed;
string msg = ctx.GetString(msgId, new Java.Lang.Object[] { keyName });
Toast.MakeText(_activity, msg, ToastLength.Long).Show();
UpdatePrivateKeyNames(keyNamesAdapter, fileStorage, ctx);
keySpinner.SetSelection(SftpKeySpinnerCreateNewIdx);
}
};
authModeSpinner.ItemSelected += (sender, args) =>
{
var passwordBox = dlgContents.FindViewById<EditText>(Resource.Id.sftp_password);
var publicKeyButton = dlgContents.FindViewById<Button>(Resource.Id.send_public_key_button);
var keyfileGroup = dlgContents.FindViewById<LinearLayout>(Resource.Id.sftp_keyfile_group);
switch (authModeSpinner.SelectedItemPosition)
{
case SftpModeSpinnerPasswd:
passwordBox.Visibility = ViewStates.Visible;
publicKeyButton.Visibility = ViewStates.Gone;
keyfileGroup.Visibility = ViewStates.Gone;
break;
case SftpModeSpinnerPubKey:
passwordBox.Visibility = ViewStates.Gone;
publicKeyButton.Visibility = ViewStates.Visible;
keyfileGroup.Visibility = ViewStates.Gone;
break;
case SftpModeSpinnerCustomKey:
passwordBox.Visibility = ViewStates.Gone;
publicKeyButton.Visibility = ViewStates.Gone;
keyfileGroup.Visibility = ViewStates.Visible;
break;
}
};
if (!defaultPath.EndsWith(_schemeSeparator))
{
var fileStorage = new Keepass2android.Javafilestorage.SftpStorage(activity.ApplicationContext);
SftpStorage.ConnectionInfo ci = fileStorage.SplitStringToConnectionInfo(defaultPath);
dlgContents.FindViewById<EditText>(Resource.Id.sftp_host).Text = ci.Host;
dlgContents.FindViewById<EditText>(Resource.Id.sftp_port).Text = ci.Port.ToString();
dlgContents.FindViewById<EditText>(Resource.Id.sftp_user).Text = ci.Username;
dlgContents.FindViewById<EditText>(Resource.Id.sftp_password).Text = ci.Password;
dlgContents.FindViewById<EditText>(Resource.Id.sftp_key_name).Text = ci.KeyName;
dlgContents.FindViewById<EditText>(Resource.Id.sftp_key_passphrase).Text = ci.KeyPassphrase;
dlgContents.FindViewById<EditText>(Resource.Id.sftp_initial_dir).Text = ci.LocalPath;
if (string.IsNullOrEmpty(ci.Password))
if (ci.ConnectTimeoutSec != SftpStorage.UnsetSftpConnectTimeout)
{
spinner.SetSelection(1);
dlgContents.FindViewById<EditText>(Resource.Id.sftp_connect_timeout).Text = ci.ConnectTimeoutSec.ToString();
}
if (ci.ConfigOpts.Contains(SftpStorage.SshCfgKex))
{
dlgContents.FindViewById<EditText>(Resource.Id.sftp_kex).Text = ci.ConfigOpts[SftpStorage.SshCfgKex].ToString();
}
if (ci.ConfigOpts.Contains(SftpStorage.SshCfgServerHostKey))
{
dlgContents.FindViewById<EditText>(Resource.Id.sftp_shk).Text = ci.ConfigOpts[SftpStorage.SshCfgServerHostKey].ToString();
}
if (!string.IsNullOrEmpty(ci.Password))
{
authModeSpinner.SetSelection(SftpModeSpinnerPasswd);
} else if (!string.IsNullOrEmpty(ci.KeyName))
{
authModeSpinner.SetSelection(SftpModeSpinnerCustomKey);
keySpinner.SetSelection(ResolveKeySpinnerSelection(keyNamesAdapter, ci.KeyName));
} else
{
authModeSpinner.SetSelection(SftpModeSpinnerPubKey);
}
}
builder.SetView(dlgContents);
builder.SetPositiveButton(Android.Resource.String.Ok,
(sender, args) =>
{
string host = dlgContents.FindViewById<EditText>(Resource.Id.sftp_host).Text;
string portText = dlgContents.FindViewById<EditText>(Resource.Id.sftp_port).Text;
int port = Keepass2android.Javafilestorage.SftpStorage.DefaultSftpPort;
if (!string.IsNullOrEmpty(portText))
int.TryParse(portText, out port);
string user = dlgContents.FindViewById<EditText>(Resource.Id.sftp_user).Text;
string password = dlgContents.FindViewById<EditText>(Resource.Id.sftp_password).Text;
string initialPath = dlgContents.FindViewById<EditText>(Resource.Id.sftp_initial_dir).Text;
if (string.IsNullOrEmpty(initialPath))
initialPath = "/";
string sftpPath = new Keepass2android.Javafilestorage.SftpStorage(activity.ApplicationContext).BuildFullPath(host, port, initialPath, user,
password);
onStartBrowse(sftpPath);
});
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>((sender, e) => onCancel());
builder.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => {
int idx = 0;
string host = dlgContents.FindViewById<EditText>(Resource.Id.sftp_host).Text;
string portText = dlgContents.FindViewById<EditText>(Resource.Id.sftp_port).Text;
int port = SftpStorage.DefaultSftpPort;
if (!string.IsNullOrEmpty(portText))
int.TryParse(portText, out port);
string user = dlgContents.FindViewById<EditText>(Resource.Id.sftp_user).Text;
string password = null;
string keyName = null, keyPassphrase = null;
switch (dlgContents.FindViewById<Spinner>(Resource.Id.sftp_auth_mode_spinner).SelectedItemPosition)
{
case SftpModeSpinnerPasswd:
password = dlgContents.FindViewById<EditText>(Resource.Id.sftp_password).Text;
break;
case SftpModeSpinnerPubKey:
break;
case SftpModeSpinnerCustomKey:
keyName = ResolveSelectedKeyName(keyNamesAdapter, keySpinner.SelectedItemPosition);
keyPassphrase = dlgContents.FindViewById<EditText>(Resource.Id.sftp_key_passphrase).Text;
break;
}
string initialPath = dlgContents.FindViewById<EditText>(Resource.Id.sftp_initial_dir).Text;
if (string.IsNullOrEmpty(initialPath))
initialPath = "/";
string connectTimeoutText = dlgContents.FindViewById<EditText>(Resource.Id.sftp_connect_timeout).Text;
int connectTimeout = SftpStorage.UnsetSftpConnectTimeout;
if (!string.IsNullOrEmpty(connectTimeoutText))
{
int.TryParse(connectTimeoutText, out connectTimeout);
}
string kexAlgorithms = dlgContents.FindViewById<EditText>(Resource.Id.sftp_kex).Text;
string shkAlgorithms = dlgContents.FindViewById<EditText>(Resource.Id.sftp_shk).Text;
string sftpPath = fileStorage.BuildFullPath(
host, port, initialPath, user, password, connectTimeout, keyName, keyPassphrase,
kexAlgorithms, shkAlgorithms);
onStartBrowse(sftpPath);
});
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>((sender, e) => onCancel());
builder.SetNegativeButton(Android.Resource.String.Cancel, evtH);
builder.SetTitle(activity.GetString(Resource.String.enter_sftp_login_title));
@@ -129,9 +274,41 @@ namespace keepass2android
dialog.Show();
#endif
}
}
private void ShowHttpDialog(Activity activity, Util.FileSelectedHandler onStartBrowse, Action onCancel, string defaultPath)
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
private void UpdatePrivateKeyNames(ArrayAdapter dataView, SftpStorage storage, Context ctx)
{
dataView.Clear();
dataView.Add(ctx.GetString(Resource.String.private_key_create_new));
foreach (string keyName in storage.GetCustomKeyNames())
dataView.Add(keyName);
}
private int ResolveKeySpinnerSelection(ArrayAdapter dataView, string keyName)
{
int idx = -1;
for (int i = 0; i < dataView.Count; i++)
{
string itemName = dataView.GetItem(i).ToString();
if (string.Equals(keyName, itemName)) {
idx = i;
break;
}
}
return idx < 0 ? SftpKeySpinnerCreateNewIdx : idx;
}
private string ResolveSelectedKeyName(ArrayAdapter dataView, int selectedItem)
{
if (selectedItem != SftpKeySpinnerCreateNewIdx && selectedItem > 0 && selectedItem < dataView.Count)
return dataView.GetItem(selectedItem).ToString();
else
return null;
}
#endif
private void ShowHttpDialog(Activity activity, Util.FileSelectedHandler onStartBrowse, Action onCancel, string defaultPath)
{
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
@@ -214,8 +391,8 @@ namespace keepass2android
string user = dlgContents.FindViewById<EditText>(Resource.Id.ftp_user).Text;
string password = dlgContents.FindViewById<EditText>(Resource.Id.ftp_password).Text;
string initialPath = dlgContents.FindViewById<EditText>(Resource.Id.ftp_initial_dir).Text;
string ftpPath = new NetFtpFileStorage(_activity, App.Kp2a).BuildFullPath(host, port, initialPath, user,
password, encryption);
string ftpPath = new NetFtpFileStorage(_activity, App.Kp2a, () => false)
.BuildFullPath(host, port, initialPath, user, password, encryption);
onStartBrowse(ftpPath);
});
EventHandler<DialogClickEventArgs> evtH = new EventHandler<DialogClickEventArgs>((sender, e) => onCancel());

View File

@@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Linq;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Views;
@@ -57,8 +58,9 @@ namespace keepass2android
{ Resource.Id.child_db_infotext, 13 },
{ Resource.Id.fingerprint_infotext, 12 },
{ Resource.Id.autofill_infotext, 11 },
{ Resource.Id.notification_info_android8_infotext, 10 },
{ Resource.Id.infotext, 9 },
{ Resource.Id.notification_permission_infotext, 10 },
{ Resource.Id.notification_info_android8_infotext, 9 },
{ Resource.Id.infotext, 8 },
{ Resource.Id.select_other_entry, 20},
{ Resource.Id.add_url_entry, 20},
};
@@ -273,6 +275,7 @@ namespace keepass2android
UpdateFingerprintInfo();
UpdateAutofillInfo();
UpdateAndroid8NotificationInfo();
UpdatePostNotificationsPermissionInfo();
UpdateInfotexts();
RefreshIfDirty();
@@ -280,6 +283,7 @@ namespace keepass2android
SetSearchItemVisibility();
}
private void UpdateInfotexts()
{
@@ -385,6 +389,31 @@ namespace keepass2android
hasCalledOtherActivity = false;
}
private void UpdatePostNotificationsPermissionInfo(bool hideForever=false)
{
const string prefsKey = "DidShowNotificationPermissionInfo";
bool canShowNotificationInfo = ((int)Build.VERSION.SdkInt >= 33)
&& (!_prefs.GetBoolean(prefsKey, false)
&& (CheckSelfPermission(Android.Manifest.Permission.PostNotifications) !=
Permission.Granted)
&& (ShouldShowRequestPermissionRationale(Android.Manifest.Permission.PostNotifications) //this menthod returns false if we haven't asked yet or if the user has denied permission too often
|| !PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean("RequestedPostNotificationsPermission", false))//use a preference to tell the difference between "haven't asked yet" and "have asked too often"
);
if ((canShowNotificationInfo) && hideForever)
{
_prefs.Edit().PutBoolean(prefsKey, true).Commit();
canShowNotificationInfo = false;
}
if (canShowNotificationInfo)
{
RegisterInfoTextDisplay("NotificationPermissionInfo"); //this ensures that we don't show the general info texts too soon
}
UpdateBottomBarElementVisibility(Resource.Id.notification_permission_infotext, canShowNotificationInfo);
}
private void UpdateAndroid8NotificationInfo(bool hideForever = false)
{
const string prefsKey = "DidShowAndroid8NotificationInfo";
@@ -606,6 +635,25 @@ namespace keepass2android
}
if (FindViewById(Resource.Id.post_notification_button_allow) != null)
{
FindViewById(Resource.Id.post_notification_button_allow).Click += (sender, args) =>
{
//remember that we did ask for permission at least once:
var edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
edit.PutBoolean("RequestedPostNotificationsPermission", true);
edit.Commit();
Android.Support.V4.App.ActivityCompat.RequestPermissions(this, new[] { Android.Manifest.Permission.PostNotifications }, 0);
UpdatePostNotificationsPermissionInfo(true);
};
FindViewById(Resource.Id.post_notification_button_dont_show_again).Click += (sender, args) =>
{
UpdatePostNotificationsPermissionInfo(true);
};
}

View File

@@ -869,6 +869,15 @@ namespace keepass2android
KeyProviderTypes.Add(KeyProviders.KeyFile);
continue;
}
if (type.StartsWith(KeyProviders.ChallengeXC.ToString()+KeyProviders.KeyFile.ToString()))
{
_keyFile = WebUtility.UrlDecode((type.Substring(KeyProviders.ChallengeXC.ToString().Length)).Substring(KeyProviders.KeyFile.ToString().Length));
Kp2aLog.Log("Added XC key file of length " + _keyFile.Length);
KeyProviderTypes.Add(KeyProviders.ChallengeXC);
KeyProviderTypes.Add(KeyProviders.KeyFile);
continue;
}
foreach (KeyProviders providerType in Enum.GetValues(typeof(KeyProviders)))
{
if (type == providerType.ToString())
@@ -1239,9 +1248,13 @@ namespace keepass2android
case 6:
KeyProviderTypes.Add(KeyProviders.ChallengeXC);
break;
case 7:
KeyProviderTypes.Add(KeyProviders.ChallengeXC);
KeyProviderTypes.Add(KeyProviders.KeyFile);
case 7:
//don't set to "" to prevent losing the filename. (ItemSelected is also called during recreation!)
Kp2aLog.Log("key file length before: " + _keyFile?.Length);
_keyFile = (FindViewById(Resource.Id.label_keyfilename).Tag ?? "").ToString();
Kp2aLog.Log("key file length after: " + _keyFile?.Length);
KeyProviderTypes.Add(KeyProviders.ChallengeXC);
KeyProviderTypes.Add(KeyProviders.KeyFile);
break;
default:
throw new Exception("Unexpected position " + args.Position + " / " +
@@ -1256,7 +1269,6 @@ namespace keepass2android
RequestCodePrepareOtpAuxFile, false);
};
}
}
private void RestoreState(Bundle savedInstanceState)

View File

@@ -43,7 +43,7 @@
</queries>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_notify_locked" android:label="KP2A entry search" android:name="keepass2android.keepass2android_debug.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android_debug.permission.Kp2aChooseAutofill" android:protectionLevel="signature" />
<application
@@ -258,6 +258,7 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Samsung Pass permission -->
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
<!-- READ_PHONE_STATE seems to come from some library or so, not clear where. We don't want to have it, remove it: -->

View File

@@ -42,8 +42,8 @@
<action android:name="android.intent.action.VIEW" />
</intent>
</queries>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher" android:label="KP2A entry search" android:name="keepass2android.keepass2android.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android.permission.Kp2aChooseAutofill" android:protectionLevel="signature" />
@@ -270,6 +270,10 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="keepass2android.keepass2android.permission.KP2aInternalSearch" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<!-- Samsung Pass permission -->
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />

View File

@@ -40,7 +40,7 @@
</intent>
</queries>
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="31" />
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="33" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_launcher_offline" android:label="KP2A entry search" android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher_offline" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android_nonet.permission.Kp2aChooseAutofill" android:protectionLevel="signature" />
<application
@@ -243,6 +243,9 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalSearch" />

View File

@@ -245,6 +245,50 @@
style="@style/BottomBarButton" />
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/notification_permission_infotext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:orientation="vertical">
<TextView android:id="@+id/infotext" android:text="@string/PostNotificationsPermissionInfo_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_margin="6dp"
android:layout_marginBottom="2dp"
/>
<RelativeLayout
android:id="@+id/notification_permissions_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:baselineAligned="false">
<Button
android:id="@+id/post_notification_button_allow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:paddingTop="4dp"
android:text="@string/post_notifications_dialog_allow"
style="@style/BottomBarButton" />
<Button
android:id="@+id/post_notification_button_dont_show_again"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:paddingTop="4dp"
android:text="@string/dont_show_again"
style="@style/BottomBarButton" />
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/infotext"
android:layout_width="match_parent"

View File

@@ -61,17 +61,80 @@
</LinearLayout>
<EditText
android:id="@+id/sftp_password"
android:id="@+id/sftp_password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:singleLine="true"
android:hint="@string/hint_pass"
android:importantForAccessibility="no"/>
<Button
android:id="@+id/send_public_key_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/send_public_key" />
<LinearLayout
android:id="@+id/sftp_keyfile_group"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/private_key_select" />
<Spinner
android:id="@+id/sftp_key_names"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/sftp_add_key_group"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText android:id="@+id/sftp_key_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_sftp_key_name" />
<EditText
android:id="@+id/sftp_key_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:minLines="1"
android:maxLines="6"
android:hint="@string/hint_sftp_key_content" />
<Button
android:id="@+id/sftp_save_key_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/save_key" />
</LinearLayout>
<Button
android:id="@+id/sftp_delete_key_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/delete_key" />
<EditText
android:id="@+id/sftp_key_passphrase"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:singleLine="true"
android:hint="@string/hint_pass"
android:importantForAccessibility="no"/>
<Button android:id="@+id/send_public_key_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/send_public_key" />
android:inputType="textPassword"
android:hint="@string/hint_sftp_key_passphrase" />
</LinearLayout>
<TextView android:id="@+id/initial_dir"
@@ -88,6 +151,49 @@
android:singleLine="true"
android:text="/"
/>
</LinearLayout>
<TextView android:id="@+id/connect_timeout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dip"
android:layout_marginTop="4dip"
android:text="@string/connect_timeout" />
<EditText
android:id="@+id/sftp_connect_timeout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:inputType="number" />
<TextView android:id="@+id/sftp_kex_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dip"
android:layout_marginTop="4dip"
android:text="@string/sftp_kex_title" />
<EditText
android:id="@+id/sftp_kex"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textNoSuggestions"
android:hint="@string/hint_sftp_kex"
android:singleLine="true"
android:text=""
/>
<TextView android:id="@+id/sftp_shk_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dip"
android:layout_marginTop="4dip"
android:text="@string/sftp_shk_title" />
<EditText
android:id="@+id/sftp_shk"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textNoSuggestions"
android:hint="@string/hint_sftp_shk"
android:singleLine="true"
android:text=""
/>
</LinearLayout>

View File

@@ -492,7 +492,6 @@
<string name="filestoragename_dropboxKP2A">Dropbox (مجلد KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">إذا كنت لا تريد منح KP2A حق الوصول إلى Dropbox بالكامل، قم بتحديد هذا الخيار. سوف يتطلب حق الوصول فقط إلى المجلد Apps/Keepass2Android. هذا مناسب بشكل خاص عند إنشاء قاعدة بيانات جديدة. إذا كان بالفعل لديك قاعدة بيانات، انقر فوق هذا الخيار لإنشاء المجلد، ثم ضع الملف داخل المجلد (من جهاز الكمبيوتر الخاص بك) وقم بتحديد هذا الخيار مرة أخرى لفتح الملف.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_full">جميع الملفات والملفات المشتركة</string>
@@ -994,7 +993,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>كلمة السر</item>
<item>المفتاح الخاص/العمومي</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>تجاهل فشل التحقق من صحة الشهادة</item>

View File

@@ -74,7 +74,8 @@
<string name="DbQuicklockedChannel_name">Cəld Kilid Açma</string>
<string-array name="sftp_auth_modes">
<item>Şifrə</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string name="disable_secure_screen_check">Bu mesajı ləğv et</string>
<string name="switch_ime_text">Keepass2Android klaviaturasını aktivləşdir.</string>

View File

@@ -515,7 +515,6 @@
<string name="filestoragename_dropboxKP2A">Dropbox (каталог KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Калі вы хочаце даць KP2A доступ да ўсяго сховішча Dropbox, абярыце гэты параметр. Праграма будзе запытваць доступ толькі да каталога Apps/Keepass2Android. Гэта асабліва пасуе пры стварэнні новай базы даных. Калі ў вас ужо ёсць база даных, абярыце гэты параметр, каб стварыць каталог, перамясціце ў яго файл базы даных, а пасля зноў абярыце гэты параметр, каб адкрыць файл.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Гэты тып сховішча запытвае доступ толькі да каталога pCloud \"Праграмы/Keepass2Android\". Калі вы хочаце выкарыстоўваць наяўную базу даных з акаўнта pCloud, то пераканайцеся, што файл знаходзіцца ў каталозе pCloud.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -1048,7 +1047,8 @@ Initial public release
</string-array>
<string-array name="sftp_auth_modes">
<item>Пароль</item>
<item>Прыватны / Публічны ключ</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Не зважаць на памылкі праверкі сертыфіката</item>

View File

@@ -461,6 +461,7 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>Парола</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
</resources>

View File

@@ -246,6 +246,11 @@
<item>Passphrase</item>
<item>Passphrase + Password</item>
</string-array>
<string-array name="PasswordGeneratorCaseModes">
<item>minúscules</item>
<item>MAJÚSCULES</item>
<item>First Character Uppercase</item>
</string-array>
<string name="search_hint">Trobar el que</string>
<string name="search_results">Resultats de cerca</string>
<string name="search_in">Cerca a</string>
@@ -477,7 +482,6 @@
<string name="filestoragename_dropboxKP2A">Dropbox (carpeta de KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Si no voleu donar accés KP2A al seu Dropbox complet, heu de seleccionar aquesta opció. Es sol·licitarà només l\'accés a la carpeta Apps/Keepass2Android. Això és especialment adequat quan es crea una nova base de dades. Si ja teniu una base de dades, feu clic a aquesta opció per crear la carpeta, a continuació, posar el seu arxiu dins la carpeta (des del seu PC) i llavors seleccioni aquesta opció de nou per obrir l\'arxiu.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_full">Tots els fitxers i fitxers compartits</string>
@@ -943,7 +947,8 @@ Revisió inicial per al públic
</string-array>
<string-array name="sftp_auth_modes">
<item>Contrasenya</item>
<item>Clau Privada/Pública</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignora els errors de validació de certificat</item>

View File

@@ -112,8 +112,8 @@
<string name="entry_created">Vytvořeno</string>
<string name="entry_expires">Vyprší</string>
<string name="entry_group_name">Název skupiny</string>
<string name="entry_keyfile">Klíčový soubor (volitelný)</string>
<string name="keyfile_heading">Klíčový soubor</string>
<string name="entry_keyfile">Soubor klíče (volitelný)</string>
<string name="keyfile_heading">Soubor klíče</string>
<string name="entry_modified">Změněno</string>
<string name="entry_password">Heslo</string>
<string name="entry_save">Uložit</string>
@@ -134,7 +134,7 @@
<string name="error_invalid_db">Chybná databáze.</string>
<string name="error_invalid_path">Chybná cesta.</string>
<string name="error_no_name">Je nutné zadat název.</string>
<string name="error_nopass">Je potřeba heslo nebo klíčový soubor.</string>
<string name="error_nopass">Je vyžadováno heslo nebo soubor klíče.</string>
<string name="error_pass_gen_type">Minimálně jeden typ generování hesla musí být zvolen</string>
<string name="error_pass_match">Hesla se neshodují.</string>
<string name="error_rounds_not_number">Počet opakování musí být číslo.</string>
@@ -149,21 +149,22 @@
<string name="hint_conf_pass">potvrdit heslo</string>
<string name="hint_generated_password">vygenerované heslo</string>
<string name="hint_group_name">Název skupiny</string>
<string name="hint_keyfile">klíčový soubor</string>
<string name="hint_keyfile">Soubor klíče</string>
<string name="hint_length">délka</string>
<string name="hint_pass">heslo</string>
<string name="hint_keyfile_path">Cesta k privátnímu klíči SSH</string>
<string name="hint_login_pass">Heslo</string>
<string name="hint_title">název</string>
<string name="hint_url">Adresa URL</string>
<string name="hint_override_url">přepsat adresu URL</string>
<string name="hint_tags">štítek1, štítek2</string>
<string name="hint_username">uživatelské jméno</string>
<string name="InvalidPassword">Chybné heslo nebo klíčový soubor.</string>
<string name="InvalidPassword">Chybné heslo nebo soubor klíče.</string>
<string name="invalid_algorithm">Neplatný algoritmus.</string>
<string name="invalid_db_sig">Nelze rozpoznat formát databáze.</string>
<string name="keyfile_does_not_exist">Klíčový soubor neexistuje.</string>
<string name="no_keyfile_selected">Nebyl vybrán žádný klíčový soubor.</string>
<string name="keyfile_is_empty">Klíčový soubor je prázdný.</string>
<string name="keyfile_does_not_exist">Soubor klíče neexistuje.</string>
<string name="no_keyfile_selected">Nebyl vybrán žádný soubor klíče.</string>
<string name="keyfile_is_empty">Soubor klíče je prázdný.</string>
<string name="length">Délka</string>
<string name="list_size_title">Velikost seznamu skupin</string>
<string name="list_size_summary">Velikost textu v seznamu skupin</string>
@@ -217,8 +218,8 @@
<string name="progress_create">Vytvářím novou databázi…</string>
<string name="create_database">Vytvořit databázi</string>
<string name="progress_title">Pracuji…</string>
<string name="remember_keyfile_summary">Pamatovat si umístění klíčových souborů</string>
<string name="remember_keyfile_title">Uložit klíčový soubor</string>
<string name="remember_keyfile_summary">Pamatovat si umístění souborů klíčů</string>
<string name="remember_keyfile_title">Uložit soubor klíče</string>
<string name="remove_from_filelist">Odstranit</string>
<string name="edit">Změnit</string>
<string name="rijndael">Rijndael (AES)</string>
@@ -301,6 +302,8 @@
<string name="NoDalVerification_summary">Zrušit kontrolu pokud doména a položka v aplikaci souhlasí.</string>
<string name="InlineSuggestions_title">Integrovat s klávesnicí</string>
<string name="InlineSuggestions_summary">Zobrazuje návrhy automatického doplňování jako možnosti na klávesnici (pokud je podporováno metodou vstupu)</string>
<string name="LogAutofillView_title">Zahrnout obrazovky automatického doplňování do protokolu</string>
<string name="LogAutofillView_summary">Zahrne detaily o obrazovkách, kde byly detekovány prostory pro automatické dopňování, do protokolu (pokud je zapnut protokolovací soubor). Tyto detaily mohou být zaslány vývojáři pokud automatické doplňování nefunguje správně.</string>
<string name="requires_android11">Vyžaduje Android 11 nebo novější</string>
<string name="kp2a_findUrl">Najít heslo</string>
<string name="excludeExpiredEntries">Vyloučit expirované položky</string>
@@ -415,6 +418,7 @@
<string name="ShowUnlockedNotification_summary">Zobrazovat oznamovací ikonu pokud je databáze odemčena.</string>
<string name="IconVisibilityInfo_Android8_text">Android 8 přinesl nové chování upozornění. Pokud chcete schovat oznamovací ikonu Keepass2Android můžete tak učinit v nastavení systému. Nastavte kategorii důležitosti upozornění na Minimum.</string>
<string name="IconVisibilityInfo_Android8_btnSettings">Otevřít nastavení</string>
<string name="PostNotificationsPermissionInfo_text">Keepass2Android umožňuje zobrazit systémové oznámení, když databáze není uzamčena. Aby to fungovalo, udělte prosím oprávnění.</string>
<string name="DontCare">Není třeba</string>
<string name="DocumentAccessRevoked">Soubor není dále dostupný pro Keepass2Android. Buď byl smazán, nebo bylo zrušeno povolení k přístupu. Prosím otevřete daný soubor znovu pomocí: Změnit databázi.</string>
<string name="PreloadDatabaseEnabled_title">Přednačítat databázový soubor</string>
@@ -499,9 +503,27 @@
<string name="hint_sftp_host">počítač (např: 192.168.0.1)</string>
<string name="hint_sftp_port">port</string>
<string name="initial_directory">Počáteční adresář (nepovinné):</string>
<string name="connect_timeout">Časový limit připojení v sekundách (volitelné):\"</string>
<string name="enter_sftp_login_title">Zadejte přihlašovací údaje k SFTP:</string>
<string name="sftp_auth_mode">Režim ověření</string>
<string name="send_public_key">Odeslat veřejný klíč...</string>
<string name="select_private_keyfile">Vyberte privátní klíč...</string>
<string name="hint_sftp_key_name">Název nového klíče</string>
<string name="hint_sftp_key_content">Obsah nového klíče</string>
<string name="private_key_saved">Privátní klíč uložen</string>
<string name="private_key_save_failed">NEPODAŘILO se uložit privátní klíč: %1$s</string>
<string name="private_key_info">Zadejte název klíče a obsah pro uložení</string>
<string name="private_key_delete">Odstraněný privátní klíč: %1$s</string>
<string name="private_key_delete_failed">NEPODAŘILO se odstranit privátní klíč: %1$s</string>
<string name="save_key">Uložit privátní klíč</string>
<string name="delete_key">Odstranit privátní klíč</string>
<string name="private_key_select">Vybraný privátní klíč</string>
<string name="private_key_create_new">[Přidat nový...]</string>
<string name="hint_sftp_key_passphrase">Heslo klíče (volitelné)</string>
<string name="sftp_kex_title">Algoritmus (algoritmy) pro výměnu klíčů (KEX) (volitelné)</string>
<string name="hint_sftp_kex">\"Názvy/spec. oddělené čárkou</string>
<string name="sftp_shk_title">Algoritmus (algoritmy) klíče serverového hostitele (volitelné)</string>
<string name="hint_sftp_shk">\"Názvy/spec. oddělené čárkou</string>
<string name="enter_ftp_login_title">Zadejte přihlašovací údaje pro FTP:</string>
<string name="enter_mega_login_title">Zadejte přihlašovací údaje pro váš účet MEGA</string>
<string name="select_storage_type">Vyberte typ úložiště:</string>
@@ -519,7 +541,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A soubory)</string>
<string name="filestoragehelp_gdriveKP2A">Nechcete-li dát aplikaci KeePass2Android přístup k celému vašemu Google Drive, použijte tuto možnost. Upozorňujeme, že nejprve musíte vytvořit soubor s databází položek, existující soubory nejsou viditelné pro aplikaci. Toto můžete udělat z obrazovky Vytvořit databázi, nebo exportem právě otevřené databáze, volbou této možnosti</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Tento typ úložiště si vyžaduje přístup pouze do pCloudové složky \"Applications/Keepass2Android\". Chcete-li použít existující databázi ze svého pCloud účtu, ujistěte se, že je soubor umístěn v této složce.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -539,11 +560,11 @@
<string name="master_password">Hlavní heslo</string>
<string name="help_master_password">Databáze bude zašifrována heslem, které zde nastavíte. Pro zabezpečení databáze zvolte silné heslo! Tip: Vytvořte jednu nebo dvě věty a použijte první písmena jednotlivých slov jako heslo. Využívejte interpunkční znaménka.</string>
<string name="hint_master_password">Nastavte hlavní heslo pro ochranu databáze:</string>
<string name="key_file">Klíčový soubor</string>
<string name="help_key_file">Klíčový soubor je v podstatě heslo uložené v souboru. Klíčové soubory jsou obvykle silnější než hlavní heslo, protože klíč může být mnohem složitější, nicméně je také těžší držet klíčový soubor v tajnosti. Je-li vaše databáze umístěna na vzdáleném úložišti, neukládejte klíčový soubor do stejného místa! Tím by bylo zašifrování souboru zcela zbytečné. Důležité upozornění: Po vytvoření databáze již nelze změnit obsah klíčového souboru!</string>
<string name="hint_key_file">Zvolte, pokud chcete použít klíčový soubor spolu s hlavním heslem:</string>
<string name="use_key_file">Použít klíčový soubor</string>
<string name="error_adding_keyfile">Chyba při přidávání klíčového souboru!</string>
<string name="key_file">Soubor klíče</string>
<string name="help_key_file">Soubor klíče je v podstatě heslo uložené v souboru. Soubory klíčů jsou obvykle silnější než hlavní heslo, protože klíč může být mnohem složitější, nicméně je také těžší držet soubor klíče v tajnosti. Je-li vaše databáze umístěna na vzdáleném úložišti, neukládejte soubor klíče do stejného místa! Tím by bylo zašifrování souboru zcela zbytečné. Důležité upozornění: Po vytvoření databáze již nelze změnit obsah souboru klíče!</string>
<string name="hint_key_file">Zvolte, pokud chcete použít soubor klíče spolu s hlavním heslem:</string>
<string name="use_key_file">Použít soubor klíče</string>
<string name="error_adding_keyfile">Chyba při přidávání souboru klíče</string>
<string name="init_otp">Otevřít pomocný soubor OTP…</string>
<string name="otp_explanation">Zadejte další jednorázové heslo (OTP). Dotkněte se zad telefonu s Yubikey NEO pro zadání pomocí NFC (vyžaduje aplikaci Yubiclip).</string>
<string name="otp_hint">OTP %1$d</string>
@@ -572,6 +593,7 @@
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Logovací soubor pro ladění chyb</string>
<string name="DebugLog_title">Použít protokolovací soubor</string>
<string name="JSchDebug_title">Protokolování ladění SFTP</string>
<string name="DebugLog_summary">Ulož výstup aplikace do lokálního souboru</string>
<string name="DebugLog_send">Pošli soubor s ladícími informacemi...</string>
<string name="loading">Nahrávám…</string>
@@ -685,6 +707,12 @@
<string name="EntryChannel_desc">Upozornění pro usnadnění přístupu k momentálně zvolené položce.</string>
<string name="CloseDbAfterFailedAttempts">Zavřít databázi po třech neúspěšných pokusech o odemknutí.</string>
<string name="WarnFingerprintInvalidated">Varování! Biometrické ověření může být zneplatněno Androidem, např. po přidání nového otisku prstu do nastavení zařízení. Ujistěte se, že vždy víte, jak odemknout pomocí hlavního hesla!</string>
<string-array name="ChangeLog_1_09e">
<item>Opravy pádů a nečekaného odhlašování</item>
<item>Přepnutí na novou implementaci SFTP, podporující moderní algoritmy pro veřejné klíče jako rsa-sha2-256</item>
<item>Označení hesel jako citlivé údaje při kopírování do schránky (Android 13)</item>
<item>Vylepšení automatického vyplňování</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Přidaná podpora zobrazování, mazání a obnovování záloh položek</item>
<item>Přidaná podpora pro cloudové úložiště MEGA </item>
@@ -720,7 +748,7 @@
<item>Upgrade pCloud SDK kvůli opravě problému s autentizací</item>
<item>Update Jsch na verzi 0.1.55</item>
<item>Přidáno menu na obrazovku volby databáze.</item>
<item>Přidána možnost exportu importovaného klíčového souboru</item>
<item>Přidána možnost exportu importovaného souboru klíče</item>
</string-array>
<string-array name="ChangeLog_1_08c">
<item>Již se neukládají názvy balíčků Android aplikací v poli URL</item>
@@ -1040,17 +1068,18 @@ První veřejné vydání
</string-array>
<string-array name="password_modes">
<item>Pouze heslo</item>
<item>Heslo + klíčový soubor</item>
<item>Heslo + soubor klíče</item>
<item>Heslo + OTP</item>
<item>Heslo + OTP heslo (režim obnovení)</item>
<item>Heslo + Odpověď výzvy</item>
<item>Heslo + Odpověď výzvy (režim obnovení)</item>
<item>Heslo + Výzva-Odpověď pro Keepass XC</item>
<item>Heslo + Klíčový soubor + výzva-odpověď (Challenge-response) pro KeePassXC</item>
<item>Heslo + Soubor klíče + Výzva-Odpověď (Challenge-Response) pro KeePassXC</item>
</string-array>
<string-array name="sftp_auth_modes">
<item>Heslo</item>
<item>Soukromý/Veřejný klíč</item>
<item>K2A Privátní/veřejný klíč</item>
<item>Vlastní privátní klíč</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignorovat neúspěšné ověření certifikátu</item>
@@ -1068,6 +1097,11 @@ První veřejné vydání
<string name="autofill_enable_for">Povolit Automatické doplnění pro %1$s</string>
<string name="invalid_link_association">Nedaří se spojit doménu %1$s s aplikací %2$s</string>
<string name="enable_fingerprint_hint">Keepass2Android nalezl hardwarovou biometrickou čtečku. Chcete pro tuto databázi povolit Biometrické odemykáni?</string>
<string name="post_notifications_dialog_title">Povolit notifikace</string>
<string name="post_notifications_dialog_message">Keepass2Android může zobrazovat notifikace s tlačítky pro kopírování hodnot, jako jsou hesla a TOTP, do schránky nebo pro vyvolání vestavěné klávesnice. To je užitečné pro přenos hodnot do jiných aplikací bez nutnosti opakovaného přepínání do Keepass2Android. Chcete takové notifikace povolit?</string>
<string name="post_notifications_dialog_allow">Povolit notifikace</string>
<string name="post_notifications_dialog_disable">Vypnout tuto funkci</string>
<string name="post_notifications_dialog_notnow">Nyní ne</string>
<string name="understand">Rozumím</string>
<string name="dont_show_again">Vícekrát neukazuj</string>
<string name="masterkey_infotext_head">Pamatujete si Vaše hlavní heslo?</string>

View File

@@ -175,7 +175,7 @@
<string name="masktotp_title">Maskér TOTP-felt</string>
<string name="masktotp_summary">Skjul TOTP-felt som standard</string>
<string name="NoAutofillDisabling_title">Ingen mulighed for at deaktivere autofyld</string>
<string name="NoAutofillDisabling_summary">Hvis aktiveret, vil appen ikke vise muligheden for at deaktivere autofyld for bestemte poster.</string>
<string name="NoAutofillDisabling_summary">Hvis aktiveret, vil appen ikke vise muligheden for at deaktivere autoudfyld for bestemte poster.</string>
<string name="menu_about">Om</string>
<string name="menu_change_key">Skift hovednøgle</string>
<string name="menu_copy_pass">Kopiér adgangskode</string>
@@ -301,6 +301,8 @@
<string name="NoDalVerification_summary">Deaktiverer tjek af om domæne- og app-pakke matcher</string>
<string name="InlineSuggestions_title">Integrer med tastatur</string>
<string name="InlineSuggestions_summary">Viser forslag til autofyld som indlejrede muligheder i tastaturet (hvis understøttet af inputmetoden)</string>
<string name="LogAutofillView_title">Log autoudfyld-visning</string>
<string name="LogAutofillView_summary">Skriv detaljer om autofill-visningen til fejlfindingsloggen (hvis fejlfindingslogning er aktiveret). Disse detaljer kan sendes til udvikleren, hvis autofyld ikke virker som forventet.</string>
<string name="requires_android11">Kræver Android 11 eller senere.</string>
<string name="kp2a_findUrl">Find adgangskode</string>
<string name="excludeExpiredEntries">Udelad udløbne poster</string>
@@ -519,7 +521,6 @@
<string name="filestoragename_gdrive">Google Drev</string>
<string name="filestoragename_gdriveKP2A">Google Drev (KP2A filer)</string>
<string name="filestoragehelp_gdriveKP2A">Hvis du ikke ønsker at give KP2A adgang til hele dit Google Drev, kan du vælge denne mulighed. Bemærk, at du skal oprette en databasefil først, eksisterende filer er ikke synlige for appen. Du kan enten vælge denne mulighed fra skærmen Opret database eller, hvis du allerede har åbnet en database, ved at vælge muligheden for at eksportere databasen.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Denne lagringstype vil kun anmode om adgang til pCloud-mappen \"Applications/Keepass2Android\". Hvis du vil benytte en eksisterende database fra din pCloud-konto, skal du sørge for, at filen er placeret i denne pCloud-mappe.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -689,7 +690,7 @@
<item>Fejlrettelse til nedbrud og uventede log-outs</item>
<item>Skift til ny SFTP-implementering, som understøtter moderne offentlige nøglealgoritmer såsom rsa-sha2-256</item>
<item>Markér adgangskoder som følsomme ved kopiering til udklipsholder (Android 13)</item>
<item>Autofill improvements</item>
<item>Forbedringer af autofyld</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Tilføjet understøttelse af visning, fjernelse og gendannelse af sikkerhedskopierede poster</item>
@@ -947,7 +948,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>Adgangskode</item>
<item>Privat/Offentlig nøgle</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignorér certifikatgodkendelsesfejl</item>

View File

@@ -3,7 +3,7 @@
<resources>
<string name="about_feedback">Feedback</string>
<string name="AboutText">Keepass2Android ist ein Passwortmanager mit Lese- und Schreib-Zugriff auf KeePass 2.x Datenbanken.</string>
<string name="CreditsText">Die Benutzeroberfläche basiert auf einem Port von KeePassDroid (entwickelt von Brian Pellin) nach Mono for Android. Der Code für die Datenbank-Operationen nutzt eine angepasste Version einer Bibliothek aus KeePass (entwickelt von Dominik Reichl).
<string name="CreditsText">Die Benutzeroberfläche basiert auf einem Port von Keepassdroid (entwickelt von Brian Pellin) nach Mono for Android. Der Code für die Datenbank-Operationen nutzt eine angepasste Version einer Bibliothek aus KeePass (entwickelt von Dominik Reichl).
Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die Google durchgeführt und zur Verfügung gestellt hat; er wird nach den Bedingungen der Creative Commons 3.0 Attribution License genutzt.</string>
<string name="CreditsTextSFTP">SFTP-Unterstützung ist implementiert unter Nutzung der JSch-Bibliothek (BSD-Lizenz), erstellt durch JCraft, Inc.</string>
<string name="CreditsIcons">Das Hammer-Icon wurde von John Caserta (Noun Project) erstellt. Das Pinguin-Icon wurde von Adriano Emerick (Noun Project) erstellt. Das Feder-Icon wurde von Jon Testa (Noun Project) erstellt. Das Apfel-Icon wurde von Ava Rowell (Noun Project) erstellt. Das Bild-Icon stammt von https://icons8.com/icon/5570/Picture.</string>
@@ -162,9 +162,9 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="InvalidPassword">Passwort oder Schlüsseldatei ungültig.</string>
<string name="invalid_algorithm">Ungültiger Algorithmus.</string>
<string name="invalid_db_sig">Datenbank-Format wurde nicht erkannt.</string>
<string name="keyfile_does_not_exist">Schlüsseldatei existiert nicht.</string>
<string name="keyfile_does_not_exist">Schlüssel-Datei existiert nicht.</string>
<string name="no_keyfile_selected">Keine Schlüsseldatei ausgewählt.</string>
<string name="keyfile_is_empty">Schlüsseldatei ist leer.</string>
<string name="keyfile_is_empty">Schlüssel-Datei ist leer.</string>
<string name="length">Länge</string>
<string name="list_size_title">Größe der Gruppenliste</string>
<string name="list_size_summary">Schriftgröße in der Gruppenliste</string>
@@ -302,6 +302,8 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="NoDalVerification_summary">Deaktiviert die Prüfung, ob Domain und App-Package zueinander passen</string>
<string name="InlineSuggestions_title">In die Tastatur integrieren</string>
<string name="InlineSuggestions_summary">Zeigt Vorschläge für automatisches Ausfüllen innerhalb der virtuellen Tastatur (wenn von dieser unterstützt)</string>
<string name="LogAutofillView_title">Autofill-Ansicht loggen</string>
<string name="LogAutofillView_summary">Schreibt Details über die Autofill-Ansicht in das Fehlerprotokoll (wenn die Fehlerprotokollierung aktiviert ist). Diese Details können an den Entwickler gesendet werden, wenn das automatische Ausfüllen nicht wie erwartet funktioniert.</string>
<string name="requires_android11">Benötigt Android 11 oder neuer</string>
<string name="kp2a_findUrl">Passwort finden</string>
<string name="excludeExpiredEntries">Abgelaufene Einträge ausschließen</string>
@@ -520,7 +522,6 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A-Dateien)</string>
<string name="filestoragehelp_gdriveKP2A">Wenn KP2A keinen Zugriff auf das komplette eigenen Google Drive erhalten soll, kann diese Option gewählte werden. Bitte beachten, dass zuerst eine Datenbankdatei erstellt werden muss; vorhandene Dateien sind für die App nicht sichtbar. Diese Option entweder auf dem Bildschirm „Datenbank erstellen“ auswählen oder, wenn bereits eine Datenbank geöffnet wurde, indem die Datenbank mit dieser Option exportiert wird.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Dieser Speichertyp verlangt nur Zugriff auf den pCloud-Ordner „Applications/Keepass2Android“. Wenn eine vorhandene Datenbank aus dem eigenen pCloud-Konto verwendet werden soll, bitte sicherstellen, dass die Datei in diesem pCloud-Ordner gespeichert ist.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -688,7 +689,7 @@ Anbei einige Tipps, die bei der Diagnose des Problems helfen können:\n
<item>Fehlerbehebung, um Abstürze und unerwartetes Abmelden zu vermeiden</item>
<item>Wechsel auf eine neue SFTP-Implementierung, jetzt mit Unterstützung von modernen Public-Key-Algorithmen wie rsa-sha2-256</item>
<item>Passwörter werden beim Kopieren in die Zwischenablage als vertraulich markiert (Android 13)</item>
<item>Autofill improvements</item>
<item>Autofill Verbesserungen</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Unterstützung für das Ansehen, Entfernen und Wiederherstellen von Eintragssicherungen hinzugefügt</item>
@@ -1050,7 +1051,8 @@ Erstes öffentliches Release</string>
</string-array>
<string-array name="sftp_auth_modes">
<item>Passwort</item>
<item>Privater/Öffentlicher Schlüssel</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Fehler bei Zertifikatsüberprüfung ignorieren</item>

View File

@@ -521,7 +521,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (αρχεία KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Αν δεν θέλετε να δώσετε πρόσβαση KP2A πρόσβαση σε όλο το Google Drive σας, μπορείτε να επιλέξετε αυτή την επιλογή. Σημειώστε ότι πρέπει να δημιουργήσετε ένα αρχείο βάσης δεδομένων πρώτα - τα υπάρχοντα αρχεία δεν είναι ορατά στην εφαρμογή. Επιλέξτε αυτή την επιλογή από την οθόνη Δημιουργία βάσης δεδομένων ή, αν έχετε ήδη ανοίξει μια βάση δεδομένων, με την εξαγωγή της βάσης δεδομένων επιλέγοντας αυτή την επιλογή.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Αυτός ο τύπος αποθήκευσης θα ζητήσει πρόσβαση μόνο στον pCloud φάκελο \"Applications/Keepass2Android\". Αν θέλετε να χρησιμοποιήσετε μια υπάρχουσα βάση δεδομένων από τον pCloud λογαριασμό σας, βεβαιωθείτε ότι το αρχείο έχει τοποθετηθεί σε αυτόν τον φάκελο.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -1050,7 +1049,8 @@ Initial public release
</string-array>
<string-array name="sftp_auth_modes">
<item>Συνθηματικό</item>
<item>Ιδιωτικό/Δημόσιο κλειδί</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Παράβλεψη αποτυχιών επικύρωσης πιστοποιητικού</item>

View File

@@ -71,7 +71,7 @@
<string name="copy_password">Seleccionar para copiar la contraseña al portapapeles</string>
<string name="copy_totp">Seleccionar para copiar el TOTP al portapapeles</string>
<string name="available_through_keyboard">La entrada está disponible a través del teclado KP2A</string>
<string name="app_language_pref_title">Idioma de aplicación</string>
<string name="app_language_pref_title">Idioma de la aplicación</string>
<string name="entry_is_available">está disponible</string>
<string name="not_possible_im_picker">No se puede abrir el cuadro de diálogo para seleccionar el método de entrada. Por favor, active el teclado manualmente.</string>
<string name="please_activate_keyboard">Por favor habilita el teclado de Keepass2Android en la configuración del sistema.</string>
@@ -83,7 +83,7 @@
<string name="disclaimer_formal">Keepass2Android NO TIENE TOTAL GARANTÍA; Este es software libre, y puedes redristribuirlo bajo las condiciones de la licencia GPL version 2 o posterior.</string>
<string name="ellipsis">\u2026</string>
<string name="copy_to_clipboard">Copiar al portapapeles</string>
<string name="SystemLanguage">Idioma del sistema</string>
<string name="SystemLanguage">Idioma</string>
<string name="fingerprint_description">Por favor, autentifícate para continuar</string>
<string name="fingerprint_fatal">No se puede configurar desbloqueo con huella dactilar:</string>
<string name="fingerprint_not_recognized">La autenticación biométrica falló. Inténtalo de nuevo</string>
@@ -152,6 +152,7 @@
<string name="hint_keyfile">archivo de clave</string>
<string name="hint_length">tamaño</string>
<string name="hint_pass">contraseña</string>
<string name="hint_keyfile_path">Ruta de la clave privada SSH</string>
<string name="hint_login_pass">Contraseña</string>
<string name="hint_title">nombre</string>
<string name="hint_url">URL</string>
@@ -301,6 +302,8 @@
<string name="NoDalVerification_summary">Deshabilita comprobar si el dominio y el paquete de la aplicación coinciden</string>
<string name="InlineSuggestions_title">Integrar con el teclado</string>
<string name="InlineSuggestions_summary">Muestra las sugerencias de autorrelleno como opciones en línea en el teclado (si el método de entrada lo admite)</string>
<string name="LogAutofillView_title">Vista de autorrelleno del registro</string>
<string name="LogAutofillView_summary">Escribe detalles sobre la vista de autorrelleno en el registro de depuración (si el registro de depuración está activado). Estos detalles se pueden enviar al desarrollador si el autorrelleno no funciona como se esperaba.</string>
<string name="requires_android11">Requiere Android 11 o posterior</string>
<string name="kp2a_findUrl">Encuentra una contraseña</string>
<string name="excludeExpiredEntries">Excluir las entradas caducadas</string>
@@ -415,6 +418,7 @@
<string name="ShowUnlockedNotification_summary">Mostrar una notificación en curso mientras la base de datos está desbloqueada.</string>
<string name="IconVisibilityInfo_Android8_text">Android 8 ha introducido nuevo comportamiento para notificaciones. Si desea ocultar el icono de notificaciones de Keepass2Android, por favor configúrelo en ajustes del sistema. Establezca la importancia de la categoría de notificación al mínimo.</string>
<string name="IconVisibilityInfo_Android8_btnSettings">Abrir ajustes</string>
<string name="PostNotificationsPermissionInfo_text">Keepass2Android puede mostrar una notificación del sistema mientras su base de datos no está bloqueada. Para que esto funcione, por favor concede permiso.</string>
<string name="DontCare">No me interesa</string>
<string name="DocumentAccessRevoked">El archivo ya no es accesible a Keepass2Android. Ha sido eliminado o han sido revocados los permisos de acceso. Por favor use re-abrir el archivo, por ejemplo, con Cambiar base de datos.</string>
<string name="PreloadDatabaseEnabled_title">Pre-cargar archivo de base de datos</string>
@@ -499,9 +503,27 @@
<string name="hint_sftp_host">host (ejemplo: 192.168.0.1)</string>
<string name="hint_sftp_port">Puerto</string>
<string name="initial_directory">Directorio inicial (opcional):</string>
<string name="connect_timeout">Tiempo de espera de la conexión en segundos (opcional):\"</string>
<string name="enter_sftp_login_title">Introduzca los datos de acceso SFTP:</string>
<string name="sftp_auth_mode">Modo de autenticación</string>
<string name="send_public_key">Enviar clave pública...</string>
<string name="select_private_keyfile">Seleccionar clave privada...</string>
<string name="hint_sftp_key_name">Nuevo nombre de clave</string>
<string name="hint_sftp_key_content">Nuevo contenido de clave</string>
<string name="private_key_saved">Clave privada guardada</string>
<string name="private_key_save_failed">FALLO al guardar la clave privada: %1$s</string>
<string name="private_key_info">Introduce el nombre y el contenido de la clave para guardarla</string>
<string name="private_key_delete">Clave privada eliminada: %1$s</string>
<string name="private_key_delete_failed">FALLO al borrar la clave privada: %1$s</string>
<string name="save_key">Guardar clave privada</string>
<string name="delete_key">Eliminar clave privada</string>
<string name="private_key_select">Clave privada seleccionada</string>
<string name="private_key_create_new">[Añadir nuevo...]</string>
<string name="hint_sftp_key_passphrase">Frase clave (opcional)</string>
<string name="sftp_kex_title">Algoritmo(s) de intercambio de claves (KEX) (opcional)</string>
<string name="hint_sftp_kex">\"Nombres/especificaciones separadas por comas</string>
<string name="sftp_shk_title">Algoritmo(s) de clave de host del servidor (opcional)</string>
<string name="hint_sftp_shk">\"Nombres/especificaciones separadas por comas</string>
<string name="enter_ftp_login_title">Introducir datos de acceso FTP:</string>
<string name="enter_mega_login_title">Digite las credenciales de su cuenta en MEGA:</string>
<string name="select_storage_type">Seleccione el tipo de almacenamiento:</string>
@@ -519,7 +541,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (archivos KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Si no deseas brindarle acceso completo a KP2A para acceder a todos los archivos en Google Drive, debes seleccionar esta opción. Recuerda que primero debes crear una base de datos y los archivos existentes no son visibles para la aplicación. Selecciona esta opción desde la pantalla de \"Crear una base de datos\" o, si ya abriste una base de datos, puedes exportarla seleccionando esta opción.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Este tipo de almacenamiento sólo solicitará acceso a la carpeta de pCloud \"Aplicaciones/Keepass2Android\". Si desea utilizar una base de datos existente de su cuenta pCloud, asegúrese de que el archivo se coloca en dicha carpeta de pCloud.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -572,6 +593,7 @@
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Fichero registro para Depuración</string>
<string name="DebugLog_title">Usar el archivo de registro</string>
<string name="JSchDebug_title">Registro de depuración SFTP</string>
<string name="DebugLog_summary">Escribir salida de app a fichero local de log</string>
<string name="DebugLog_send">Enviar registro de depuración...</string>
<string name="loading">Cargando…</string>
@@ -685,6 +707,12 @@
<string name="EntryChannel_desc">Notificación para simplificar el acceso a la entrada seleccionada actualmente.</string>
<string name="CloseDbAfterFailedAttempts">Cierre la base de datos después de tres intentos fallidos de desbloqueo biométrico.</string>
<string name="WarnFingerprintInvalidated">¡Atención! La autenticación biométrica puede ser invalidada por Android, p. ej. después de añadir una nueva huella dactilar en los ajustes de su dispositivo. ¡Esté seguro de conocer siempre cómo desbloquear con su contraseña maestra!</string>
<string-array name="ChangeLog_1_09e">
<item>Corrección de fallos y cierres de sesión inesperados</item>
<item>Cambiar a una nueva implementación de SFTP, compatible con algoritmos modernos de clave pública como rsa-sha2-256.</item>
<item>Marcar contraseñas como sensibles al copiarlas al portapapeles (Android 13)</item>
<item>Mejoras en el autorrelleno</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Añadido el soporte para visualizar, remover y restaurar los respaldos de las entradas</item>
<item>Implementado el soporte para el servicio en la nube de MEGA</item>
@@ -1058,7 +1086,8 @@ Publicación inicial</string>
</string-array>
<string-array name="sftp_auth_modes">
<item>Contraseña</item>
<item>Clave pública/privada</item>
<item>Clave K2A privada/pública</item>
<item>Clave privada personalizada</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignorar errores de validación de certificado</item>
@@ -1076,6 +1105,11 @@ Publicación inicial</string>
<string name="autofill_enable_for">Activar Autorrelleno para %1$s</string>
<string name="invalid_link_association">No se pudo asociar el dominio web %1$s con la app %2$s</string>
<string name="enable_fingerprint_hint">Keepass2Android ha detectado hardware biométrico. ¿Desea habilitar el desbloqueo biométrico para esta base de datos?</string>
<string name="post_notifications_dialog_title">Permitir notificaciones</string>
<string name="post_notifications_dialog_message">Keepass2Android puede mostrar notificaciones con botones para copiar valores como contraseñas y TOTPs al portapapeles, o para abrir el teclado integrado. Esto es útil para transferir valores a otras aplicaciones sin cambiar a Keepass2Android repetidamente. ¿Deseas activar estas notificaciones?</string>
<string name="post_notifications_dialog_allow">Permitir notificaciones</string>
<string name="post_notifications_dialog_disable">Desactivar esta función</string>
<string name="post_notifications_dialog_notnow">Ahora no</string>
<string name="understand">Entiendo</string>
<string name="dont_show_again">No volver a mostrar</string>
<string name="masterkey_infotext_head">¿Recuerda su contraseña maestra?</string>

View File

@@ -314,7 +314,6 @@
<string name="filestoragename_dropbox">Dropbox</string>
<string name="filestoragename_dropboxKP2A">Dropbox (KP2A karpeta)</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_myfiles">Nire fitxategiak</string>
@@ -459,7 +458,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>Pasahitza</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignore certificate validation failures</item>

View File

@@ -532,7 +532,6 @@ mycloud.me.com/webdav/ )</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">گوگل‌درایو (فایل‌های KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">اگر می‌خواهید گوگل‌درایوتان به‌طور کامل در دسترس KP2A نباشد، این گزینه را انتخاب کنید. توجه نمایید که ابتدا باید یک فایل پایگاه‌داده بسازید. برنامه نمی‌تواند فایل‌های موجود را ببیند. این گزینه را یا از صفحهٔ ساخت پایگاه‌داده انتخاب کنید یا اگر هم‌اکنون یک پایگاه‌داده باز کرده‌اید از طریق صادرکردن پایگاه‌داده و انتخاب این گزینه اقدام کنید.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_full">همهٔ فایل‌ها و فایل‌های اشتراک‌گذاری‌شده</string>
@@ -813,7 +812,8 @@ mycloud.me.com/webdav/ )</string>
</string-array>
<string-array name="sftp_auth_modes">
<item>گذرواژه</item>
<item>کلید خصوصی/عمومی</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>اعتبارسنجی‌نشدن گواهی را نادیده بگیر</item>

View File

@@ -174,6 +174,7 @@
<string name="maskpass_summary">Piilota salasanat oletuksena</string>
<string name="masktotp_title">Peitä TOTP-kenttä</string>
<string name="masktotp_summary">Piilota TOTP-kenttä oletuksena</string>
<string name="NoAutofillDisabling_title">Automaattisen täytön poistaminen käytöstä ei ole mahdollista</string>
<string name="NoAutofillDisabling_summary">Jos aktivoitu, sovellus ei näytä vaihtoehtoa poistaa automaattista täyttöä tiettyjen merkintöjen osalta.</string>
<string name="menu_about">Tietoja</string>
<string name="menu_change_key">Vaihda pääavain</string>
@@ -281,6 +282,7 @@
<string name="author">Keepass2Androidin on kehittänyt Philipp Crocoll.</string>
<string name="further_authors">Kiitokset kehitykseen osallistuneille: %1$s.</string>
<string name="designers">Kiitokset kuvakkeiden ja ohjelman ulkonäön suunnitteluun osallistuneille: %1$s.</string>
<string name="supporters">Kiitos taloudellisesta tuesta %1$s.</string>
<string name="credit_plugin1">Keepass Twofish Cipher -lisäosan on kehittänyt Scott Greenberg ja se sisältyy KP2A:han.</string>
<string name="credit_android_filechooser">Android-tiedostonvalitsimen on kehittänyt Hai Bison</string>
<string name="credit_keyboard">KP2A Android -näppäimistö perustuu Gingerbread-näppäimistöön ja Android Open Source Projectin avoimeen lähdekoodiin. Se käyttää Klaus Weidnerin kehittämästä Hacker\'s Keyboardista liitännäisten hallinnointikoodia.</string>
@@ -299,6 +301,8 @@
<string name="NoDalVerification_summary">Poistaa käytöstä verkkotunnuksen ja sovelluspaketin täsmäämisen tarkistuksen</string>
<string name="InlineSuggestions_title">Integroi näppäimistön kanssa</string>
<string name="InlineSuggestions_summary">Näyttää automaattisen täytön ehdotukset näppäimistön sisäkkäisinä valintoina (jos syöte on tuettu)</string>
<string name="LogAutofillView_title">Automaattisen täytön näkymä lokiin</string>
<string name="LogAutofillView_summary">Kirjoita tiedot automaattisen täytön näkymästä virheenjäljityslokiin (jos virheenjäljitysloki on käytössä). Nämä tiedot voidaan lähettää kehittäjälle, jos automaattinen täyttö ei toimi odotetusti.</string>
<string name="requires_android11">Vaatii Android 11 tai uudemman</string>
<string name="kp2a_findUrl">Etsi salasana</string>
<string name="excludeExpiredEntries">Sivuuta vanhentuneet merkinnät</string>
@@ -328,6 +332,7 @@
<string name="QuickUnlockHideLength_title">Piilota pika-avauskoodin pituus</string>
<string name="QuickUnlockHideLength_summary">Jos käytössä, pika-avauskoodin pituutta ei näytetä pika-avausnäytössä.</string>
<string name="QuickUnlockKeyFromDatabase_title">QuickUnlock-avain tietokannan syötteestä</string>
<string name="QuickUnlockKeyFromDatabase_summary">Jos aktiivinen tietokanta sisältää otsikon QuickUnlock sen juuriryhmässä, tämän merkinnän salasanaa käytetään pika-avauksen koodina.</string>
<string name="QuickUnlock_fail">Pika-avaus epäonnistui: väärä salasana!</string>
<string name="SaveAttachmentDialog_title">Tallenna liite</string>
<string name="SaveAttachmentDialog_text">Valitse liitteen tallennuspaikka.</string>
@@ -348,6 +353,10 @@
<string name="add_extra_string">Lisää lisärivi</string>
<string name="configure_totp">Määritä TOTP</string>
<string name="totp_secret_key">Salainen avain</string>
<string name="totp_encoding_rfc6238">Oletusarvoiset RFC6238 token asetukset</string>
<string name="totp_encoding_steam">Steamin token asetukset</string>
<string name="totp_encoding_custom">Mukautetut tokenin asetukset</string>
<string name="totp_time_step">Aikaväli</string>
<string name="totp_length">Koodin pituus</string>
<string name="totp_scan">Skannaa QR-koodi</string>
<string name="delete_extra_string">Poista lisärivi</string>
@@ -413,6 +422,7 @@
<string name="PreloadDatabaseEnabled_title">Esilataa tietokanta</string>
<string name="PreloadDatabaseEnabled_summary">Aloita tietokannan lataus taustalla jo salasanaa syötettäessä.</string>
<string name="SyncAfterQuickUnlock_title">Synkronoi QuickUnlock jälkeen</string>
<string name="SyncAfterQuickUnlock_summary">Synkronoi tietokanta etätiedoston kanssa pika-avauksen käyttämisen jälkeen.</string>
<string name="AskOverwriteBinary">Haluatko korvata nykyisen saman nimisen binääritiedoston?</string>
<string name="AskOverwriteBinary_title">Haluatko korvata nykyisen binääritiedoston?</string>
<string name="AskOverwriteBinary_yes">Korvaa</string>
@@ -508,10 +518,10 @@
<string name="filestoragename_dropbox">Dropbox</string>
<string name="filestoragename_dropboxKP2A">Dropbox (KP2A kansio)</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_myfiles">Omat tiedostot</string>
<string name="filestoragename_onedrive2_appfolder">Keepass2Android-sovelluksen kansio</string>
<string name="filestoragename_sftp">SFTP (SSH-tiedostonsiirto)</string>
<string name="filestoragename_mega">MEGA</string>
<string name="filestoragename_content">Järjestelmän tiedostonvalitsin</string>
@@ -576,6 +586,7 @@
<string name="SCOPE_CURRENT_ENTRY_title">Nykyisen merkinnän tiedot</string>
<string name="SCOPE_CURRENT_ENTRY_explanation">Liitännäiselle lähetetään kaikki merkinnän tiedot ja se saa luvan tarjota toimintoja ja muuttaa merkinnän näkymää.</string>
<string name="SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE_title">Pyydä omia tunnuksia</string>
<string name="SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE_explanation">Liitännäinen saa pyytää omaan sovelluspakettiinsa liittyviä tunnuksia.</string>
<string name="SCOPE_QUERY_CREDENTIALS_title">Pyydä tunnuksia</string>
<string name="get_regular_version">Hanki lisää tallennusmuotoja</string>
<string name="CertificateWarning">Varoitus: Palvelimen varmenteen vahvistus epäonnistui: %1$s. Asenna tarvittava juurivarmenne laitteeseen tai katso asetukset!</string>
@@ -956,7 +967,8 @@ Suojelee sinua Leikepöytään perustuvalta salasana sniffaukselta (Poista vanha
</string-array>
<string-array name="sftp_auth_modes">
<item>Salasana</item>
<item>Yksityinen/Julkinen avain</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ohita varmenteiden virheet</item>

View File

@@ -152,6 +152,7 @@
<string name="hint_keyfile">clé de fichier</string>
<string name="hint_length">longueur</string>
<string name="hint_pass">mot de passe</string>
<string name="hint_keyfile_path">Chemin de la clé privée SSH</string>
<string name="hint_login_pass">Mot de passe</string>
<string name="hint_title">nom</string>
<string name="hint_url">URL</string>
@@ -301,6 +302,8 @@
<string name="NoDalVerification_summary">Désactive la vérification si le domaine et le package d\'application correspondent</string>
<string name="InlineSuggestions_title">Intégrer au clavier</string>
<string name="InlineSuggestions_summary">Affiche les suggestions de saisie automatique comme options en ligne dans le clavier (si pris en charge par la méthode de saisie)</string>
<string name="LogAutofillView_title">Enregistrer la vue de remplissage automatique</string>
<string name="LogAutofillView_summary">Écrire des détails sur la vue de remplissage automatique dans le journal de débogage (si le journal de débogage est activé). Ces détails peuvent être envoyés au développeur si le remplissage automatique ne fonctionne pas comme prévu.</string>
<string name="requires_android11">Nécessite Android 11 ou supérieur</string>
<string name="kp2a_findUrl">Trouver un mot de passe</string>
<string name="excludeExpiredEntries">Exclure les entrées arrivées à expiration</string>
@@ -415,6 +418,7 @@
<string name="ShowUnlockedNotification_summary">Afficher une notification pendant que la base de données est déverrouillée.</string>
<string name="IconVisibilityInfo_Android8_text">Android 8 a introduit un nouveau comportement pour les notifications. Si vous souhaitez masquer licône pour les notifications de Keepass2Android, veuillez le configurer via le panneau de configuration. Définissez limportance de la catégorie de notification à Minimum.</string>
<string name="IconVisibilityInfo_Android8_btnSettings">Ouvrir les paramètres</string>
<string name="PostNotificationsPermissionInfo_text">Keepass2Android peut afficher une notification système pendant que votre base de données n\'est pas verrouillée. Pour que ceci fonctionne, veuillez accorder l\'autorisation.</string>
<string name="DontCare">Peu importe</string>
<string name="DocumentAccessRevoked">Le fichier n\'est plus accessible pour Keepass2Android. Soit il a été supprimé ou les permissions d\'accès ont été révoquées. Merci de ré-ouvrir le fichier, par exemple en utilisant Changer de base de données.</string>
<string name="PreloadDatabaseEnabled_title">Préchargement de la base de données</string>
@@ -499,9 +503,26 @@
<string name="hint_sftp_host">Adresse IP du serveur (ex : 192.168.0.1)</string>
<string name="hint_sftp_port">port</string>
<string name="initial_directory">Répertoire initial (facultatif) :</string>
<string name="connect_timeout">Délai de connexion en secondes (optionnel) : \"</string>
<string name="enter_sftp_login_title">Entrez les données de connexion SFTP :</string>
<string name="sftp_auth_mode">Mode d\'authentification</string>
<string name="send_public_key">Envoyez la clé publique...</string>
<string name="select_private_keyfile">Sélectionner une clé privée...</string>
<string name="hint_sftp_key_name">Nom de la nouvelle clé</string>
<string name="hint_sftp_key_content">Contenu de la nouvelle clé</string>
<string name="private_key_saved">Clé privée enregistrée</string>
<string name="private_key_save_failed">ÉCHEC d\'enregistrement de la clé privée : %1$s</string>
<string name="private_key_info">Entrer le nom et le contenu de la clé à enregistrer</string>
<string name="private_key_delete">Clé privée supprimée : %1$s</string>
<string name="private_key_delete_failed">ÉCHEC de suppression de la clé privée : %1$s</string>
<string name="save_key">Enregistrer la clé privée</string>
<string name="delete_key">Supprimer la clé privée</string>
<string name="private_key_select">Clé privée sélectionnée</string>
<string name="private_key_create_new">[Ajouter une nouvelle...]</string>
<string name="hint_sftp_key_passphrase">Mot de passe de la clé (facultatif)</string>
<string name="sftp_kex_title">Algorithme(s) d\'échange de clés (KEX) (facultatif)</string>
<string name="hint_sftp_kex">\"Nom/spécification séparés par des virgules</string>
<string name="hint_sftp_shk">\"Nom/spécification séparés par des virgules</string>
<string name="enter_ftp_login_title">Saisir les données de connexion FTP :</string>
<string name="enter_mega_login_title">Entrez les données de connexion de votre compte MEGA :</string>
<string name="select_storage_type">Sélectionner le type de stockage :</string>
@@ -519,7 +540,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (fichiers KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Si vous ne voulez pas donner à KP2A un accès complet à Google Drive, vous pouvez sélectionner cette option. Notez que vous devez d\'abord créer un fichier de base de données, les fichiers existants ne sont pas visibles pour l\'application. Choisissez cette option dans l\'écran Créer une base de données ou, si vous avez déjà ouvert une base de données, en exportant la base de données en choisissant cette option.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Ce type de stockage ne demandera l\'accès qu\'au dossier \"Applications/Keepass2Android\". Si vous souhaitez utiliser une base de données existante à partir de votre compte PCloud, assurez-vous que le fichier soit placé dans ce dossier.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -572,6 +592,7 @@
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Fichier journal pour le débogage</string>
<string name="DebugLog_title">Utiliser fichier de journal</string>
<string name="JSchDebug_title">Enregistrement de débogage SFTP</string>
<string name="DebugLog_summary">Écrire la sortie de lapp dans un fichier journal local</string>
<string name="DebugLog_send">Envoyer le journal de débogage...</string>
<string name="loading">Chargement en cours…</string>
@@ -687,7 +708,7 @@ Voici quelques conseils qui pourraient aider à diagnostiquer le problème : \n
<item>Correction de bugs pour les plantages et les déconnexions inattendues</item>
<item>Passage à une nouvelle implémentation SFTP, prenant en charge les algorithmes à clé publique modernes tels que rsa-sha2-256</item>
<item>Marquer les mots de passe comme sensibles lors de la copie dans le presse-papiers (Android 13)</item>
<item>Autofill improvements</item>
<item>Améliorations du remplissage automatique</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Ajout du support pour la visualisation, la suppression et la restauration des sauvegardes d\'entrée</item>
@@ -996,7 +1017,8 @@ Première version publique</string>
</string-array>
<string-array name="sftp_auth_modes">
<item>Mot de passe</item>
<item>Clé privée/publique</item>
<item>Clé privée/publique K2A</item>
<item>Clé privée personnalisée</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignorer les échecs de validation de certificat</item>
@@ -1014,6 +1036,11 @@ Première version publique</string>
<string name="autofill_enable_for">Activer le remplissage automatique pour %1$s</string>
<string name="invalid_link_association">Impossible d\'associer le domaine web %1$s avec l\'application %2$s</string>
<string name="enable_fingerprint_hint">Keepass2Android a détecté un matériel biométrique. Voulez-vous activer le déverrouillage biométrique pour cette base de données ?</string>
<string name="post_notifications_dialog_title">Autoriser les notifications</string>
<string name="post_notifications_dialog_message">Keepass2Android peut afficher des notifications avec des boutons pour copier des valeurs telles que les mots de passe et les TOTPs dans le presse-papiers, ou pour afficher le clavier intégré. Ceci est utile pour transférer des valeurs dans d\'autres applications sans basculer vers Keepass2Android à plusieurs reprises. Voulez-vous activer de telles notifications ?</string>
<string name="post_notifications_dialog_allow">Autoriser les notifications</string>
<string name="post_notifications_dialog_disable">Désactiver cette fonctionnalité</string>
<string name="post_notifications_dialog_notnow">Plus tard</string>
<string name="understand">Je comprends</string>
<string name="dont_show_again">Ne plus afficher</string>
<string name="masterkey_infotext_head">Vous rappelez-vous de votre mot de passe principal ?</string>

View File

@@ -834,7 +834,8 @@ Lanzamento público inicial
</string-array>
<string-array name="sftp_auth_modes">
<item>Contrasinal</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignorar fallos de validación de certificados</item>

View File

@@ -267,6 +267,7 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>סיסמה</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
</resources>

View File

@@ -503,7 +503,6 @@
<string name="filestoragename_dropboxKP2A">Dropbox (KP2A mapa)</string>
<string name="filestoragehelp_dropboxKP2A">Ako ne želite dati KP2A puni pristup svom Dropboxu, možete odabrati ovu opciju. Tražit će samo pristup mapi Apps/Keepass2Android. To je posebno pogodno prilikom izrade nove baze podataka. Ako već imate bazu podataka, kliknite ovu opciju za stvaranje mape, a zatim stavite datoteku unutar mape (s računala) te ponovo odaberite ovu opciju za otvaranje datoteke.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_full">Sve datoteke i podjeljene datoteke</string>
@@ -951,7 +950,8 @@ Prvotna javna verzija </string>
</string-array>
<string-array name="sftp_auth_modes">
<item>Lozinka</item>
<item>Privatni/Javni ključ</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Zanemari pogreške potvrda certifikata</item>

View File

@@ -30,8 +30,8 @@
<string name="ShowGroupnameInSearchResult_title">A csoportnevek megjelenítése a keresési eredményeknél</string>
<string name="ShowGroupnameInSearchResult_resume">A keresési eredményeknél a csoportnevek megjelenítése a bejegyzések címe alatt. Hasznos, ha léteznek azonos című bejegyzések.</string>
<string name="NavigationToGroupCompleted_message">A megjelenített csoport: %1$s</string>
<string name="AutofillDisabledQueriesPreference_title">Tiltott AutoFill célpontok</string>
<string name="AutofillDisabledQueriesPreference_summary">Azoknak az alkalmazások és weboldalak listája, amelyek esetén az AutoFill szolgáltatás nem engedélyezett</string>
<string name="AutofillDisabledQueriesPreference_title">Automatikus kitöltésből kizárt célpontok</string>
<string name="AutofillDisabledQueriesPreference_summary">Azoknak az alkalmazások és weboldalak listája, amelyek esetén az automatikus kitöltés szolgáltatás nem engedélyezett</string>
<string name="OfferSaveCredentials_summary">Ha aktiválva van, az automatikusan kitölthető mezők kézzel történő kitöltése után a rendszer rá fog kérdezni, hogy kívánja-e az azonosítókat menteni.</string>
<string name="OfferSaveCredentials_title">Azonosítók mentésének felajánlása</string>
<string name="ShowGroupInEntry_title">A csoportnév megjelenítése a bejegyzések adatlapján.</string>
@@ -301,6 +301,8 @@
<string name="NoDalVerification_summary">Eltekintés az internettartomány-alkalmazás összetertozásának ellenőrzésétől</string>
<string name="InlineSuggestions_title">Billentyűzet-integráció</string>
<string name="InlineSuggestions_summary">Az Automatikus kitöltéssel kapcsolatos javaslatok megjelnítése a billentyűzet inline opcióiként (ha a billentyűzet ezt támogatja)</string>
<string name="LogAutofillView_title">Az automatikus kitöltés naplózása</string>
<string name="LogAutofillView_summary">Az automatikus kitöltési használatával kapcsolatos információk rögzítése a naplófájlba (ha a naplófájl használata engedélyezett). Ezeket az információkat elküldheti a fejlesztőnek, ha az automatikus kitöltés nem működik megfelelően.</string>
<string name="requires_android11">Android 11, vagy újabb verzió szükséges</string>
<string name="kp2a_findUrl">Jelszó keresése</string>
<string name="excludeExpiredEntries">Lejárt bejegyzések kizárása</string>
@@ -519,7 +521,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A fájlok)</string>
<string name="filestoragehelp_gdriveKP2A">Válassza ezt az opciót, ha nem akarja, hogy a KP2A a teljes Google Drive-hoz hozzáférjen. Fontos, hogy először egy új adatbázist kell létrehoznia, a korábban létrehozott fájlokat az alkalmazás nem fogja látni. Vagy válassza ezt az opciót az Új adatbázis létrehozása képernyőn, vagy, ha már létezik az opció, exportálja azt ennek ezzel az opcióval.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Ez a tárhelytípus csak az \"Applications/Keepass2Android\" mappához igényel hozzáférést. Ahhoz, hogy a PCloud tárhelyed egy meglévő adatbázisát használd, azt ebben az mappában kell elhelyezned.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -757,7 +758,8 @@ Kezdeti közösségi kiadás</string>
</string-array>
<string-array name="sftp_auth_modes">
<item>Jelszó</item>
<item>Titkos/nyilvános kulcs</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Továbblépés, figyelmeztetés nélkül</item>
@@ -771,8 +773,8 @@ Kezdeti közösségi kiadás</string>
<string name="autofill_enable_failed">Elnézést, úgy tűnik az Ön készüléke nem támogatja a rendszerbeállítások megnyitását az alkalmazásból. Kérjük indítsa el a rendszerbeállításokat és engedélyezze az Automatikus kitöltés szolgáltatást.</string>
<string name="show_autofill_help">Automatikus kitöltés segítség megjelenítése</string>
<string name="autofill_sign_in_prompt">Kitöltés Keepass2Androiddal</string>
<string name="autofill_disable">Az AutoFill tiltása %1$s esetén</string>
<string name="autofill_enable_for">Az AutoFill engedélyezése %1$s esetén</string>
<string name="autofill_disable">Az automatikus kitöltés tiltása %1$s esetén</string>
<string name="autofill_enable_for">Az automatikus kitöltés engedélyezése %1$s esetén</string>
<string name="invalid_link_association">Nem sikerült összerendelni a %1$s internettartományt a %2$s alkalmazással</string>
<string name="enable_fingerprint_hint">Biometrikus eszköz detektálva. Szeretné engedélyezni az adatbázis biometrikus feloldását?</string>
<string name="understand">Értem</string>

View File

@@ -21,6 +21,8 @@
<string name="app_name_nonet">Keepass2Android Offline</string>
<string name="short_app_name_nonet">KP2A Luring</string>
<string name="show_kill_app">Tombol-Tutup</string>
<string name="application">Aplikasi</string>
<string name="application_settings">Pengaturan aplikasi</string>
<string name="ShowGroupnameInSearchResult_title">Tampilkan nama grup dalam hasil pencarian</string>
<string name="ShowGroupnameInSearchResult_resume">Menampilkan nama grup di bawah judul entri dalam hasil pencarian. Berguna jika beberapa entri memiliki nama yang sama.</string>
<string name="NavigationToGroupCompleted_message">Grup yang tampil adalah: %1$s</string>
@@ -49,6 +51,7 @@
<string name="unlock_database_title">Buka basis data</string>
<string name="brackets">Tanda kurung</string>
<string name="cancel">Batal</string>
<string name="Ok">Oke</string>
<string name="ClearClipboard">Papan klip dihapus.</string>
<string name="clipboard_timeout">Batas waktu papan klip</string>
<string name="clipboard_timeout_summary">Waktu sebelum menghapus isi papan klip setelah menyalin nama pengguna atau sandi</string>
@@ -765,7 +768,8 @@ Rilis publik awal </string>
</string-array>
<string-array name="sftp_auth_modes">
<item>Kata kunci</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Mengabaikan kegagalan validasi sertifikat</item>

View File

@@ -521,7 +521,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (file KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Se non vuoi dare a KP2A accesso completo al tuo Google Drive completo, puoi selezionare questa opzione. Nota che è necessario prima creare un file di database, i file esistenti non sono visibili per l\'app. Scegli questa opzione dalla schermata Crea database o, se hai già aperto un database, esportando il database scegliendo questa opzione.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Questo tipo di archiviazione richiederà l\'accesso solo alla cartella pCloud \"Applicazioni/Keepass2Android\". Se desideri utilizzare un database esistente dal tuo account pCloud, assicurati che il file sia posizionato in questa cartella pCloud.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -1061,7 +1060,8 @@ Prima release pubblica
</string-array>
<string-array name="sftp_auth_modes">
<item>Password</item>
<item>chiave Privata/Pubblica</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignora gli errori di convalida del certificato</item>

View File

@@ -87,7 +87,7 @@
<string name="fingerprint_description">続行するには認証してください</string>
<string name="fingerprint_fatal">生体認証ロック解除を設定できません:</string>
<string name="fingerprint_not_recognized">生体認証に失敗しました。もう一度やり直してください。</string>
<string name="fingerprint_success">生体認証に成功しました</string>
<string name="fingerprint_success">生体認証を完了しました</string>
<string name="fingerprint_os_error">生体認証によるロック解除には、Android 6.0 以降が必要です。</string>
<string name="fingerprint_hardware_error">生体認証ハードウェアを発見できません。</string>
<string name="fingerprint_no_enrolled">このデバイスで生体認証が設定されていません。最初にシステムの設定で登録してください。</string>
@@ -521,7 +521,6 @@
<string name="filestoragename_gdrive">Google ドライブ</string>
<string name="filestoragename_gdriveKP2A">Google ドライブ (KP2A ファイル)</string>
<string name="filestoragehelp_gdriveKP2A">Google ドライブ全体へのアクセス権を KP2A に付与したくない場合はこちらを選択してください。このオプションを使用すると既存のファイルはアプリから見えないため、まずデータベースファイルを作成する必要があることに注意してください。新しいデータベースを作成する場合はこのまま続行してください。既存のデータベースをアップロードしたい場合はデータベースのエクスポートからこのオプションを実行してください。</string>
<string name="filestoragename_pcloud">pCloud</string>
<string name="filestoragehelp_pcloud">このストレージタイプは、pCloud フォルダー「Applications/Keepass2Android」へのアクセスのみを要求します。お使いの pCloud アカウント上の既存のデータベースを利用したい場合は、そのファイルが、この pCloud フォルダー内にあるかどうかを確認してください。</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -1070,7 +1069,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>パスワード</item>
<item>秘密鍵/公開鍵</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>証明書の検証エラーを無視</item>

View File

@@ -488,7 +488,6 @@
이미 데이터베이스 파일를 갖고 있다면, 이 옵션을 선택해서 폴더를 생성한 다음, 기존의 데이터베이스 파일을 생성된 폴더에 넣으십시오. (PC나 기타 경로를 통해서)
그 다음 이 옵션을 다시 선택하여 데이터베이스 파일을 여십시오.</string>
<string name="filestoragename_gdrive">Google 드라이브</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragename_onedrive">원드라이브</string>
<string name="filestoragename_onedrive2">원드라이브</string>
<string name="filestoragename_onedrive2_full">모든 파일 및 공유한 파일</string>
@@ -996,7 +995,8 @@ Keepass2Android는 오프라인에서도 데이터베이스 파일의 사용이
</string-array>
<string-array name="sftp_auth_modes">
<item>비밀번호</item>
<item>비공개/공개 키</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>인증서 유효성 검사 오류 무시</item>

View File

@@ -217,7 +217,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>പാസ്സ്‌വേർഡ്‌</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string name="understand">എനിക്ക് മനസിലായി</string>
<string name="dont_show_again">വീണ്ടും കാണിക്കരുത്</string>

View File

@@ -676,7 +676,8 @@ Her er noen tips som kan hjelpe med å feilsøke problemet:\n
</string-array>
<string-array name="sftp_auth_modes">
<item>Passord</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Overse bekreftelsesfeil i sertifikater</item>

View File

@@ -521,7 +521,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A bestanden)</string>
<string name="filestoragehelp_gdriveKP2A">Als je KP2A geen toegang wilt geven tot je volledige Google Drive, kunt je deze optie selecteren. Merk op dat je eerst een databasebestand moet aanmaken, bestaande bestanden zijn niet zichtbaar voor de app. Kies deze optie uit het database scherm aanmaken, of als je al een database hebt geopend door de database te exporteren die voor deze optie wordt gekozen.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Dit opslagtype zal alleen toegang vragen tot de pCloud map \"Applications/Keepass2Android\". Als je een bestaande database uit jouw PCloud account wil gebruiken, zorg dan dat het bestand in die pCloud map wordt geplaatst.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -1069,7 +1068,8 @@ Eerste publieke publicatie
</string-array>
<string-array name="sftp_auth_modes">
<item>Wachtwoord</item>
<item>Privé/Publieke sleutel</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Negeer mislukte certificaat validaties</item>

View File

@@ -145,6 +145,7 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>Passord</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
</resources>

View File

@@ -301,6 +301,8 @@
<string name="NoDalVerification_summary">Wyłącza sprawdzanie zgodności domeny i pakietu aplikacji</string>
<string name="InlineSuggestions_title">Zintegruj z klawiaturą</string>
<string name="InlineSuggestions_summary">Pokazuje sugestie autouzupełniania jako opcje wbudowane w klawiaturze (jeśli jest obsługiwana przez metodę wprowadzania)</string>
<string name="LogAutofillView_title">Widok autouzupełniania dziennika</string>
<string name="LogAutofillView_summary">Zapisz szczegóły dotyczące widoku autouzupełniania do dziennika debugowania (jeśli włączono logowanie debugowania). Szczegóły te można wysłać do dewelopera, jeśli autouzupełnianie nie działa zgodnie z oczekiwaniami.</string>
<string name="requires_android11">Wymaga Androida 11 lub późniejszego</string>
<string name="kp2a_findUrl">Znajdź hasło</string>
<string name="excludeExpiredEntries">Pomiń wygasłe wpisy</string>
@@ -519,7 +521,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Dysk Google (pliki KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Jeśli nie chcesz dać KP2A dostępu do pełnego Dysku Google, możesz wybrać tę opcję. Pamiętaj, że najpierw musisz utworzyć plik bazy danych, istniejące pliki nie są widoczne dla aplikacji. Wybierz tę opcję z ekranu tworzenia bazy danych lub, jeśli już otworzyłeś bazę danych, eksportując bazę danych wybierając tę opcję.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Ten typ magazynu będzie wymagał jedynie dostępu do folderu pCloud. \"Applications/Keepass2Android\". Jeżeli chcesz używać istniejącej bazy danych ze swojego konta pCloud, proszę upewnij się, że plik jest umieszczony w tym katalogu pCloud.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -690,7 +691,7 @@
<item>Naprawa błędów awarii i nieoczekiwanych wylogowań</item>
<item>Przełącz się na nową implementację SFTP, wspierając nowoczesne algorytmy klucza publicznego, takie jak rsa-sha2-256</item>
<item>Oznacz hasła jako wrażliwe podczas kopiowania do schowka (Android 13)</item>
<item>Autofill improvements</item>
<item>Ulepszenia autouzupełniania</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Dodano wsparcie dla przeglądania, usuwania i przywracania kopii zapasowych wpisów</item>
@@ -1049,7 +1050,8 @@ Podziękowania dla Niki Hüttner (www.close-cut.de) za nowe logo!\n </string>
</string-array>
<string-array name="sftp_auth_modes">
<item>Hasło</item>
<item>Klucz prywatny/publiczny</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignoruj niepowodzenia weryfikacji certyfikatów</item>

View File

@@ -24,7 +24,7 @@
<string name="app_timeout_summary">Tempo até que o banco de dados seja travado quando o aplicativo estiver inativo.</string>
<string name="kill_app_label">Encerrar o processo do aplicativo</string>
<string name="show_kill_app">Botão fechar</string>
<string name="show_kill_app_summary">Mostrar botão de fechar a aplicação na tela de palavra-chave (para utilizadores paranoicos)</string>
<string name="show_kill_app_summary">Mostrar um botão no ecrã de palavra-passe para matar o processo de aplicação (para utilizadores paranoicos)</string>
<string name="application">App</string>
<string name="application_settings">Configurações</string>
<string name="ShowGroupnameInSearchResult_title">Exibir o nome do grupo no resultado da pesquisa</string>
@@ -32,8 +32,8 @@
<string name="NavigationToGroupCompleted_message">Grupo de exibição é agora: %1$s</string>
<string name="AutofillDisabledQueriesPreference_title">Desabilitar Autopreenchimento para os alvos</string>
<string name="AutofillDisabledQueriesPreference_summary">Visualiza uma lista de aplicativos e sites para os quais o Autopreenchimento foi desativado</string>
<string name="OfferSaveCredentials_summary">Se ativado, o Android pergunta se deseja salvar as credenciais após você inserir manualmente dados em campos auto-preenchidos.</string>
<string name="OfferSaveCredentials_title">Oferecer opção de salvar credenciais</string>
<string name="OfferSaveCredentials_summary">Se ativado, o Android pergunta se deseja salvar as credenciais após que você inseriu manualmente dados em campos auto-preenchidos.</string>
<string name="OfferSaveCredentials_title">Oferecer salvação de credenciais</string>
<string name="ShowGroupInEntry_title">Mostrar nome do grupo na tela de entrada</string>
<string name="unknown_uri_scheme">O Keepass2Android não pode lidar com a URI %1$s. Entre em contato com o desenvolvedor!</string>
<string name="Entry_singular">Uma entrada</string>
@@ -47,7 +47,7 @@
<string name="FileHandling_prefs">Manipulação de arquivos</string>
<string name="keyboard_prefs">Teclado</string>
<string name="export_prefs">Exportar banco de dados...</string>
<string name="fingerprint_prefs">Desbloqueio por impressão digital</string>
<string name="fingerprint_prefs">Debloqueio por impressão digital</string>
<string name="import_db_prefs">Importar base de dados para pasta interna</string>
<string name="import_keyfile_prefs">Importar arquivo chave para pasta interna</string>
<string name="export_keyfile_prefs">Exportar arquivo-chave da pasta interna</string>
@@ -55,7 +55,7 @@
<string name="OnlyAvailableForLocalFiles">Só disponível para arquivos locais.</string>
<string name="FileIsInInternalDirectory">Arquivo armazenado no diretório interno.</string>
<string name="DatabaseFileMoved">Banco de dados copiado para a pasta interna. Clique em OK para abrir o novo local. Nota: Não se esqueça de exportar o banco de dados para um local seguro regularmente!</string>
<string name="KeyfileMoved">Arquivo de chaves copiado para a pasta interna. Verifique se você tem um backup seguro antes de excluir do local atual!</string>
<string name="KeyfileMoved">Arquivo de chaves copiado para a pasta interna. Verifique se que você tem um backup seguro antes de excluir do local atual!</string>
<string name="KeyfileMoveRequiresRememberKeyfile">Impossível usar a pasta interna enquanto o local do arquivo de chaves não for memorizado. Modifique as preferências de segurança.</string>
<string name="unlock_database_button">Desbloquear</string>
<string name="unlock_database_title">Desbloquear banco de dados</string>
@@ -82,15 +82,15 @@
<string name="digits">Dígitos</string>
<string name="disclaimer_formal">Keepass2Android vem com ABSOLUTAMENTE NENHUMA GARANTIA; Este é um software livre, e você está convidado a redistribui-lo sob as condições da GPL versão 2 ou posterior.</string>
<string name="ellipsis">\u2026</string>
<string name="copy_to_clipboard">Copiar para a área de transferência</string>
<string name="copy_to_clipboard">Copiar para a área de transferencia</string>
<string name="SystemLanguage">Idioma do sistema</string>
<string name="fingerprint_description">Confirme a digital para continuar</string>
<string name="fingerprint_fatal">Não consigo configurar Desbloqueio por Digital:</string>
<string name="fingerprint_not_recognized">Digital não reconhecida. Tente novamente</string>
<string name="fingerprint_not_recognized">Digital não reconhecida. Tente de novo</string>
<string name="fingerprint_success">Digital reconhecida</string>
<string name="fingerprint_os_error">Desbloqueio por Digital requer Android 6.0 ou posterior.</string>
<string name="fingerprint_hardware_error">Nenhum leitor de digital detectado.</string>
<string name="fingerprint_no_enrolled">Leitor biométrico não configurado no dispositivo. Por favor vá para configurações do sistema primeiro.</string>
<string name="fingerprint_no_enrolled">Você não tem digitais registradas neste dispositivo. Por favor vá para configurações do sistema primeiro.</string>
<string name="disable_fingerprint_unlock">Desabilitar o Desbloqueio Biométrico </string>
<string name="enable_fingerprint_unlock">Habilitar o Desbloqueio de Biométrico completo</string>
<string name="enable_fingerprint_quickunlock">Habilitar o Desbloqueio de Biométrico para o QuickUnlock</string>
@@ -154,6 +154,7 @@
<string name="hint_keyfile">arquivo chave</string>
<string name="hint_length">tamanho</string>
<string name="hint_pass">senha</string>
<string name="hint_keyfile_path">Caminho da chave privada SSH</string>
<string name="hint_login_pass">Senha</string>
<string name="hint_title">nome</string>
<string name="hint_url">URL</string>
@@ -419,6 +420,7 @@
<string name="ShowUnlockedNotification_summary">Mostra uma notificação permanente enquanto o banco de dados está desbloqueado.</string>
<string name="IconVisibilityInfo_Android8_text">O android 8 introduziu um novo comportamento para as notificações. Se quiser esconder o ícone de notificação do Keepass2Android, faça através das configurações do sistema. Altere a importância da categoria de notificação para Mínimo.</string>
<string name="IconVisibilityInfo_Android8_btnSettings">Abrir as configurações</string>
<string name="PostNotificationsPermissionInfo_text">Keepass2Android pode exibir uma notificação do sistema enquanto seu banco de dados não está bloqueado. Para que isso funcione, conceda permissão.</string>
<string name="DontCare">Eu não ligo</string>
<string name="DocumentAccessRevoked">O arquivo não está mais acessível ao Keepass2Android. Pode ter sido removido ou as permissões de acesso foram revogadas. É necessário reabrir o arquivo, por exemplo usando \"Alterar banco de dados\".</string>
<string name="PreloadDatabaseEnabled_title">Pré-carregar arquivo de banco de dados</string>
@@ -503,9 +505,27 @@
<string name="hint_sftp_host">host (ex: 192.168.0.1)</string>
<string name="hint_sftp_port">porta</string>
<string name="initial_directory">Diretório inicial (opcional):</string>
<string name="connect_timeout">Segundos de tempo limite de conexão (opcional):\"</string>
<string name="enter_sftp_login_title">Insira os dados de login SFTP:</string>
<string name="sftp_auth_mode">Modo de autenticação</string>
<string name="send_public_key">Enviar chave pública...</string>
<string name="select_private_keyfile">Selecione a chave privada...</string>
<string name="hint_sftp_key_name">Novo nome da chave</string>
<string name="hint_sftp_key_content">Novo conteúdo da chave</string>
<string name="private_key_saved">Chave privada salva</string>
<string name="private_key_save_failed">FALHA ao salvar a chave privada: %1$s</string>
<string name="private_key_info">Digite o nome da chave e o conteúdo para salvar</string>
<string name="private_key_delete">Chave privada excluída: %1$s</string>
<string name="private_key_delete_failed">FALHA ao excluir a chave privada: %1$s</string>
<string name="save_key">Salvar chave privada</string>
<string name="delete_key">Excluir chave privada</string>
<string name="private_key_select">Chave privada selecionada</string>
<string name="private_key_create_new">[Adicionar Nova...]</string>
<string name="hint_sftp_key_passphrase">Senha chave (opcional)</string>
<string name="sftp_kex_title">Algoritmo(s) de Troca de Chaves (KEX) (opcional)</string>
<string name="hint_sftp_kex">\"Nomes/especificações separados por vírgula</string>
<string name="sftp_shk_title">Algoritmo(s) de Chave de Host do Servidor (opcional)</string>
<string name="hint_sftp_shk">\"Nomes/especificações separados por vírgula</string>
<string name="enter_ftp_login_title">Insira os dados de login FTP:</string>
<string name="enter_mega_login_title">Digite seus dados da conta MEGA:</string>
<string name="select_storage_type">Selecione o tipo de armazenamento:</string>
@@ -523,7 +543,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (Arquivos KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Se você não quiser dar acesso KP2A ao seu Google Drive completo, você pode selecionar esta opção. Observe que você precisa criar um arquivo de banco de dados primeiro, os arquivos existentes não são visíveis para o aplicativo. Escolha esta opção na tela Criar banco de dados ou, se você já abriu um banco de dados, exportando o banco de dados escolhendo esta opção.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Este tipo de armazenamento só solicitará acesso à pasta pCloud \"Applications/Keepass2Android\". Se você deseja usar um banco de dados existente de sua conta pCloud, certifique-se de que o arquivo está colocado nesta pasta pCloud.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -576,6 +595,7 @@
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Arquivo de Log para Depuração</string>
<string name="DebugLog_title">Usar arquivo de log</string>
<string name="JSchDebug_title">Registro de depuração SFTP</string>
<string name="DebugLog_summary">Escrever a saída da App em arquivo de log local</string>
<string name="DebugLog_send">Enviar log de depuração...</string>
<string name="loading">Carregando…</string>
@@ -1060,7 +1080,8 @@ Lançamento público inicial
</string-array>
<string-array name="sftp_auth_modes">
<item>Senha</item>
<item>Chave pública/privada</item>
<item>Chave privada/pública K2A</item>
<item>Chave privada personalizada</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignorar falhas de validação de certificado</item>
@@ -1078,6 +1099,11 @@ Lançamento público inicial
<string name="autofill_enable_for">Habilitar auto-preenchimento para %1$s</string>
<string name="invalid_link_association">Não foi possível associar o domínio %1$s com a aplicação %2$s</string>
<string name="enable_fingerprint_hint">Keepass2Android detectou hardware de biométrico. Deseja ativar o Desbloqueio Biométrico para esta base de dados?</string>
<string name="post_notifications_dialog_title">Permitir notificações</string>
<string name="post_notifications_dialog_message">Keepass2Android pode mostrar notificações com botões para copiar valores como senhas e TOTPs para a área de transferência ou para abrir o teclado integrado. Isso é útil para transferir valores para outros aplicativos sem mudar para Keepass2Android repetidamente. Deseja ativar essas notificações?</string>
<string name="post_notifications_dialog_allow">Permitir notificações</string>
<string name="post_notifications_dialog_disable">Desabilitar este recurso</string>
<string name="post_notifications_dialog_notnow">Não agora</string>
<string name="understand">Entendido</string>
<string name="dont_show_again">Não mostrar novamente</string>
<string name="masterkey_infotext_head">Você se lembra de sua senha mestra?</string>

View File

@@ -519,7 +519,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (ficheiros KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Se não quiser fornecer o acesso KP2A ao seu Google Drive, pode selecionar esta opção. Note que tem primeiro criar um ficheiro de base de dados, os ficheiros existentes não são visíveis para a aplicação. Escolha esta opção a partir do ecrã Criar base de dados ou, caso você já tenha aberto uma base de dados, exportando a base de dados escolhendo essa opção.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Este tipo de armazenamento só irá solicitar acesso à pasta pCloud \"Aplicações/Keepass2Android\". Se quiser usar a base de dados existente da sua conta pCloud, por favor certifique-se que o ficheiro é colocado nesta pasta pCloud.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -1052,7 +1051,8 @@ Lançamento público inicial
</string-array>
<string-array name="sftp_auth_modes">
<item>Palavra-passe</item>
<item>Chave pública/privada</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignorar falhas de validação de certificado</item>

View File

@@ -476,7 +476,6 @@
<string name="filestoragename_dropboxKP2A">Dropbox (folder KP2A)</string>
<string name="filestoragehelp_dropboxKP2A">Dacă nu doriți să dați KP2A acces la Dropbox-ul complet, puteți selecta această opțiune. Va solicita acces doar la directorul Apps/Keepass2Android. Acest lucru este potrivit în special atunci când se creează o bază de date nouă. Dacă aveţi deja o bază de date, selectaţi această opţiune pentru a crea directorul, apoi plasați fișierul în director (de la PC) și apoi selectați din nou această opțiune pentru deschiderea fișierului.</string>
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_full">Toate fișierele și fișierele partajate</string>
@@ -749,7 +748,8 @@ Versiunea publică iniţială
</string-array>
<string-array name="sftp_auth_modes">
<item>Parolă</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignoră eșecuri de validare a certificatului</item>

View File

@@ -244,7 +244,7 @@
<string name="sort_name">Сортировка по имени</string>
<string name="sort_db">Сортировка по дате создания</string>
<string name="sort_moddate">Сортировка по дате изменения</string>
<string name="sort_default">Оставить порядок по-умолчанию</string>
<string name="sort_default">Оставить порядок по умолчанию</string>
<string name="special">Особые</string>
<string name="special_extended">Специальная расширенная</string>
<string name="at_least_one_from_each_group">По крайней мере, по одному от каждой группы</string>
@@ -301,6 +301,8 @@
<string name="NoDalVerification_summary">Отключает проверку, если домен соответствует приложению</string>
<string name="InlineSuggestions_title">Интеграция с клавиатурой</string>
<string name="InlineSuggestions_summary">Показывать предложения автозаполнения как встроенные опции в клавиатуре (если поддерживается методом ввода).</string>
<string name="LogAutofillView_title">Просмотр журнала автозаполнения</string>
<string name="LogAutofillView_summary">Записывать информацию о режиме автозаполнения в журнал отладки (если отладка включена). Эта информация может быть передана разработчику, если автозаполнение работает не так, как ожидалось.</string>
<string name="requires_android11">Требуется Android 11 или новее</string>
<string name="kp2a_findUrl">Вспомнить пароль</string>
<string name="excludeExpiredEntries">Исключить просроченные записи</string>
@@ -519,7 +521,6 @@
<string name="filestoragename_gdrive">Диск Google</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A файлы)</string>
<string name="filestoragehelp_gdriveKP2A">Если вы не хотите предоставлять KP2A доступ к вашему полному Google Диску, вы можете выбрать эту опцию. Обратите внимание, что сначала необходимо создать файл базы данных, существующие файлы не видны приложению. Либо выберите эту опцию на экране Создать базу данных, либо, если вы уже открыли базу данных, экспортируйте ее, выбрав эту опцию.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Этот тип хранилища запрашивает доступ только к папке pCloud «Приложения/Keepass2Android». Если вы хотите использовать существующую базу данных из вашей учетной записи pCloud, пожалуйста, убедитесь, что файл размещен в этой папке pCloud.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -540,8 +541,8 @@
<string name="help_master_password">База шифруется паролем, вводимым сюда. Выберите сложный пароль для безопасности базы данных! Совет: придумайте одно-два предложения и используйте первые буквы слов в качестве пароля. Включая знаки препинания.</string>
<string name="hint_master_password">Выберите мастер-пароль для защиты базы данных:</string>
<string name="key_file">Файл-ключ</string>
<string name="help_key_file">Ключевой файл - это пароль, хранящийся в файле. Файлы ключей, как правило, сильнее, чем мастер-пароли, потому что ключ может быть намного сложнее; однако также сложнее сохранить их в секрете. Если вы храните свою базу данных в облаке, не храните там и ключевой файл! Это сделало бы его совершенно бесполезным! Важно: не изменяйте содержимое ключевого файла после создания базы данных!</string>
<string name="hint_key_file">Выберите, если вы хотите использовать файл-ключ в дополнение к мастер-паролю:</string>
<string name="help_key_file">Ключевой файл это пароль, хранящийся в форме файла. Файлы-ключи, как правило, обладают большей сложностью, чем мастер-пароли, однако файлы сложнее сохранить в секрете. Если Ваша база данных находится в облаке, не храните в этом же облаке ключевой файл — это бессмысленно и небезопасно! ВНИМАНИЕ: не изменяйте содержимое файла-ключа после создания базы данных!</string>
<string name="hint_key_file">Включите эту опцию, чтобы в дополнение к мастер-паролю требовался файл-ключ:</string>
<string name="use_key_file">Использовать файл-ключ</string>
<string name="error_adding_keyfile">Ошибка при добавлении файла-ключа!</string>
<string name="init_otp">Загрузить вспомогательный файл одноразовых паролей…</string>
@@ -684,6 +685,12 @@
<string name="EntryChannel_desc">Уведомление для упрощенного доступа к выбранной в данный момент записи.</string>
<string name="CloseDbAfterFailedAttempts">Закрыть базу данных после трёх неудачных попыток биометрической разблокировки.</string>
<string name="WarnFingerprintInvalidated">Внимание! Биометрическая аутентификация может быть аннулирована через Android, напр. после добавления нового отпечатка пальца в настройках устройства. Убедитесь, что вы всегда знаете, как разблокировать с помощью мастер-пароля!</string>
<string-array name="ChangeLog_1_09e">
<item>Исправлены ошибки, приводящие к сбоям и незапланированным выходам</item>
<item>Смена реализации SFTP на новую, поддерживающую современные алгоритмы открытых ключей, таких как rsa-sha2-256</item>
<item>Помечать пароли как чувствительные при копировании в буфер обмена (Android 13)</item>
<item>Улучшения автозаполнения</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Добавлена поддержка просмотра, удаления и восстановления резервных копий записей</item>
<item>Реализована поддержка облачного хранилища MEGA</item>
@@ -1041,12 +1048,13 @@
<item>Пароль + секретные OTP (одноразовые пароли) (режим восстановления)</item>
<item>Пароль + Вызов-Ответ</item>
<item>Пароль + секретный Вызов-Ответ (режим восстановления)</item>
<item>Password + Challenge-Response для Keepass XC</item>
<item>Пароль + Ключ файл + Вызов-ответ для Keepass XC</item>
<item>Пароль + Вызов-Ответ для Keepass XC</item>
<item>Пароль + файл-ключ + Вызов-Ответ для Keepass XC</item>
</string-array>
<string-array name="sftp_auth_modes">
<item>Пароль</item>
<item>Приватный/Публичный ключ</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Игнорировать ошибки проверки сертификата</item>

View File

@@ -49,7 +49,7 @@
<string name="export_prefs">Export databázy...</string>
<string name="fingerprint_prefs">Biometrické odomykanie</string>
<string name="import_db_prefs">Import DB do interného priečinka</string>
<string name="import_keyfile_prefs">Import súbor. kľúča do interného priečinka</string>
<string name="import_keyfile_prefs">Importovať súbor s kľúčom do interného priečinka</string>
<string name="export_keyfile_prefs">Exportovať súbor s kľúčom z interného priečinka</string>
<string name="keyboardswitch_prefs">Prepínanie klávesnice</string>
<string name="OnlyAvailableForLocalFiles">Dostupné len pre lokálne súbory.</string>
@@ -152,13 +152,14 @@
<string name="hint_keyfile">súbor s kľúčom</string>
<string name="hint_length">dĺžka</string>
<string name="hint_pass">heslo</string>
<string name="hint_keyfile_path">Cesta k súkromnému kľúču SSH</string>
<string name="hint_login_pass">Heslo</string>
<string name="hint_title">meno</string>
<string name="hint_url">Adresa URL</string>
<string name="hint_override_url">prepísanie URL</string>
<string name="hint_tags">tag1, tag2</string>
<string name="hint_username">meno používateľa</string>
<string name="InvalidPassword">Chybné heslo, alebo súbor keyfile.</string>
<string name="InvalidPassword">Chybné heslo, alebo súbor s kľúčom.</string>
<string name="invalid_algorithm">Neplatný algoritmus.</string>
<string name="invalid_db_sig">Formát Databázy nerozpoznaný.</string>
<string name="keyfile_does_not_exist">Súbor s kľúčom neexistuje.</string>
@@ -301,6 +302,8 @@
<string name="NoDalVerification_summary">Vypne kontrolu, či sa zhoduje doména a balík aplikácie</string>
<string name="InlineSuggestions_title">Integrácia s klávesnicou</string>
<string name="InlineSuggestions_summary">Zobrazuje návrhy automatického dopĺňania ako vložené možnosti na klávesnici (ak to metóda vstupu podporuje)</string>
<string name="LogAutofillView_title">Zobrazenie záznamu pre autom. dopĺňanie</string>
<string name="LogAutofillView_summary">Zápis podrobností o zobrazení autom. dopĺňania do záznamu o ladení (ak je ladenie zapnuté). Tie je možné odoslať vývojárovi, ak autom. dopĺňanie nefunguje podľa očakávania.</string>
<string name="requires_android11">Vyžaduje Android 11 alebo novší</string>
<string name="kp2a_findUrl">Nájsť heslo</string>
<string name="excludeExpiredEntries">Vylúčiť expirované záznamy</string>
@@ -415,6 +418,7 @@
<string name="ShowUnlockedNotification_summary">Zobraziť prichádzajúcu notifikáciu aj pri odomknutej databáze.</string>
<string name="IconVisibilityInfo_Android8_text">Android 8 obsahuje nové postupy pre notifikácie. Ak chcete skryť ikonu pre notifikácie aplikácie Keepass2Android, prosím nakonfigurujte ich cez nastavenia systému. Dôležitosť kategórie notifikácií nastavte na minimum.</string>
<string name="IconVisibilityInfo_Android8_btnSettings">Otvoriť nastavenia</string>
<string name="PostNotificationsPermissionInfo_text">Keepass2Android môže zobrazovať systémovú notifikáciu, pokiaľ nie je vaša databáza uzamknutá. Pre zapnutie tejto funkcie udeľte oprávnenie</string>
<string name="DontCare">Nezaujíma ma to</string>
<string name="DocumentAccessRevoked">Súbor už nie je dostupný pre Keepass2Android. Buď bol odstránený alebo boli zrušené prístupové oprávnenia. Prosím znovu otvorte súbor, napríklad príkazom pre zmenu databázy.</string>
<string name="PreloadDatabaseEnabled_title">Vopred načítať databázu</string>
@@ -499,9 +503,26 @@
<string name="hint_sftp_host">host (napr: 192.168.0.1)</string>
<string name="hint_sftp_port">port</string>
<string name="initial_directory">Počiatočný priečinok (voliteľne):</string>
<string name="connect_timeout">Časový limit pripojenia v sek. (voliteľne): \"</string>
<string name="enter_sftp_login_title">Zadajte SFTP login:</string>
<string name="sftp_auth_mode">Spôsob autentifikácie</string>
<string name="send_public_key">Odoslať verejný kľúč...</string>
<string name="select_private_keyfile">Vyberte súkromný kľúč...</string>
<string name="hint_sftp_key_name">Nový názov kľúča</string>
<string name="hint_sftp_key_content">Nový obsah kľúča</string>
<string name="private_key_saved">Súkromný kľúč bol uložený</string>
<string name="private_key_save_failed">NEPODARILO sa uložiť súkromný kľúč: %1$s</string>
<string name="private_key_info">Zadajte názov kľúča a obsah na uloženie</string>
<string name="private_key_delete">Vymazaný súkromný kľúč: %1$s</string>
<string name="private_key_delete_failed">NEPODARILO sa vymazať súkromný kľúč: %1$s</string>
<string name="save_key">Uložiť privátny kľúč</string>
<string name="delete_key">Vymazať privátny kľúč</string>
<string name="private_key_select">Vybraný súkromný kľúč</string>
<string name="private_key_create_new">[Pridať nový...]</string>
<string name="sftp_kex_title">Algoritmus pre výmenu kľúča (KEX) (voliteľne)</string>
<string name="hint_sftp_kex">\"Mená oddeľované čiarkou/špec.</string>
<string name="sftp_shk_title">Algoritmus pre server. host. kľúč (voliteľne)</string>
<string name="hint_sftp_shk">\"Mená oddeľované čiarkou/špec.</string>
<string name="enter_ftp_login_title">Zadajte prihlas. údaje pre FTP:</string>
<string name="enter_mega_login_title">Zadajte svoje prihlasovacie údaje ku kontu MEGA:</string>
<string name="select_storage_type">Vyberte spôsob uloženia:</string>
@@ -519,7 +540,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (súbory KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Ak nechcete poskytnúť KP2A prístup k celému úložisku Google Drive, môžete vybrať túto možnosť. Pamätajte na to, že najskôr musíte vytvoriť súbor s databázou, existujúce súbory aplikácia neuvidí. Buď vyberte takúto možnosť z obrazovky pre vytvorenie databázy, alebo už otvorenú databázu exportujte vybratím tejto možnosti.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Tento typ uloženia vyžaduje len prístup k priečinku pCloud \"Aplikácie/Keepass2Android\". Ak chcete použiť existujúcu databázu z konta pCloud, prosím uistite sa, že súbor je umiestnený v priečinku pCloud.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -572,6 +592,7 @@
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Súbor so záznamami pre ladenie</string>
<string name="DebugLog_title">Použiť súbor so záznamami</string>
<string name="JSchDebug_title">Záznam pre ladenie SFTP</string>
<string name="DebugLog_summary">Zapísať výstup aplikácie do lokálneho súboru so záznamom</string>
<string name="DebugLog_send">Odoslať záznam pre ladenie...</string>
<string name="loading">Načítava sa…</string>
@@ -690,7 +711,7 @@
<item>Oprava chyby vedúcej k pádom a neočakávaným odhláseniam</item>
<item>Prepnutie na novú implementáciu SFTP, s podporou pre moderné algoritmy verejných kľúčov, ako je rsa-sha2-256</item>
<item>Označiť heslá ako citlivé údaje pri kopírovaní do schránky (Android 13)</item>
<item>Autofill improvements</item>
<item>Vylepšenia automatického dopĺňania</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Pridaná podpora prehliadania, odstraňovania a obnovovania záloh záznamu</item>
@@ -1067,7 +1088,8 @@ Prvé verejné vydanie
</string-array>
<string-array name="sftp_auth_modes">
<item>Heslo</item>
<item>Súkromný/verejný kľúč</item>
<item>K2A Súkromný/verejný kľúč</item>
<item>Používateľský privátny kľúč</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignorovať zlyhania pri overení certifikátu</item>
@@ -1085,6 +1107,11 @@ Prvé verejné vydanie
<string name="autofill_enable_for">Zapnúť autom. dopĺňanie pre %1$s</string>
<string name="invalid_link_association">Nemožno asociovať webové doménu %1$s s aplikáciou %2$s</string>
<string name="enable_fingerprint_hint">Keepass2Android detegoval biometrický hardvér. Chcete zapnúť biometrické odomykanie pre túto databázu?</string>
<string name="post_notifications_dialog_title">Povoliť notifikácie</string>
<string name="post_notifications_dialog_message">Keepass2Android dokáže zobrazovať notifikácie s tlačidlami na kopírovanie hodnôt, ako sú napr. heslá a časové jednorazové heslá do schránky, alebo na zobrazenie vstavanej klávesnice. Je to užitočné pri prenášaní hodnôt do iných aplikácií bez neustáleho prepínania na aplikáciu Keepass2Android. Chcete povoliť takéto notifikácie?</string>
<string name="post_notifications_dialog_allow">Povoliť notifikácie</string>
<string name="post_notifications_dialog_disable">Zablokovať túto funkciu</string>
<string name="post_notifications_dialog_notnow">Teraz nie</string>
<string name="understand">Rozumiem</string>
<string name="dont_show_again">Už nezobrazovať</string>
<string name="masterkey_infotext_head">Pamätáte si svoje primárne heslo?</string>

View File

@@ -152,6 +152,7 @@
<string name="hint_keyfile">datoteka ključa</string>
<string name="hint_length">dolžina</string>
<string name="hint_pass">geslo</string>
<string name="hint_keyfile_path">Pot zasebnega ključa SSH</string>
<string name="hint_login_pass">Geslo</string>
<string name="hint_title">ime</string>
<string name="hint_url">URL</string>
@@ -417,6 +418,7 @@
<string name="ShowUnlockedNotification_summary">Prikaži ikono obvestila, ko je podatkovna zbirka odklenjena.</string>
<string name="IconVisibilityInfo_Android8_text">Android 8 je uvedel novo vedenje za obvestila. Če želite skriti ikono Keepass2Android, preverite in uredite sistemske nastavitve. Nastavite pomembnost kategorije obvestil na najmanj.</string>
<string name="IconVisibilityInfo_Android8_btnSettings">Odpri nastavitve</string>
<string name="PostNotificationsPermissionInfo_text">Keepass2Android lahko prikaže sistemsko obvestilo, ko podatkovna zbirka ni zaklenjena. Da bo to delovalo, prosimo, podelite dovoljenje.</string>
<string name="DontCare">Me ne zanima</string>
<string name="DocumentAccessRevoked">Datoteka ni več dostopna za Keepass2Android. Ali je bila preklicana ali so bila dovoljena umaknjena. Ponovno odprite datoteko, npr. z uporabo funkcije Zamenjaj bazo.</string>
<string name="PreloadDatabaseEnabled_title">Predn. dat. pod. zbirke</string>
@@ -501,9 +503,27 @@
<string name="hint_sftp_host">gostitelj (npr.: 192.168.0.1)</string>
<string name="hint_sftp_port">vrata</string>
<string name="initial_directory">Začetna mapa (izbirno):</string>
<string name="connect_timeout">Časovna omejitev povezave v sekundah (neobvezno):\"</string>
<string name="enter_sftp_login_title">Vnesite podatke za prijavo SFTP:</string>
<string name="sftp_auth_mode">Način overitve</string>
<string name="send_public_key">Pošlji javni ključ ...</string>
<string name="select_private_keyfile">Izberite zasebni ključ ...</string>
<string name="hint_sftp_key_name">Novo ime ključa</string>
<string name="hint_sftp_key_content">Nova vsebina ključa</string>
<string name="private_key_saved">Zasebni ključ shranjen</string>
<string name="private_key_save_failed">Ni uspelo shraniti zasebnega ključa: %1$s</string>
<string name="private_key_info">Vnesite ime ključa in vsebino, ki jo želite shraniti</string>
<string name="private_key_delete">Izbrisan zasebni ključ: %1$s</string>
<string name="private_key_delete_failed">Neuspešno izbrisan zasebni ključ: %1$s</string>
<string name="save_key">Shrani zasebni ključ</string>
<string name="delete_key">Izbriši zasebni ključ</string>
<string name="private_key_select">Izbrani zasebni ključ</string>
<string name="private_key_create_new">[Dodaj novo ...]</string>
<string name="hint_sftp_key_passphrase">Geslo ključa (neobvezno)</string>
<string name="sftp_kex_title">Algoritem(-i) za izmenjavo ključev (KEX) (neobvezno)</string>
<string name="hint_sftp_kex">\"Imena/spec, ločena z vejico</string>
<string name="sftp_shk_title">Algoritem(-i) ključa gostitelja strežnika (neobvezno)</string>
<string name="hint_sftp_shk">\"Imena/spec, ločena z vejico</string>
<string name="enter_ftp_login_title">Vnesite podatke FTP za prijavo:</string>
<string name="enter_mega_login_title">Vnesite podatke za prijavo v račun MEGA:</string>
<string name="select_storage_type">Izberite mesto za shranjevanje:</string>
@@ -521,7 +541,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A datoteke)</string>
<string name="filestoragehelp_gdriveKP2A">Če KP2A ne želite omogočiti dostopa do celotnega Google Drive, lahko izberete to možnost. Upoštevajte, da morate najprej ustvariti novo datoteko baze podatkov, obstoječe datoteke niso vidne aplikaciji. To možnost izberite na zaslonu Ustvari bazo podatkov ali, če ste že odprli bazo podatkov, izvozite bazo podatkov in izberete to možnost.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Ta vrsta shranjevanja bo zahtevala dostop samo do pCloud mape »Aplikacije/ Keepass2Android«. Če želite uporabiti obstoječo bazo podatkov iz vašega PCloud računa, se prepričajte, da je datoteka v pCloud mapi.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -574,6 +593,7 @@
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Dnevnik datoteke za odpravljanje napak</string>
<string name="DebugLog_title">Uporabi datoteko dnevnika</string>
<string name="JSchDebug_title">Dnevnik za odpravljanje napak SFTP</string>
<string name="DebugLog_summary">Izpišite dogodke aplikacije v lokalno datoteko dnevnika</string>
<string name="DebugLog_send">Pošlji dnevnik napak ...</string>
<string name="loading">Nalaganje …</string>
@@ -1060,7 +1080,8 @@ Začetna javna izdaja
</string-array>
<string-array name="sftp_auth_modes">
<item>Geslo</item>
<item>Zasebni/Javni ključ</item>
<item>K2A Zasebni/javni ključ</item>
<item>Zasebni ključ po meri</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Prezri spodletela preverjanja veljavnosti potrdil</item>
@@ -1078,6 +1099,11 @@ Začetna javna izdaja
<string name="autofill_enable_for">Omogoči samodejno izpolnjevanje za %1$s</string>
<string name="invalid_link_association">Spletne domene %1$s ni bilo mogoče povezati z aplikacijo %2$s</string>
<string name="enable_fingerprint_hint">Keepass2Android je zaznal biometrično strojno opremo. Ali želite omogočiti Biometrično odklepanje za to bazo?</string>
<string name="post_notifications_dialog_title">Dovoli obvestila</string>
<string name="post_notifications_dialog_message">Keepass2Android lahko prikaže obvestila z gumbi za kopiranje vrednosti, kot so gesla in TOTP, v odložišče ali za priklic vgrajene tipkovnice. To je uporabno za prenos vrednosti v druge aplikacije, ne da bi morali večkrat preklopiti na Keepass2Android. Želite omogočiti takšna obvestila?</string>
<string name="post_notifications_dialog_allow">Dovoli obvestila</string>
<string name="post_notifications_dialog_disable">Onemogočite to funkcijo</string>
<string name="post_notifications_dialog_notnow">Ne zdaj</string>
<string name="understand">Razumem</string>
<string name="dont_show_again">Ne prikaži več</string>
<string name="masterkey_infotext_head">Ali se spomnite vašega glavnega gesla?</string>

View File

@@ -306,6 +306,7 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>Лозинка</item>
<item>Приватни/Јавни кључ</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
</resources>

View File

@@ -519,7 +519,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A-filer)</string>
<string name="filestoragehelp_gdriveKP2A">Om du inte vill ge KP2A åtkomst till hela din Google Drive kan du välja det här alternativet. Observera att du måste skapa en databasfil först, befintliga filer är inte synliga för appen. Välj antingen det här alternativet från skärmen Skapa databas eller, om du redan har öppnat en databas, genom att exportera databasen genom att välja det här alternativet.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Denna lagringstyp kommer endast att begära åtkomst till pCloud-mappen \"Applications/Keepass2Android\". Om du vill använda en befintlig databas från ditt pCloud-konto, se till att filen är placerad i den här pCloud-mappen.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -878,7 +877,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>Lösenord</item>
<item>Privat/offentlig nyckel</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignorera certifikatvalideringsfel</item>

View File

@@ -519,7 +519,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A dosyaları)</string>
<string name="filestoragehelp_gdriveKP2A">KP2A\'ya tam Google Drive\'ınıza erişim izni vermek istemiyorsanız, bu seçeneği belirleyebilirsiniz. Önce bir veritabanı dosyası oluşturmanız gerektiğini unutmayın, mevcut dosyalar uygulama tarafından görülmez. Ya veritabanı oluştur ekranından bu seçeneği seçin ya da zaten bir veritabanı açtıysanız, bu seçeneği seçerek veritabanını dışa aktarın.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Bu depolama türü yalnızca pCloud \"Applications/Keepass2Android\" klasörüne erişim talep eder. pCloud hesabınızdaki mevcut bir veritabanını kullanmak istiyorsanız, lütfen dosyanın bu pCloud klasörüne yerleştirildiğinden emin olun.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -1062,7 +1061,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>Parola</item>
<item>Özel/Ortak anahtar</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Sertifika doğrulama hatalarını yoksay</item>

View File

@@ -521,7 +521,6 @@
<string name="filestoragename_gdrive">Диск Google</string>
<string name="filestoragename_gdriveKP2A">Google Диск (файли KP2A)</string>
<string name="filestoragehelp_gdriveKP2A">Якщо не хочете надавати доступ KP2A до всього свого Google Диска, ви можете вибрати цю опцію. Зверніть увагу, що спочатку потрібно створити файл бази даних, наявні файли не показуються для застосунку. Або виберіть цю опцію з екрана створення бази даних або, якщо ви вже відкрили базу даних, експортуйте її, вибравши цю опцію.</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">Цей тип сховища вимагатиме доступу лише до теки \"Applications/Keepass2Android\" у pCloud. Якщо ви хочете використовувати наявну базу даних з вашого облікового запису pCloud, переконайтеся, що файл розміщений у цій теці pCloud.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -1069,7 +1068,8 @@ Initial public release
</string-array>
<string-array name="sftp_auth_modes">
<item>Пароль</item>
<item>Приватний/Публічний ключ</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ігнорувати помилки перевірки сертифіката</item>

View File

@@ -680,7 +680,8 @@ Bản phát hành chính thức đầu tiên</string>
</string-array>
<string-array name="sftp_auth_modes">
<item>Mật mã</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Bỏ qua các thất bại khi xác nhận chứng chỉ</item>

View File

@@ -521,7 +521,6 @@
<string name="filestoragename_gdrive">Google 雲端硬碟</string>
<string name="filestoragename_gdriveKP2A">Google 雲端硬碟 (KP2A 檔案)</string>
<string name="filestoragehelp_gdriveKP2A">如不想讓 KP2A 存取整個 Google 雲端硬碟,請選此項。請注意此選項會使程式無法存取現有檔案,需要先建立資料庫檔案。請在建立資料庫或匯出已開啟的資料庫時,選擇此選項。</string>
<string name="filestoragename_pcloud">pCloud</string>
<string name="filestoragehelp_pcloud">這個儲存類型只會請求存取 pCloud「Applications/Keepass2Android」資料夾。要使用你 pCloud 賬戶裏現有的資料庫,請確保其檔案置於該 pCloud 資料夾。</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -1067,7 +1066,8 @@
</string-array>
<string-array name="sftp_auth_modes">
<item>密碼</item>
<item>私鑰 / 公鑰</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>忽略憑證驗證失敗</item>

View File

@@ -10,7 +10,7 @@
<string name="deny">拒绝</string>
<string name="add_entry">添加条目</string>
<string name="edit_entry">编辑条目</string>
<string name="add_url_entry">添加URL</string>
<string name="add_url_entry">URL 创建条目</string>
<string name="add_group">添加群组</string>
<string name="add_group_title">添加群组</string>
<string name="edit_group_title">编辑群组</string>
@@ -152,6 +152,7 @@
<string name="hint_keyfile">密钥文件</string>
<string name="hint_length">长度</string>
<string name="hint_pass">密码</string>
<string name="hint_keyfile_path">SSH 私钥路径</string>
<string name="hint_login_pass">密码</string>
<string name="hint_title">名称</string>
<string name="hint_url">网址:</string>
@@ -417,6 +418,7 @@
<string name="ShowUnlockedNotification_summary">当数据库处于解锁状态栏持续显示通知。</string>
<string name="IconVisibilityInfo_Android8_text">Android 8.0的通知引入了新的行为。若你想隐藏Keepass2Android的通知图标请前往系统设置将通知的重要性设为中等。</string>
<string name="IconVisibilityInfo_Android8_btnSettings">打开设置</string>
<string name="PostNotificationsPermissionInfo_text">Keepass2Android 可以在您的数据库未被锁定时显示系统通知。要正常显示此通知,请授予权限。</string>
<string name="DontCare">无所谓</string>
<string name="DocumentAccessRevoked">Keepass2Android无法访问此文件。文件可能已被删除或其访问权限受限。请使用“更改数据库”重新打开文件。</string>
<string name="PreloadDatabaseEnabled_title">预载入数据库文件</string>
@@ -501,9 +503,27 @@
<string name="hint_sftp_host">主机(比如: 192.168.0.1</string>
<string name="hint_sftp_port">端口</string>
<string name="initial_directory">初始目录(可选):</string>
<string name="connect_timeout">连接超时秒 (可选)\"</string>
<string name="enter_sftp_login_title">输入 SFTP 登录数据:</string>
<string name="sftp_auth_mode">身份验证模式</string>
<string name="send_public_key">发送公钥...</string>
<string name="select_private_keyfile">选择私钥...</string>
<string name="hint_sftp_key_name">新密钥名称</string>
<string name="hint_sftp_key_content">新密钥内容</string>
<string name="private_key_saved">私钥已保存</string>
<string name="private_key_save_failed">未能保存私钥:%1$s</string>
<string name="private_key_info">输入要保存的密钥名称和内容</string>
<string name="private_key_delete">删除私钥:%1$s</string>
<string name="private_key_delete_failed">删除私钥失败:%1$s</string>
<string name="save_key">保存私钥</string>
<string name="delete_key">删除私钥</string>
<string name="private_key_select">选定私钥</string>
<string name="private_key_create_new">[新建私钥...]</string>
<string name="hint_sftp_key_passphrase">密钥的密码短语 (可选)</string>
<string name="sftp_kex_title">密钥交换KEX算法 (可选)</string>
<string name="hint_sftp_kex">\" 英文逗号分隔的命名/规范</string>
<string name="sftp_shk_title">服务器主机密钥算法 (可选)</string>
<string name="hint_sftp_shk">\" 英文逗号分隔的命名/规范</string>
<string name="enter_ftp_login_title">输入 FTP 登录数据:</string>
<string name="enter_mega_login_title">输入您的 MEGA 帐户登录数据:</string>
<string name="select_storage_type">选择存储类型:</string>
@@ -521,7 +541,6 @@
<string name="filestoragename_gdrive">Google Drive</string>
<string name="filestoragename_gdriveKP2A">Google Drive (KP2A文件)</string>
<string name="filestoragehelp_gdriveKP2A">如果您不想给予KP2A对您Google Drive的完整访问权您可以选择此选项。 请注意,您需要先创建一个数据库文件,现有文件对本应用不可见。 您可以从“创建数据库”屏幕中选择此选项,或者选择此选项导出数据库,前提是您已经打开了一个数据库。</string>
<string name="filestoragename_pcloud">PCloud</string>
<string name="filestoragehelp_pcloud">这种存储类型只请求访问 pCloud 文件夹 \"Applications/Keepass2Android\"。如果您想使用 pCloud 帐户中现有的数据库,请确保文件被放置在这个 pCloud 文件夹中。</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
@@ -574,6 +593,7 @@
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">调试用日志文件</string>
<string name="DebugLog_title">使用日志文件</string>
<string name="JSchDebug_title">SFTP 调试记录</string>
<string name="DebugLog_summary">将应用输出写入本地日志文件</string>
<string name="DebugLog_send">发送调试日志...</string>
<string name="loading">载入中...</string>
@@ -1062,7 +1082,8 @@ Initial public release
</string-array>
<string-array name="sftp_auth_modes">
<item>密码</item>
<item>私钥/公钥</item>
<item>K2A 私钥 / 公钥</item>
<item>自定义私钥</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>忽略证书验证失败</item>
@@ -1080,6 +1101,11 @@ Initial public release
<string name="autofill_enable_for">对 %1$s 启用自动填充</string>
<string name="invalid_link_association">无法关联 Web 域 %1$s 与应用程序 %2$s</string>
<string name="enable_fingerprint_hint">Keepass2Android检测到指纹传感器。你想为此数据库启用指纹解锁吗</string>
<string name="post_notifications_dialog_title">允许通知</string>
<string name="post_notifications_dialog_message">Keepass2Android 可以显示带按钮的通知,允许快速复制像密码和 TOTP 这样的值到剪贴板,或者带起内置键盘。 这允许你在不反复切换到 Keepass2Android 的情况下便可将值转移到其他应用。你想要启用这种通知吗?</string>
<string name="post_notifications_dialog_allow">允许通知</string>
<string name="post_notifications_dialog_disable">禁用此功能</string>
<string name="post_notifications_dialog_notnow">暂不</string>
<string name="understand">我明白了</string>
<string name="dont_show_again">不再显示</string>
<string name="masterkey_infotext_head">你还记得你的主密码吗?</string>

View File

@@ -98,6 +98,7 @@
<string name="TrayTotp_SeedField_key">TrayTotp_SeedField_key</string>
<string name="TrayTotp_prefs_key">TrayTotp_prefs_key</string>
<string name="DebugLog_key">DebugLog_key</string>
<string name="FtpDebug_key">FtpDebug_key</string>
<string name="DebugLog_prefs_key">DebugLog_prefs_key</string>
<string name="DebugLog_send_key">DebugLog_send</string>
<string name="AutofillDisabledQueriesPreference_key">AutofillDisabledQueriesPreference_key</string>
@@ -215,4 +216,4 @@
<string name="ClearPasswordOnLeave_key">ClearPasswordOnLeave</string>
</resources>
</resources>

View File

@@ -167,6 +167,7 @@
<string name="hint_keyfile">key file</string>
<string name="hint_length">length</string>
<string name="hint_pass">password</string>
<string name="hint_keyfile_path">SSH private key path</string>
<string name="hint_login_pass">Password</string>
<string name="hint_title">name</string>
<string name="hint_url">URL</string>
@@ -483,6 +484,10 @@
<string name="IconVisibilityInfo_Android8_text">Android 8 has introduced new behavior for notifications. If you want to hide the icon for Keepass2Android\'s notifications, please configure this through the system settings. Set the importance of the notification category to Minimum.</string>
<string name="IconVisibilityInfo_Android8_btnSettings">Open settings</string>
<string name="PostNotificationsPermissionInfo_text">Keepass2Android can display a system notification while your database is not locked. For this to work, please grant permission.</string>
<string name="DontCare">I don\'t care</string>
<string name="DocumentAccessRevoked">The file is no longer accessible to Keepass2Android. Either it was removed or the access permissions have been revoked. Please use re-open the file, e.g. using Change database.</string>
@@ -589,9 +594,27 @@
<string name="hint_sftp_host">host (ex: 192.168.0.1)</string>
<string name="hint_sftp_port">port</string>
<string name="initial_directory">Initial directory (optional):</string>
<string name="connect_timeout">Connection timeout seconds (optional):"</string>
<string name="enter_sftp_login_title">Enter SFTP login data:</string>
<string name="sftp_auth_mode">Authentication mode</string>
<string name="send_public_key">Send public key...</string>
<string name="select_private_keyfile">Select private key...</string>
<string name="hint_sftp_key_name">New key name</string>
<string name="hint_sftp_key_content">New key content</string>
<string name="private_key_saved">Private key saved</string>
<string name="private_key_save_failed">FAILED to save private key: %1$s</string>
<string name="private_key_info">Enter key name and content to save</string>
<string name="private_key_delete">Deleted private key: %1$s</string>
<string name="private_key_delete_failed">FAILED to deleted private key: %1$s</string>
<string name="save_key">Save Private Key</string>
<string name="delete_key">Delete Private Key</string>
<string name="private_key_select">Selected private key</string>
<string name="private_key_create_new">[Add New...]</string>
<string name="hint_sftp_key_passphrase">Key passphrase (optional)</string>
<string name="sftp_kex_title">Key Exchange (KEX) Algorithm(s) (optional)</string>
<string name="hint_sftp_kex">"Comma-separated names/spec</string>
<string name="sftp_shk_title">Server Host Key Algorithm(s) (optional)</string>
<string name="hint_sftp_shk">"Comma-separated names/spec</string>
<string name="enter_ftp_login_title">Enter FTP login data:</string>
@@ -681,6 +704,7 @@
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Log-File for Debugging</string>
<string name="DebugLog_title">Use log file</string>
<string name="FtpDebug_title">FTP/SFTP debug logging</string>
<string name="DebugLog_summary">Write app output to a local log file</string>
<string name="DebugLog_send">Send debug log...</string>
@@ -1326,7 +1350,8 @@ Initial public release
</string-array>
<string-array name="sftp_auth_modes">
<item>Password</item>
<item>Private/Public key</item>
<item>K2A Private/Public key</item>
<item>Custom Private key</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>Ignore certificate validation failures</item>
@@ -1349,6 +1374,12 @@ Initial public release
<string name="enable_fingerprint_hint">Keepass2Android has detected biometric hardware. Do you want to enable Biometric Unlock for this database?</string>
<string name="post_notifications_dialog_title">Allow notifications</string>
<string name="post_notifications_dialog_message">Keepass2Android can show notifications with buttons to copy values like passwords and TOTPs to clipboard, or to bring up the built-in keyboard. This is useful to transfer values into other apps without switching to Keepass2Android repeatedly. Do you want to enable such notifications?</string>
<string name="post_notifications_dialog_allow">Allow notifications</string>
<string name="post_notifications_dialog_disable">Disable this feature</string>
<string name="post_notifications_dialog_notnow">Not now</string>
<string name="understand">I understand</string>
<string name="dont_show_again">Do not show again</string>

View File

@@ -670,10 +670,17 @@
android:defaultValue="false"
android:title="@string/DebugLog_title"
android:key="@string/DebugLog_key" />
<Preference
android:enabled="true"
android:title="@string/DebugLog_send"
android:key="@string/DebugLog_send_key" />
<CheckBoxPreference
android:enabled="true"
android:persistent="true"
android:defaultValue="false"
android:title="@string/FtpDebug_title"
android:key="@string/FtpDebug_key" />
</PreferenceScreen>
</PreferenceScreen>

View File

@@ -111,7 +111,6 @@ namespace keepass2android
public class Kp2aApp: IKp2aApp, ICacheSupervisor
{
public void Lock(bool allowQuickUnlock = true, bool lockWasTriggeredByTimeout = false)
{
@@ -315,7 +314,7 @@ namespace keepass2android
}
else
{
//Anrdoid 8 requires that we call StartForeground() shortly after starting the service with StartForegroundService.
//Android 8 requires that we call StartForeground() shortly after starting the service with StartForegroundService.
//This is not possible when we're closing the service. In this case we don't use the StopSelf in the OngoingNotificationsService.OnStartCommand() anymore but directly stop the service.
OngoingNotificationsService.CancelNotifications(ctx); //The docs are not 100% clear if OnDestroy() will be called immediately. So make sure the notifications are up to date.
@@ -467,8 +466,11 @@ namespace keepass2android
}
}
private void AskForReload(Activity activity, Action<bool> actionOnResult)
private readonly HashSet<RealProgressDialog> _activeProgressDialogs = new HashSet<RealProgressDialog>();
// Whether the app is currently showing a dialog that requires user input, like a yesNoCancel dialog
private bool _isShowingUserInputDialog = false;
private void AskForReload(Activity activity, Action<bool> actionOnResult)
{
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.SetTitle(activity.GetString(Resource.String.AskReloadFile_title));
@@ -489,6 +491,8 @@ namespace keepass2android
actionOnResult(true);
actionOnResult = null;
}
OnUserInputDialogClose();
});
builder.SetNegativeButton(activity.GetString(Android.Resource.String.No), (dlgSender, dlgEvt) =>
@@ -499,8 +503,9 @@ namespace keepass2android
actionOnResult(false);
actionOnResult = null;
}
});
OnUserInputDialogClose();
});
Dialog dialog = builder.Create();
@@ -516,8 +521,11 @@ namespace keepass2android
actionOnResult(false);
actionOnResult = null;
}
OnUserInputDialogClose();
}));
OnUserInputDialogShow();
dialog.Show();
}
@@ -581,35 +589,59 @@ namespace keepass2android
EventHandler dismissHandler,
Context ctx, string messageSuffix = "")
{
Handler handler = new Handler(Looper.MainLooper);
Handler handler = new Handler(Looper.MainLooper);
handler.Post(() =>
{
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.SetTitle(GetResourceString(titleKey));
builder.SetMessage(GetResourceString(messageKey)+(messageSuffix != "" ? " " + messageSuffix: ""));
builder.SetMessage(GetResourceString(messageKey) + (messageSuffix != "" ? " " + messageSuffix : ""));
string yesText = GetResourceString(yesString);
builder.SetPositiveButton(yesText, yesHandler);
// _____handlerWithShow are wrappers around given handlers to update _isSHowingYesNoCancelDialog
// and to show progress dialog after yesNoCancel dialog is closed
EventHandler<DialogClickEventArgs> yesHandlerWithShow = (sender, args) =>
{
OnUserInputDialogClose();
yesHandler.Invoke(sender, args);
};
string yesText = GetResourceString(yesString);
builder.SetPositiveButton(yesText, yesHandlerWithShow);
string noText = "";
if (noHandler != null)
{
EventHandler<DialogClickEventArgs> noHandlerWithShow = (sender, args) =>
{
OnUserInputDialogClose();
noHandler.Invoke(sender, args);
};
noText = GetResourceString(noString);
builder.SetNegativeButton(noText, noHandler);
builder.SetNegativeButton(noText, noHandlerWithShow);
}
string cancelText = "";
if (cancelHandler != null)
{
EventHandler<DialogClickEventArgs> cancelHandlerWithShow = (sender, args) =>
{
OnUserInputDialogClose();
cancelHandler.Invoke(sender, args);
};
cancelText = ctx.GetString(Android.Resource.String.Cancel);
builder.SetNeutralButton(cancelText,
cancelHandler);
cancelHandlerWithShow);
}
AlertDialog dialog = builder.Create();
if (dismissHandler != null)
dialog.SetOnDismissListener(new Util.DismissListener(() => dismissHandler(dialog, EventArgs.Empty)));
{
dialog.SetOnDismissListener(new Util.DismissListener(() => {
OnUserInputDialogClose();
dismissHandler(dialog, EventArgs.Empty);
}));
}
OnUserInputDialogShow();
dialog.Show();
if (yesText.Length + noText.Length + cancelText.Length >= 20)
@@ -626,12 +658,53 @@ namespace keepass2android
}
}
}
);
}
}
public Handler UiThreadHandler
/// <summary>
/// Shows all non-dismissed progress dialogs.
/// If there are multiple progressDialogs active, they all will be showing.
/// There probably will never be multiple dialogs at the same time because only one ProgressTask can run at a time.
/// Even if multiple dialogs show at the same time, it shouldn't be too much of an issue
/// because they are just progress indicators.
/// </summary>
private void ShowAllActiveProgressDialogs()
{
foreach (RealProgressDialog progressDialog in _activeProgressDialogs)
{
progressDialog.Show();
}
}
private void HideAllActiveProgressDialogs()
{
foreach (RealProgressDialog progressDialog in _activeProgressDialogs)
{
progressDialog.Hide();
}
}
/// <summary>
/// Hide progress dialogs whenever a dialog that requires user interaction
/// appears so that the progress dialogs cannot cover the user-interaction dialog
/// </summary>
private void OnUserInputDialogShow()
{
_isShowingUserInputDialog = true;
HideAllActiveProgressDialogs();
}
/// <summary>
/// Show previously hidden progress dialogs after user interaction with dialog finished
/// </summary>
private void OnUserInputDialogClose()
{
_isShowingUserInputDialog = false;
ShowAllActiveProgressDialogs();
}
public Handler UiThreadHandler
{
get { return new Handler(); }
}
@@ -642,9 +715,11 @@ namespace keepass2android
private class RealProgressDialog : IProgressDialog
{
private readonly ProgressDialog _pd;
private readonly Kp2aApp _app;
public RealProgressDialog(Context ctx)
public RealProgressDialog(Context ctx, Kp2aApp app)
{
_app = app;
_pd = new ProgressDialog(ctx);
_pd.SetCancelable(false);
}
@@ -669,18 +744,28 @@ namespace keepass2android
{
Kp2aLog.LogUnexpectedError(e);
}
_app._activeProgressDialogs.Remove(this);
}
public void Show()
{
_pd.Show();
_app._activeProgressDialogs.Add(this);
// Only show if asking dialog not also showing
if (!_app._isShowingUserInputDialog)
{
_pd.Show();
}
}
public void Hide()
{
_pd.Hide();
}
}
public IProgressDialog CreateProgressDialog(Context ctx)
{
return new RealProgressDialog(ctx);
return new RealProgressDialog(ctx, this);
}
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
@@ -747,8 +832,8 @@ namespace keepass2android
new OneDrive2FullFileStorage(),
new OneDrive2MyFilesFileStorage(),
new OneDrive2AppFolderFileStorage(),
new SftpFileStorage(LocaleManager.LocalizedAppContext, this),
new NetFtpFileStorage(LocaleManager.LocalizedAppContext, this),
new SftpFileStorage(LocaleManager.LocalizedAppContext, this, IsFtpDebugEnabled()),
new NetFtpFileStorage(LocaleManager.LocalizedAppContext, this, IsFtpDebugEnabled),
new WebDavFileStorage(this),
new PCloudFileStorage(LocaleManager.LocalizedAppContext, this),
new PCloudFileStorageAll(LocaleManager.LocalizedAppContext, this),
@@ -764,7 +849,13 @@ namespace keepass2android
}
}
public void TriggerReload(Context ctx, Action<bool> actionOnResult)
private static bool IsFtpDebugEnabled()
{
return PreferenceManager.GetDefaultSharedPreferences(LocaleManager.LocalizedAppContext)
.GetBoolean(LocaleManager.LocalizedAppContext.GetString(Resource.String.FtpDebug_key), false);
}
public void TriggerReload(Context ctx, Action<bool> actionOnResult)
{
Handler handler = new Handler(Looper.MainLooper);
handler.Post(() =>

View File

@@ -178,6 +178,12 @@ namespace keepass2android
FindPreference(GetString(Resource.String.DebugLog_key)).PreferenceChange += OnDebugLogChanged;
FindPreference(GetString(Resource.String.DebugLog_send_key)).PreferenceClick += OnSendDebug;
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
FindPreference(GetString(Resource.String.FtpDebug_key)).PreferenceChange += OnJSchDebugChanged;
#else
FindPreference(GetString(Resource.String.FtpDebug_key)).Enabled = false;
#endif
HashSet<string> supportedLocales = new HashSet<string>() { "en", "af", "ar", "az", "be", "bg", "ca", "cs", "da", "de", "el", "es", "eu", "fa", "fi", "fr", "gl", "he", "hr", "hu", "id", "in", "it", "iw", "ja", "ko", "ml", "nb", "nl", "nn", "no", "pl", "pt", "ro", "ru", "si", "sk", "sl", "sr", "sv", "tr", "uk", "vi", "zh" };
var languagePref = (ListPreference)FindPreference(GetString(Resource.String.app_language_pref_key));
new AppLanguageManager(this, languagePref, supportedLocales);
@@ -381,18 +387,41 @@ namespace keepass2android
private void OnDebugLogChanged(object sender, Preference.PreferenceChangeEventArgs e)
{
if ((bool)e.NewValue)
{
Kp2aLog.CreateLogFile();
}
if ((bool)e.NewValue)
Kp2aLog.CreateLogFile();
else
{
Kp2aLog.FinishLogFile();
}
Kp2aLog.FinishLogFile();
}
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
SetJSchLogging(PreferenceManager.GetDefaultSharedPreferences(Application.Context)
.GetBoolean(Application.Context.GetString(Resource.String.FtpDebug_key), false));
#endif
}
private void AlgorithmPrefChange(object sender, Preference.PreferenceChangeEventArgs preferenceChangeEventArgs)
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
private void OnJSchDebugChanged(object sender, Preference.PreferenceChangeEventArgs e)
{
bool debugEnabled = (bool)e.NewValue;
SetJSchLogging(debugEnabled);
string prefKey = Application.Context.GetString(Resource.String.FtpDebug_key);
PreferenceManager.SharedPreferences.Edit().PutBoolean(prefKey, debugEnabled).Apply();
}
private void SetJSchLogging(bool enabled)
{
var sftpStorage = new Keepass2android.Javafilestorage.SftpStorage(Context);
string? logFilename = null;
if (Kp2aLog.LogToFile)
{
logFilename = Kp2aLog.LogFilename;
}
sftpStorage.SetJschLogging(enabled, logFilename);
}
#endif
private void AlgorithmPrefChange(object sender, Preference.PreferenceChangeEventArgs preferenceChangeEventArgs)
{
var db = App.Kp2a.CurrentDb;
var previousCipher = db.KpDatabase.DataCipherUuid;
@@ -835,7 +864,7 @@ namespace keepass2android
#if DEBUG
preference.Enabled = (usageCount > 1);
#else
#else
preference.Enabled = (usageCount > 50);
#endif
preference.PreferenceChange += delegate(object sender, Preference.PreferenceChangeEventArgs args)