Compare commits

..

480 Commits

Author SHA1 Message Date
Philipp Crocoll
e7b4cfe53e Manifest for 1.09a-r3 2021-08-22 17:40:57 +02:00
Philipp Crocoll
5aba209857 Merge branch 'master' of c:/ph/keepass2android 2021-07-03 15:50:39 +02:00
Philipp Crocoll
80cfb9e098 fix to superfluous dropbox login requests 2021-07-03 15:50:25 +02:00
Philipp Crocoll
b98c887d71 Merge branch 'l10n_master2' of https://github.com/PhilippC/keepass2android 2021-07-03 14:52:10 +02:00
PhilippC
be8e8f9b02 New translations strings.xml (Dutch) 2021-07-03 14:49:44 +02:00
PhilippC
87cbf65654 New translations strings.xml (Dutch) 2021-07-03 14:45:24 +02:00
Philipp Crocoll
6a0e132945 Merge branch 'l10n_master2' of https://github.com/PhilippC/keepass2android 2021-07-03 14:41:21 +02:00
PhilippC
438cf5793a New translations strings.xml (Dutch) 2021-07-03 14:37:44 +02:00
PhilippC
74a736ef87 New translations strings.xml (Spanish) 2021-07-03 14:37:15 +02:00
Philipp Crocoll
4ce451b99c Merge branch 'master' of c:/ph/keepass2android 2021-07-03 13:28:02 +02:00
Philipp Crocoll
f017d3fdf2 remove intermediate file 2021-07-03 13:27:54 +02:00
Philipp Crocoll
54eb656726 1.09a-r1 release manifest 2021-07-03 13:27:38 +02:00
Philipp Crocoll
9d63578110 fix potential crash with invalid OTP strings 2021-07-03 13:27:10 +02:00
Philipp Crocoll
06baeeb79a Merge branch 'l10n_master2' of https://github.com/PhilippC/keepass2android 2021-07-03 13:25:20 +02:00
PhilippC
a77fe463d8 New translations strings.xml (Ukrainian) 2021-07-02 17:09:44 +02:00
PhilippC
c016fc164d New translations strings.xml (Polish) 2021-07-02 17:09:43 +02:00
PhilippC
a409bb1318 New translations strings.xml (Polish) 2021-07-02 16:09:14 +02:00
PhilippC
7eeaf205cd New translations strings.xml (Chinese Simplified) 2021-06-29 05:47:30 +02:00
PhilippC
85882b2547 New translations strings.xml (Chinese Simplified) 2021-06-29 04:47:27 +02:00
PhilippC
12e4af7b0d New translations strings.xml (Portuguese, Brazilian) 2021-06-28 19:57:40 +02:00
PhilippC
774406fe00 New translations strings.xml (Portuguese, Brazilian) 2021-06-28 19:00:23 +02:00
PhilippC
e7c8534c01 New translations strings.xml (Slovenian) 2021-06-28 13:04:59 +02:00
PhilippC
0655dd41bb New translations strings.xml (German) 2021-06-28 12:06:31 +02:00
PhilippC
48bb71d5dc New translations strings.xml (Ukrainian) 2021-06-28 11:07:43 +02:00
PhilippC
b2641fe4d3 New translations strings.xml (Chinese Simplified) 2021-06-28 11:07:42 +02:00
PhilippC
e58de5a99f New translations strings.xml (Portuguese, Brazilian) 2021-06-28 11:07:38 +02:00
PhilippC
68df9ada01 New translations strings.xml (Slovenian) 2021-06-28 11:07:34 +02:00
PhilippC
a91a711499 New translations strings.xml (Slovak) 2021-06-28 11:07:23 +02:00
PhilippC
780ab1125d New translations strings.xml (Polish) 2021-06-28 11:07:22 +02:00
PhilippC
2e83495542 New translations strings.xml (Portuguese) 2021-06-28 11:07:18 +02:00
PhilippC
b69b7a8b5f New translations strings.xml (French) 2021-06-28 11:07:13 +02:00
PhilippC
00f264c313 New translations strings.xml (Russian) 2021-06-28 11:07:11 +02:00
PhilippC
ba9eb65fa6 New translations strings.xml (Czech) 2021-06-28 11:07:09 +02:00
PhilippC
824a7484e0 New translations strings.xml (Danish) 2021-06-28 11:07:07 +02:00
PhilippC
7ebab48b97 New translations strings.xml (German) 2021-06-28 11:07:06 +02:00
PhilippC
708bd9bc8a New translations strings.xml (Greek) 2021-06-28 11:07:05 +02:00
PhilippC
960b00b9ca New translations strings.xml (Portuguese) 2021-06-28 11:06:33 +02:00
PhilippC
d654137caa New translations strings.xml (Belarusian) 2021-06-28 11:06:15 +02:00
Philipp Crocoll
e4b36c172b extended changelog 1.09a 2021-06-28 11:02:46 +02:00
Philipp Crocoll
05f8a067f1 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-06-28 10:56:19 +02:00
Philipp Crocoll
d3db23d1b7 update to Dropbox Core SDK v4.0.0, add support for short-lived access tokens + refresh token (as the previous method will be removed) 2021-06-28 10:56:04 +02:00
PhilippC
30288a3106 New translations strings.xml (Belarusian) 2021-06-25 21:36:19 +02:00
PhilippC
89410e60c7 New translations strings.xml (Belarusian) 2021-06-25 20:36:35 +02:00
PhilippC
3e971b5fe9 New translations strings.xml (Belarusian) 2021-06-25 17:39:25 +02:00
PhilippC
7673e4d638 New translations strings.xml (Portuguese) 2021-06-25 16:32:44 +02:00
PhilippC
9a76ea1433 New translations strings.xml (Portuguese) 2021-06-25 15:26:11 +02:00
PhilippC
92ff60c66d New translations strings.xml (Belarusian) 2021-06-25 15:26:10 +02:00
PhilippC
dfad45f83d New translations strings.xml (Belarusian) 2021-06-25 14:28:35 +02:00
PhilippC
6c0a11d300 New translations strings.xml (Belarusian) 2021-06-25 13:16:55 +02:00
PhilippC
54f58067d3 New translations strings.xml (Azerbaijani) 2021-06-25 12:14:28 +02:00
PhilippC
edb23bef1e New translations strings.xml (Azerbaijani) 2021-06-25 12:14:27 +02:00
PhilippC
cddd0705d8 New translations strings.xml (Portuguese) 2021-06-25 12:14:26 +02:00
PhilippC
fc4447748b New translations strings.xml (Belarusian) 2021-06-25 12:14:25 +02:00
PhilippC
433310030f New translations strings.xml (Azerbaijani) 2021-06-25 11:19:05 +02:00
PhilippC
388fb2ba34 New translations strings.xml (Azerbaijani) 2021-06-25 11:19:04 +02:00
PhilippC
d2a19967bc New translations strings.xml (Azerbaijani) 2021-06-25 11:19:03 +02:00
PhilippC
b2538c0133 New translations strings.xml (Portuguese) 2021-06-25 11:19:01 +02:00
PhilippC
dde2fdb103 New translations strings.xml (Azerbaijani) 2021-06-25 10:22:31 +02:00
PhilippC
7a83064745 New translations strings.xml (Swedish) 2021-06-25 08:33:18 +02:00
PhilippC
970ebfede5 New translations strings.xml (Belarusian) 2021-06-25 08:33:17 +02:00
PhilippC
4ecc56fa79 New translations strings.xml (Azerbaijani) 2021-06-25 07:30:19 +02:00
PhilippC
1e25bc6c7c New translations strings.xml (Azerbaijani) 2021-06-25 07:30:18 +02:00
PhilippC
8eca4a1d19 New translations strings.xml (Azerbaijani) 2021-06-25 07:30:17 +02:00
PhilippC
c6f7a0ba1a New translations strings.xml (Belarusian) 2021-06-25 07:30:16 +02:00
PhilippC
fe07cf6060 New translations strings.xml (Belarusian) 2021-06-25 06:31:31 +02:00
PhilippC
022c26c89d New translations strings.xml (Portuguese) 2021-06-24 22:12:06 +02:00
PhilippC
a13bcaf223 New translations strings.xml (Portuguese) 2021-06-24 21:13:33 +02:00
PhilippC
8b469ed263 New translations strings.xml (Portuguese) 2021-06-24 21:13:32 +02:00
PhilippC
ff631935fc New translations strings.xml (Belarusian) 2021-06-24 08:39:37 +02:00
PhilippC
6fd363e071 New translations strings.xml (Belarusian) 2021-06-24 07:34:00 +02:00
PhilippC
57556764e9 New translations strings.xml (Belarusian) 2021-06-24 06:33:51 +02:00
PhilippC
fbef5de705 New translations strings.xml (Belarusian) 2021-06-23 17:06:40 +02:00
PhilippC
bff035f8a5 New translations strings.xml (Belarusian) 2021-06-23 16:05:18 +02:00
PhilippC
272a124d3a New translations strings.xml (Belarusian) 2021-06-23 07:49:26 +02:00
PhilippC
ae5fd9765d New translations strings.xml (Portuguese) 2021-06-22 19:09:30 +02:00
PhilippC
2900b293c3 New translations strings.xml (Portuguese) 2021-06-22 18:02:48 +02:00
PhilippC
b8fbca0d49 New translations strings.xml (Bulgarian) 2021-06-19 16:14:03 +02:00
PhilippC
370f5a5b4e New translations strings.xml (Bulgarian) 2021-06-19 09:42:54 +02:00
PhilippC
9558786b76 New translations strings.xml (Bulgarian) 2021-06-19 09:42:53 +02:00
PhilippC
c0260974bd New translations strings.xml (Bulgarian) 2021-06-19 08:31:57 +02:00
PhilippC
f864af6003 New translations strings.xml (Bulgarian) 2021-06-19 08:31:56 +02:00
PhilippC
30fc5e4646 New translations strings.xml (Bulgarian) 2021-06-19 07:33:12 +02:00
PhilippC
3dee4935c8 New translations strings.xml (Belarusian) 2021-06-18 15:56:37 +02:00
PhilippC
0e088a48b8 New translations strings.xml (Belarusian) 2021-06-18 14:53:27 +02:00
PhilippC
0928ab0224 New translations strings.xml (Belarusian) 2021-06-18 09:53:48 +02:00
PhilippC
ec07a61cab New translations strings.xml (Belarusian) 2021-06-18 08:55:55 +02:00
PhilippC
14d7eaf361 New translations strings.xml (Belarusian) 2021-06-18 07:48:54 +02:00
PhilippC
0f2892bd28 New translations strings.xml (Belarusian) 2021-06-17 16:56:44 +02:00
PhilippC
51ae5a2a0c New translations strings.xml (Belarusian) 2021-06-17 15:55:16 +02:00
PhilippC
5bb5fc9496 New translations strings.xml (Belarusian) 2021-06-17 09:30:17 +02:00
PhilippC
9e550f85cc New translations strings.xml (Belarusian) 2021-06-17 08:16:41 +02:00
PhilippC
4ef46e302d New translations strings.xml (Belarusian) 2021-06-17 07:10:51 +02:00
PhilippC
079806c857 New translations strings.xml (Belarusian) 2021-06-16 15:57:43 +02:00
PhilippC
deed92af0d New translations strings.xml (Belarusian) 2021-06-16 14:53:47 +02:00
PhilippC
48367c5926 New translations strings.xml (Belarusian) 2021-06-16 13:37:40 +02:00
PhilippC
0f3cdf77d4 New translations strings.xml (Belarusian) 2021-06-16 12:38:04 +02:00
PhilippC
4b06833c3f New translations strings.xml (Belarusian) 2021-06-16 10:10:39 +02:00
PhilippC
8a6bfe3288 New translations strings.xml (Belarusian) 2021-06-16 08:57:17 +02:00
PhilippC
2d106f7b87 New translations strings.xml (Belarusian) 2021-06-16 08:57:16 +02:00
PhilippC
130ef249d7 New translations strings.xml (Belarusian) 2021-06-16 08:57:15 +02:00
PhilippC
5b55cf0edc New translations strings.xml (Belarusian) 2021-06-16 07:45:40 +02:00
PhilippC
1d824049cb New translations strings.xml (Belarusian) 2021-06-16 06:37:46 +02:00
PhilippC
2eef3cfa00 New translations strings.xml (Belarusian) 2021-06-14 15:50:17 +02:00
PhilippC
f82cfeed3d New translations strings.xml (Belarusian) 2021-06-14 15:50:14 +02:00
PhilippC
3aba034155 New translations strings.xml (Belarusian) 2021-06-14 14:48:40 +02:00
PhilippC
114a0832cb New translations strings.xml (Belarusian) 2021-06-14 14:48:39 +02:00
PhilippC
66f627fd1a New translations strings.xml (Belarusian) 2021-06-14 13:48:35 +02:00
PhilippC
1ce915cd9e New translations strings.xml (Belarusian) 2021-06-14 12:39:16 +02:00
Philipp Crocoll
712f476da8 call SetInvalidatedByBiometricEnrollment for security reasons, but show a warning to users (again) that they should not forget their master key 2021-06-14 11:43:33 +02:00
PhilippC
2d0e5d208b New translations strings.xml (Belarusian) 2021-06-14 09:55:24 +02:00
PhilippC
053cef944a New translations strings.xml (Belarusian) 2021-06-11 08:40:22 +02:00
PhilippC
ff2ba3f4ed New translations strings.xml (Belarusian) 2021-06-11 07:35:26 +02:00
PhilippC
ec7d908941 New translations strings.xml (Belarusian) 2021-06-11 06:36:43 +02:00
PhilippC
1afbd4c632 New translations strings.xml (Belarusian) 2021-06-10 07:52:15 +02:00
PhilippC
a5af23b2f7 New translations strings.xml (Belarusian) 2021-06-10 07:52:15 +02:00
PhilippC
78ac8f45e9 New translations strings.xml (Belarusian) 2021-06-10 06:55:42 +02:00
PhilippC
c9ade3ad3f New translations strings.xml (Belarusian) 2021-06-09 11:12:10 +02:00
PhilippC
c588d3d581 New translations strings.xml (Belarusian) 2021-06-09 10:04:57 +02:00
PhilippC
0140b4f74e New translations strings.xml (Belarusian) 2021-06-09 09:09:44 +02:00
PhilippC
eb42d33630 New translations strings.xml (Belarusian) 2021-06-09 09:09:43 +02:00
PhilippC
f6b0eab8b1 New translations strings.xml (Belarusian) 2021-06-09 08:04:33 +02:00
PhilippC
b316c21623 New translations strings.xml (Belarusian) 2021-06-09 08:04:32 +02:00
PhilippC
bd661fddb1 New translations strings.xml (Belarusian) 2021-06-09 07:01:16 +02:00
PhilippC
fd5ca57f36 New translations strings.xml (Portuguese, Brazilian) 2021-06-06 13:32:27 +02:00
PhilippC
d38d446a70 New translations strings.xml (Ukrainian) 2021-06-06 00:02:01 +02:00
PhilippC
4f97e6e8b9 New translations strings.xml (Ukrainian) 2021-06-05 23:02:21 +02:00
PhilippC
e79450a54d New translations strings.xml (Belarusian) 2021-06-04 13:05:49 +02:00
PhilippC
44fd071c9d New translations strings.xml (Belarusian) 2021-06-04 13:05:48 +02:00
PhilippC
8487b935c4 New translations strings.xml (Belarusian) 2021-06-04 12:02:23 +02:00
PhilippC
f497194281 New translations strings.xml (Belarusian) 2021-06-04 09:00:43 +02:00
PhilippC
614e014cab New translations strings.xml (Belarusian) 2021-06-04 08:05:06 +02:00
PhilippC
5bf976747c New translations strings.xml (Belarusian) 2021-06-04 07:08:18 +02:00
PhilippC
5c2a7f27e6 New translations strings.xml (Belarusian) 2021-06-01 09:10:28 +02:00
PhilippC
4c059b625c New translations strings.xml (Belarusian) 2021-06-01 08:05:22 +02:00
PhilippC
1860a92fa0 New translations strings.xml (Belarusian) 2021-06-01 08:05:21 +02:00
PhilippC
f4799acf31 New translations strings.xml (Belarusian) 2021-06-01 06:58:46 +02:00
PhilippC
a704e523d9 New translations strings.xml (Belarusian) 2021-05-31 19:42:56 +02:00
PhilippC
725cdf3ec3 New translations strings.xml (Belarusian) 2021-05-31 18:34:07 +02:00
PhilippC
f3a3956d79 New translations strings.xml (Belarusian) 2021-05-31 10:59:49 +02:00
PhilippC
167cd39c8f New translations strings.xml (Belarusian) 2021-05-31 10:00:19 +02:00
PhilippC
681492f5f8 New translations strings.xml (Belarusian) 2021-05-31 10:00:18 +02:00
PhilippC
80766034a6 New translations strings.xml (Belarusian) 2021-05-31 09:00:58 +02:00
PhilippC
4e7b96462c New translations strings.xml (Belarusian) 2021-05-31 07:59:43 +02:00
PhilippC
761cae4a2b New translations strings.xml (Slovak) 2021-05-28 15:02:13 +02:00
PhilippC
d0d526d36d New translations strings.xml (Slovak) 2021-05-28 15:02:12 +02:00
PhilippC
f29cfd55fa New translations strings.xml (Slovak) 2021-05-28 14:05:32 +02:00
PhilippC
7a4116c016 New translations strings.xml (Belarusian) 2021-05-28 10:08:24 +02:00
PhilippC
5639517128 New translations strings.xml (Belarusian) 2021-05-28 09:10:25 +02:00
PhilippC
17c7b9c526 New translations strings.xml (Belarusian) 2021-05-28 09:10:23 +02:00
PhilippC
e40c5aa821 New translations strings.xml (Belarusian) 2021-05-28 09:10:23 +02:00
PhilippC
1915a8b1db New translations strings.xml (Belarusian) 2021-05-28 08:11:46 +02:00
PhilippC
c105312b99 New translations strings.xml (Belarusian) 2021-05-28 08:11:45 +02:00
PhilippC
2b5d4365c2 New translations strings.xml (Belarusian) 2021-05-28 06:54:24 +02:00
PhilippC
f145c68cfe New translations strings.xml (Belarusian) 2021-05-28 06:54:23 +02:00
PhilippC
2fff53f468 New translations strings.xml (Belarusian) 2021-05-27 17:07:32 +02:00
PhilippC
6a1e75bf51 New translations strings.xml (Belarusian) 2021-05-27 17:07:31 +02:00
PhilippC
4d903239ca New translations strings.xml (Belarusian) 2021-05-27 17:07:30 +02:00
PhilippC
570133d365 New translations strings.xml (Belarusian) 2021-05-27 16:09:15 +02:00
PhilippC
9cc49389e4 New translations strings.xml (Belarusian) 2021-05-27 16:09:14 +02:00
PhilippC
cfcc472c4c New translations strings.xml (Belarusian) 2021-05-27 09:36:50 +02:00
PhilippC
79ff2c1adf New translations strings.xml (Belarusian) 2021-05-27 09:36:49 +02:00
PhilippC
01e5aad370 New translations strings.xml (Russian) 2021-05-27 09:36:48 +02:00
PhilippC
50ba6c2750 New translations strings.xml (Czech) 2021-05-27 01:59:51 +02:00
PhilippC
49a4c8e771 New translations strings.xml (Czech) 2021-05-27 00:58:59 +02:00
PhilippC
97de25e2ed New translations strings.xml (Czech) 2021-05-27 00:00:45 +02:00
PhilippC
7a6d9392d3 New translations strings.xml (Russian) 2021-05-26 16:02:19 +02:00
PhilippC
4738db9b7d New translations strings.xml (Belarusian) 2021-05-26 15:06:28 +02:00
PhilippC
172e75e4e8 New translations strings.xml (Russian) 2021-05-26 15:06:27 +02:00
PhilippC
c4795547c8 New translations strings.xml (Belarusian) 2021-05-26 14:08:05 +02:00
PhilippC
fefc6ca04b New translations strings.xml (Danish) 2021-05-26 00:15:45 +02:00
PhilippC
b00945a815 New translations strings.xml (Danish) 2021-05-25 23:14:40 +02:00
PhilippC
3ad7112011 New translations strings.xml (Greek) 2021-05-25 10:58:25 +02:00
PhilippC
9b49df08e3 New translations strings.xml (Greek) 2021-05-25 09:41:14 +02:00
PhilippC
06ba923c28 New translations strings.xml (Danish) 2021-05-24 14:44:22 +02:00
PhilippC
67c5e58c2c New translations strings.xml (Belarusian) 2021-05-24 13:33:32 +02:00
PhilippC
e8c5e0d911 New translations strings.xml (Belarusian) 2021-05-24 13:33:31 +02:00
PhilippC
668836e646 New translations strings.xml (Belarusian) 2021-05-24 13:33:30 +02:00
Philipp Crocoll
e1cd8123cf improve pCloud warning to close #796 2021-05-24 10:37:56 +02:00
PhilippC
6cfeb715e5 New translations strings.xml (Spanish) 2021-05-23 23:03:12 +02:00
PhilippC
19419dc483 New translations strings.xml (Danish) 2021-05-23 18:59:35 +02:00
PhilippC
1aac650a3d New translations strings.xml (Danish) 2021-05-23 18:59:34 +02:00
PhilippC
7030c9e29b New translations strings.xml (Danish) 2021-05-23 18:01:51 +02:00
PhilippC
730fbe211c New translations strings.xml (Danish) 2021-05-23 10:24:09 +02:00
PhilippC
0993ea5e46 New translations strings.xml (Polish) 2021-05-21 17:15:30 +02:00
PhilippC
0cc4fc76fd New translations strings.xml (Polish) 2021-05-21 16:17:01 +02:00
PhilippC
3a6f536cd9 New translations strings.xml (Chinese Simplified) 2021-05-21 05:40:06 +02:00
PhilippC
4809037fdc New translations strings.xml (Portuguese, Brazilian) 2021-05-19 22:54:47 +02:00
PhilippC
3d9c084e59 New translations strings.xml (Portuguese, Brazilian) 2021-05-19 22:54:46 +02:00
PhilippC
75f64c31a9 New translations strings.xml (Portuguese, Brazilian) 2021-05-19 22:54:45 +02:00
PhilippC
4d64c06f79 New translations strings.xml (Portuguese, Brazilian) 2021-05-19 21:58:10 +02:00
PhilippC
f6a4f46b12 New translations strings.xml (French) 2021-05-19 16:45:14 +02:00
PhilippC
df68fd70f7 New translations strings.xml (French) 2021-05-19 15:48:20 +02:00
PhilippC
2631982e62 New translations strings.xml (Polish) 2021-05-19 14:53:11 +02:00
PhilippC
bcbc89f3a3 New translations strings.xml (Slovenian) 2021-05-18 21:53:27 +02:00
PhilippC
d5f5d9a6b1 Merge pull request #1676 from PhilippC/l10n_master2
New Crowdin updates
2021-05-18 20:55:44 +02:00
PhilippC
01028a956a New translations strings.xml (German) 2021-05-18 20:53:38 +02:00
PhilippC
eb55d15a2f New translations strings.xml (Chinese Simplified) 2021-05-18 20:53:36 +02:00
PhilippC
8219ea74b2 New translations strings.xml (Slovenian) 2021-05-18 20:53:18 +02:00
PhilippC
18b1cb6805 New translations strings.xml (Chinese Simplified) 2021-05-18 20:45:26 +02:00
PhilippC
b0f9dbb062 New translations strings.xml (German) 2021-05-18 20:45:14 +02:00
PhilippC
94118d31f1 New translations strings.xml (Slovenian) 2021-05-18 20:44:49 +02:00
Philipp Crocoll
44c52ee47c Merge branch 'master' of c:/ph/keepass2android 2021-05-18 20:43:49 +02:00
Philipp Crocoll
b71b9c523c add password changes to changelog 2021-05-18 20:43:39 +02:00
PhilippC
06b19dc05f New translations strings.xml (Basque) 2021-05-18 20:32:35 +02:00
PhilippC
6a05c0348b New translations strings.xml (Finnish) 2021-05-18 20:32:33 +02:00
PhilippC
c04184d715 New translations strings.xml (Hebrew) 2021-05-18 20:32:31 +02:00
PhilippC
c3bfdfcc29 New translations strings.xml (Hungarian) 2021-05-18 20:32:29 +02:00
PhilippC
b6a83339e8 New translations strings.xml (Italian) 2021-05-18 20:32:27 +02:00
PhilippC
5a50b1b91f New translations strings.xml (Japanese) 2021-05-18 20:32:26 +02:00
PhilippC
31f3b70fdb New translations strings.xml (Korean) 2021-05-18 20:32:24 +02:00
PhilippC
c4673f87d7 New translations strings.xml (Dutch) 2021-05-18 20:32:22 +02:00
PhilippC
3b5822fd8f New translations strings.xml (Polish) 2021-05-18 20:32:20 +02:00
PhilippC
f0d40713ae New translations strings.xml (Greek) 2021-05-18 20:32:18 +02:00
PhilippC
a1a249421c New translations strings.xml (Romanian) 2021-05-18 20:32:16 +02:00
PhilippC
fe927f8111 New translations strings.xml (French) 2021-05-18 20:32:14 +02:00
PhilippC
b639a0bc9f New translations strings.xml (Arabic) 2021-05-18 20:32:11 +02:00
PhilippC
1ce9afb8be New translations strings.xml (Catalan) 2021-05-18 20:32:08 +02:00
PhilippC
a277511be9 New translations strings.xml (Czech) 2021-05-18 20:32:06 +02:00
PhilippC
25e9b51d04 New translations strings.xml (Danish) 2021-05-18 20:32:04 +02:00
PhilippC
b20076e7b8 New translations strings.xml (German) 2021-05-18 20:32:02 +02:00
PhilippC
0c4d096e36 New translations strings.xml (Bulgarian) 2021-05-18 20:32:01 +02:00
PhilippC
6b3ab59f13 New translations strings.xml (Chinese Simplified) 2021-05-18 20:32:00 +02:00
PhilippC
98e937a61b New translations strings.xml (Portuguese) 2021-05-18 20:31:59 +02:00
PhilippC
b10893dfc6 New translations strings.xml (Portuguese, Brazilian) 2021-05-18 20:31:58 +02:00
PhilippC
ec6d5bb56e New translations strings.xml (Indonesian) 2021-05-18 20:31:56 +02:00
PhilippC
7dbaf6feaa New translations strings.xml (Persian) 2021-05-18 20:31:54 +02:00
PhilippC
6cffa74196 New translations strings.xml (Croatian) 2021-05-18 20:31:52 +02:00
PhilippC
7906c477c4 New translations strings.xml (Norwegian Nynorsk) 2021-05-18 20:31:50 +02:00
PhilippC
5cc139e7f0 New translations strings.xml (Azerbaijani) 2021-05-18 20:31:48 +02:00
PhilippC
a3b2ccfbb9 New translations strings.xml (Malayalam) 2021-05-18 20:31:47 +02:00
PhilippC
f0cde44dc1 New translations strings.xml (Norwegian Bokmal) 2021-05-18 20:31:43 +02:00
PhilippC
c7cf38de48 New translations strings.xml (Russian) 2021-05-18 20:31:40 +02:00
PhilippC
dad72764e8 New translations strings.xml (Slovak) 2021-05-18 20:31:38 +02:00
PhilippC
10ef165112 New translations strings.xml (Slovenian) 2021-05-18 20:31:36 +02:00
PhilippC
47fcc8007d New translations strings.xml (Serbian (Cyrillic)) 2021-05-18 20:31:34 +02:00
PhilippC
bb9a53e6cc New translations strings.xml (Galician) 2021-05-18 20:31:32 +02:00
PhilippC
46544570ad New translations strings.xml (Swedish) 2021-05-18 20:31:31 +02:00
PhilippC
db71a06371 New translations strings.xml (Turkish) 2021-05-18 20:31:30 +02:00
PhilippC
f5b88df4c1 New translations strings.xml (Ukrainian) 2021-05-18 20:31:27 +02:00
PhilippC
a4dfc59126 New translations strings.xml (Chinese Traditional) 2021-05-18 20:31:26 +02:00
PhilippC
c980d37aa2 New translations strings.xml (Vietnamese) 2021-05-18 20:31:24 +02:00
PhilippC
411740cf01 New translations strings.xml (Spanish) 2021-05-18 20:31:22 +02:00
Philipp Crocoll
9d4ca67722 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-05-18 20:30:39 +02:00
PhilippC
ad5b7e5b46 New translations strings.xml (Spanish) 2021-05-18 20:27:26 +02:00
PhilippC
785c0bba8d New translations strings.xml (German) 2021-05-18 20:27:14 +02:00
Philipp Crocoll
f6395908ee add back custom ColorTranslator, fixes PlatformnotSupportedException, closes https://github.com/PhilippC/keepass2android/issues/1679 2021-05-18 20:26:08 +02:00
Philipp Crocoll
4636f5800c add passphrase, password generator profiles, password strength estimation, closes https://github.com/PhilippC/keepass2android/issues/81 2021-05-18 08:42:47 +02:00
Philipp Crocoll
212418f11a include some more options in the password generator (extended special group, at least one from every group, exclude look-alike), closes some items of https://github.com/PhilippC/keepass2android/issues/81 2021-05-17 11:56:16 +02:00
PhilippC
7f443e8505 New translations strings.xml (Slovenian) 2021-05-16 21:08:23 +02:00
PhilippC
488d11b693 New translations strings.xml (Chinese Simplified) 2021-05-16 07:10:30 +02:00
Philipp Crocoll
f0255703c1 1.09a-r0 2021-05-15 16:24:32 +02:00
PhilippC
f7e33728f2 Merge pull request #1579 from PhilippC/l10n_master2
New Crowdin updates
2021-05-15 15:46:47 +02:00
PhilippC
86fcfb49eb New translations strings.xml (German) 2021-05-15 15:42:27 +02:00
Philipp Crocoll
17ae8f7e99 Merge branch 'master' of c:/ph/keepass2android 2021-05-15 15:26:19 +02:00
Philipp Crocoll
638a2ec462 extend changelog for 1.09a 2021-05-15 15:26:06 +02:00
PhilippC
789e474c04 New translations strings.xml (Chinese Simplified) 2021-05-15 06:41:58 +02:00
PhilippC
65ade96fd8 New translations strings.xml (Slovenian) 2021-05-14 18:12:39 +02:00
PhilippC
05e2502831 New translations strings.xml (Basque) 2021-05-14 08:37:34 +02:00
PhilippC
f1a4536a42 New translations strings.xml (Finnish) 2021-05-14 08:37:32 +02:00
PhilippC
0bb372c2e9 New translations strings.xml (Hebrew) 2021-05-14 08:37:29 +02:00
PhilippC
98c38e6f8f New translations strings.xml (Hungarian) 2021-05-14 08:37:26 +02:00
PhilippC
7739a1bb85 New translations strings.xml (Italian) 2021-05-14 08:37:22 +02:00
PhilippC
5dbc4df7c1 New translations strings.xml (Japanese) 2021-05-14 08:37:20 +02:00
PhilippC
3bee5fc4c9 New translations strings.xml (Korean) 2021-05-14 08:37:17 +02:00
PhilippC
62fe696921 New translations strings.xml (Dutch) 2021-05-14 08:37:14 +02:00
PhilippC
603f30cd9d New translations strings.xml (Polish) 2021-05-14 08:37:11 +02:00
PhilippC
764d208695 New translations strings.xml (Greek) 2021-05-14 08:37:07 +02:00
PhilippC
147c83f62b New translations strings.xml (Romanian) 2021-05-14 08:37:03 +02:00
PhilippC
da89fd674f New translations strings.xml (French) 2021-05-14 08:37:00 +02:00
PhilippC
6bc4677f3a New translations strings.xml (Arabic) 2021-05-14 08:36:55 +02:00
PhilippC
0676cd6e7c New translations strings.xml (Catalan) 2021-05-14 08:36:50 +02:00
PhilippC
7dceb7e363 New translations strings.xml (Czech) 2021-05-14 08:36:47 +02:00
PhilippC
6a2be81241 New translations strings.xml (Danish) 2021-05-14 08:36:44 +02:00
PhilippC
b26c9bfa35 New translations strings.xml (German) 2021-05-14 08:36:41 +02:00
PhilippC
19ecbfae85 New translations strings.xml (Bulgarian) 2021-05-14 08:36:40 +02:00
PhilippC
221277bf24 New translations strings.xml (Chinese Simplified) 2021-05-14 08:36:39 +02:00
PhilippC
41d864e69f New translations strings.xml (Portuguese) 2021-05-14 08:36:37 +02:00
PhilippC
46341b5d37 New translations strings.xml (Portuguese, Brazilian) 2021-05-14 08:36:35 +02:00
PhilippC
b3569f1459 New translations strings.xml (Indonesian) 2021-05-14 08:36:32 +02:00
PhilippC
963df05ed3 New translations strings.xml (Persian) 2021-05-14 08:36:29 +02:00
PhilippC
f693d93e03 New translations strings.xml (Croatian) 2021-05-14 08:36:27 +02:00
PhilippC
b3a78194dd New translations strings.xml (Norwegian Bokmal) 2021-05-14 08:36:12 +02:00
PhilippC
2d27c95388 New translations strings.xml (Russian) 2021-05-14 08:36:07 +02:00
PhilippC
3e760df174 New translations strings.xml (Slovak) 2021-05-14 08:36:04 +02:00
PhilippC
9dca986efe New translations strings.xml (Slovenian) 2021-05-14 08:36:01 +02:00
PhilippC
6967bd2b84 New translations strings.xml (Serbian (Cyrillic)) 2021-05-14 08:35:58 +02:00
PhilippC
b32e599223 New translations strings.xml (Galician) 2021-05-14 08:35:55 +02:00
PhilippC
727863e4ee New translations strings.xml (Swedish) 2021-05-14 08:35:54 +02:00
PhilippC
2c157bc76f New translations strings.xml (Turkish) 2021-05-14 08:35:51 +02:00
PhilippC
f78f2b9361 New translations strings.xml (Ukrainian) 2021-05-14 08:35:49 +02:00
PhilippC
3859ce78a5 New translations strings.xml (Chinese Traditional) 2021-05-14 08:35:46 +02:00
PhilippC
1e1c79e8f8 New translations strings.xml (Vietnamese) 2021-05-14 08:35:43 +02:00
PhilippC
beadd426c4 New translations strings.xml (Spanish) 2021-05-14 08:35:40 +02:00
Philipp Crocoll
edc650a66e Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-05-14 08:01:47 +02:00
Philipp Crocoll
562a80132a merge changes from KeePass 2.48 with support for KDBX 4.1 and slightly improved merging behavior with respect to custom icons, closes https://github.com/PhilippC/keepass2android/issues/1670 2021-05-14 08:01:34 +02:00
Philipp Crocoll
f5ee9e3955 fix bad strings 2021-05-13 16:21:12 +02:00
Philipp Crocoll
a0a8a2810c add github sponsor as supporter 2021-05-13 16:21:02 +02:00
Philipp Crocoll
1d6dadea58 implement editor for TOTP settings in EntryEditActivity, closes https://github.com/PhilippC/keepass2android/issues/770 2021-05-13 16:20:17 +02:00
PhilippC
5144fe6fc9 New translations strings.xml (Chinese Simplified) 2021-05-11 02:46:09 +02:00
PhilippC
7e0913fe8b New translations strings.xml (Chinese Simplified) 2021-05-10 20:13:58 +02:00
PhilippC
15f1aa68da New translations strings.xml (Chinese Simplified) 2021-05-10 19:08:32 +02:00
PhilippC
1eb11928c2 New translations strings.xml (Chinese Simplified) 2021-05-10 19:08:31 +02:00
PhilippC
982527b4bf New translations strings.xml (Chinese Simplified) 2021-05-10 19:08:30 +02:00
PhilippC
2a2622ddd8 New translations strings.xml (Chinese Simplified) 2021-05-10 18:09:00 +02:00
PhilippC
39939790c2 New translations strings.xml (Chinese Simplified) 2021-05-10 17:12:52 +02:00
PhilippC
ddabf82ea7 New translations strings.xml (Chinese Simplified) 2021-05-10 16:07:28 +02:00
PhilippC
7dd6745cd9 New translations strings.xml (Spanish) 2021-05-08 21:47:06 +02:00
PhilippC
3ac02da522 New translations strings.xml (Spanish) 2021-05-08 20:47:32 +02:00
PhilippC
d5982ae1e3 New translations strings.xml (Arabic) 2021-04-25 13:38:03 +02:00
PhilippC
fe2ebfcfd9 New translations strings.xml (Arabic) 2021-04-25 12:38:11 +02:00
PhilippC
977fe33d6a New translations strings.xml (Arabic) 2021-04-25 12:38:09 +02:00
PhilippC
951e11f6b0 New translations strings.xml (Swedish) 2021-04-23 00:12:19 +02:00
PhilippC
2fa1cd175d New translations strings.xml (Swedish) 2021-04-22 23:06:55 +02:00
PhilippC
5c9ab11aff New translations strings.xml (Vietnamese) 2021-04-13 11:39:41 +02:00
PhilippC
1f9327db80 Merge pull request #1612 from TiloHeidasch/master
Fixes Typo
2021-04-10 16:27:28 +02:00
PhilippC
e16429f77b New translations strings.xml (Portuguese) 2021-04-05 22:25:20 +02:00
PhilippC
3e58ef5dba New translations strings.xml (Italian) 2021-03-29 12:05:05 +02:00
Philipp Crocoll
84ac5fef4f remove option "Binary directory" (no longer used) 2021-03-29 10:11:02 +02:00
PhilippC
303d98bad7 New translations strings.xml (Norwegian Bokmal) 2021-03-28 19:53:28 +02:00
PhilippC
e39a126229 New translations strings.xml (Italian) 2021-03-27 12:23:29 +01:00
PhilippC
7257657432 New translations strings.xml (Portuguese, Brazilian) 2021-03-26 13:33:06 +01:00
PhilippC
99999dde2c New translations strings.xml (Portuguese, Brazilian) 2021-03-26 13:33:04 +01:00
PhilippC
60fc41d524 New translations strings.xml (Portuguese, Brazilian) 2021-03-26 12:12:23 +01:00
PhilippC
559a6f87b2 New translations strings.xml (Chinese Simplified) 2021-03-23 16:48:57 +01:00
PhilippC
02fbe3ce93 New translations strings.xml (German) 2021-03-17 09:54:00 +01:00
PhilippC
aa221c221e New translations strings.xml (Russian) 2021-03-17 00:00:37 +01:00
PhilippC
796efc490e New translations strings.xml (German) 2021-03-17 00:00:36 +01:00
PhilippC
b3c6b9b8e3 New translations strings.xml (German) 2021-03-16 22:47:57 +01:00
PhilippC
bc44d8a0a6 New translations strings.xml (Greek) 2021-03-16 16:22:39 +01:00
PhilippC
a43c291794 New translations strings.xml (Greek) 2021-03-16 16:22:38 +01:00
TiloHeidasch
2a4c0a8427 Fixes Typo 2021-03-16 11:45:39 +01:00
PhilippC
bca9156a38 New translations strings.xml (Chinese Simplified) 2021-03-14 13:26:34 +01:00
PhilippC
cd97ac8106 New translations strings.xml (Persian) 2021-03-12 11:24:36 +01:00
PhilippC
e464413b05 New translations strings.xml (Portuguese) 2021-03-10 20:42:31 +01:00
PhilippC
39c50eda0c New translations strings.xml (Portuguese) 2021-03-10 20:42:30 +01:00
PhilippC
4901485dc3 New translations strings.xml (Portuguese) 2021-03-10 20:42:28 +01:00
PhilippC
0762c227c1 New translations strings.xml (Portuguese) 2021-03-10 19:36:01 +01:00
Philipp Crocoll
09537689cf additional logging for https://github.com/PhilippC/keepass2android/issues/1257 2021-03-08 09:50:09 +01:00
Philipp Crocoll
92ffe596ce additional logging for keyboard issue 2021-03-08 09:49:48 +01:00
Philipp Crocoll
7037fbfe94 fix URL has two "Open URL" context menu entries 2021-03-08 09:49:21 +01:00
Philipp Crocoll
9eab59dcbe fix broken steam support 2021-03-08 09:48:56 +01:00
PhilippC
d705371f87 New translations strings.xml (Chinese Traditional) 2021-03-02 13:35:20 +01:00
PhilippC
09206a387b New translations strings.xml (Hungarian) 2021-02-24 20:05:26 +01:00
PhilippC
65fc67a4b2 New translations strings.xml (Hungarian) 2021-02-24 20:05:24 +01:00
PhilippC
7faa6a8efc New translations strings.xml (Ukrainian) 2021-02-24 18:54:22 +01:00
PhilippC
4f4b7fdfa4 New translations strings.xml (German) 2021-02-23 15:42:08 +01:00
PhilippC
ce09d9c8a9 New translations strings.xml (Polish) 2021-02-23 13:02:58 +01:00
PhilippC
c9744aeb94 New translations strings.xml (Polish) 2021-02-23 13:02:57 +01:00
PhilippC
a65dbfafa3 New translations strings.xml (Polish) 2021-02-23 13:02:56 +01:00
PhilippC
e40c117228 New translations strings.xml (Polish) 2021-02-23 12:01:21 +01:00
PhilippC
46bded5c16 New translations strings.xml (Japanese) 2021-02-23 10:22:04 +01:00
PhilippC
47ab6df725 New translations strings.xml (Russian) 2021-02-22 12:19:59 +01:00
PhilippC
94a366a13e New translations strings.xml (Dutch) 2021-02-21 21:14:00 +01:00
PhilippC
f23de8f380 New translations strings.xml (French) 2021-02-20 22:56:49 +01:00
PhilippC
f1fb2a47f0 New translations strings.xml (Slovenian) 2021-02-20 21:21:46 +01:00
PhilippC
175c64bdc8 New translations strings.xml (Chinese Simplified) 2021-02-20 10:07:48 +01:00
PhilippC
07ab9f21db New translations strings.xml (Malayalam) 2021-02-20 07:57:13 +01:00
PhilippC
56ece19994 New translations strings.xml (Malayalam) 2021-02-20 07:57:11 +01:00
PhilippC
1163e9956a New translations strings.xml (Japanese) 2021-02-20 05:22:09 +01:00
PhilippC
d02b9438eb New translations strings.xml (Portuguese, Brazilian) 2021-02-20 00:40:22 +01:00
Philipp Crocoll
40cbd51e8b Merge branch 'master' of c:/ph/keepass2android 2021-02-19 12:23:38 +01:00
Philipp Crocoll
490de22dc7 changelog and manifest for 1.09a-pre1 2021-02-19 12:23:18 +01:00
Philipp Crocoll
ef71df7d5f Merge remote-tracking branch 'remotes/origin/l10n_master2'
# resolved Conflicts:
#	src/keepass2android/Resources/values-de/strings.xml
2021-02-19 12:19:24 +01:00
PhilippC
0fbf0179f3 New translations strings.xml (Portuguese, Brazilian) 2021-02-19 12:17:44 +01:00
PhilippC
5a45360ab6 New translations strings.xml (Indonesian) 2021-02-19 12:17:42 +01:00
PhilippC
94ac246d1f New translations strings.xml (Persian) 2021-02-19 12:17:41 +01:00
PhilippC
85ef205fab New translations strings.xml (Croatian) 2021-02-19 12:17:40 +01:00
PhilippC
4dd0215ac8 New translations strings.xml (Norwegian Bokmal) 2021-02-19 12:17:34 +01:00
PhilippC
4fa940af47 New translations strings.xml (Galician) 2021-02-19 12:17:30 +01:00
PhilippC
6e76e4908e New translations strings.xml (Vietnamese) 2021-02-19 12:17:18 +01:00
PhilippC
8991538449 New translations strings.xml (Chinese Simplified) 2021-02-19 12:17:17 +01:00
PhilippC
9a51267b8f New translations strings.xml (Ukrainian) 2021-02-19 12:17:16 +01:00
PhilippC
373128f678 New translations strings.xml (Czech) 2021-02-19 12:17:15 +01:00
PhilippC
563faf6cc5 New translations strings.xml (Romanian) 2021-02-19 12:17:14 +01:00
PhilippC
aff243389d New translations strings.xml (French) 2021-02-19 12:17:12 +01:00
PhilippC
a38be4ad80 New translations strings.xml (Spanish) 2021-02-19 12:17:11 +01:00
PhilippC
bd52090236 New translations strings.xml (Arabic) 2021-02-19 12:17:09 +01:00
PhilippC
5ae11a91c1 New translations strings.xml (Catalan) 2021-02-19 12:17:07 +01:00
PhilippC
f1cc61e8ce New translations strings.xml (Danish) 2021-02-19 12:17:06 +01:00
PhilippC
5af68c5d9b New translations strings.xml (German) 2021-02-19 12:17:04 +01:00
PhilippC
dbe804f223 New translations strings.xml (Greek) 2021-02-19 12:17:03 +01:00
PhilippC
2bbbc4fe94 New translations strings.xml (Basque) 2021-02-19 12:17:02 +01:00
PhilippC
9cd66fc30c New translations strings.xml (Chinese Traditional) 2021-02-19 12:17:00 +01:00
PhilippC
65f6731b61 New translations strings.xml (Finnish) 2021-02-19 12:16:59 +01:00
PhilippC
25defeaf3a New translations strings.xml (Hungarian) 2021-02-19 12:16:58 +01:00
PhilippC
e8d8f13888 New translations strings.xml (Italian) 2021-02-19 12:16:57 +01:00
PhilippC
4a9a767fc6 New translations strings.xml (Japanese) 2021-02-19 12:16:55 +01:00
PhilippC
d8b22b4914 New translations strings.xml (Korean) 2021-02-19 12:16:54 +01:00
PhilippC
ef22f19dec New translations strings.xml (Polish) 2021-02-19 12:16:52 +01:00
PhilippC
4bcf651e5e New translations strings.xml (Portuguese) 2021-02-19 12:16:51 +01:00
PhilippC
19aea7808d New translations strings.xml (Russian) 2021-02-19 12:16:49 +01:00
PhilippC
afa4c9c0f2 New translations strings.xml (Slovak) 2021-02-19 12:16:48 +01:00
PhilippC
d2d853e916 New translations strings.xml (Slovenian) 2021-02-19 12:16:47 +01:00
PhilippC
3ead147be4 New translations strings.xml (Swedish) 2021-02-19 12:16:45 +01:00
PhilippC
c863ff1fd1 New translations strings.xml (Turkish) 2021-02-19 12:16:43 +01:00
PhilippC
6028b10bf1 New translations strings.xml (Dutch) 2021-02-19 12:15:56 +01:00
PhilippC
e74bb8d3b5 New translations strings.xml (Japanese) 2021-02-19 12:06:48 +01:00
Philipp Crocoll
7bbe3fb9b5 allow to choose Light/Dark Design as indicated by the system settings (Android 10+), closes https://github.com/PhilippC/keepass2android/issues/966 2021-02-19 12:04:47 +01:00
Philipp Crocoll
e00eb2a5bc commit unsaved change regarding read-only access to content storage 2021-02-19 11:53:45 +01:00
Philipp Crocoll
23ca92039b pCloud: recover from bad stored access tokens 2021-02-19 11:41:23 +01:00
Philipp Crocoll
54f8916ddd no longer checking for FLAG_SUPPORTS_WRITE for ContontStorage - this often returns false even for non-readonly files, closes https://github.com/PhilippC/keepass2android/issues/1212 2021-02-19 11:41:03 +01:00
Philipp Crocoll
a4b3c933ef allow to move an entry from the EntryActivity menu, closes https://github.com/PhilippC/keepass2android/issues/24 2021-02-19 09:11:35 +01:00
Philipp Crocoll
d0633e883e go back to dx+proguard+shared runtime (r8 seems to break GDrive implementation; shared runtime required for debugging) 2021-02-19 08:44:41 +01:00
Philipp Crocoll
8071e0692a make sure AutoExec items with unknown protocols don't crash the app, closes #1452 2021-02-19 08:44:08 +01:00
Philipp Crocoll
47f07c66bf Merge remote-tracking branch 'remotes/origin/l10n_master2' 2021-02-19 07:04:51 +01:00
Philipp Crocoll
5db30a2b98 Merge branch 'master' of c:/ph/keepass2android 2021-02-19 07:03:15 +01:00
Philipp Crocoll
fcd3cddbc7 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-02-19 07:02:49 +01:00
Philipp Crocoll
3c49d30de6 Merge branch 'master' of c:/ph/keepass2android 2021-02-18 17:57:37 +01:00
Philipp Crocoll
377bffd7a4 Merge branch 'Branch_3fb358c-b' 2021-02-18 17:49:30 +01:00
Philipp Crocoll
6af1da01db fix to autofill: continue if dataset has no name 2021-02-18 17:48:25 +01:00
Philipp Crocoll
a6e430e569 biometric unlock: SetInvalidatedByBiometricEnrollment(false) for API level >= 24 2021-02-18 16:23:23 +01:00
Philipp Crocoll
15d3492ae3 fix for OneDrive file storage unnecessarily prompting for login, closes https://github.com/PhilippC/keepass2android/issues/1038 2021-02-18 16:20:03 +01:00
Philipp Crocoll
7a731da44f code formatting 2021-02-18 16:19:05 +01:00
PhilippC
64a53ce887 New translations strings.xml (Czech) 2021-02-17 00:37:42 +01:00
PhilippC
45355ba972 New translations strings.xml (Ukrainian) 2021-02-16 09:07:54 +01:00
PhilippC
3b769e2a9f New translations strings.xml (Ukrainian) 2021-02-16 08:12:32 +01:00
PhilippC
ed70c3fcb1 New translations strings.xml (Ukrainian) 2021-02-15 11:58:07 +01:00
PhilippC
86d56be044 New translations strings.xml (Ukrainian) 2021-02-15 10:44:25 +01:00
PhilippC
448492cbc1 New translations strings.xml (Ukrainian) 2021-02-12 11:47:00 +01:00
PhilippC
5010eb3a7c New translations strings.xml (Ukrainian) 2021-02-12 11:46:58 +01:00
PhilippC
e429f082e4 New translations strings.xml (Ukrainian) 2021-02-12 10:41:11 +01:00
PhilippC
53be95ac35 New translations strings.xml (Dutch) 2021-02-10 00:48:43 +01:00
PhilippC
1a47b603c7 New translations strings.xml (Dutch) 2021-02-09 23:48:32 +01:00
Philipp Crocoll
4b956af0f7 add debugging to diagnose an issue reported by a user which I can't reproduce 2021-02-09 16:17:47 +01:00
Philipp Crocoll
8a9495edce include subdomains when trying to find an entry from Autofill 2021-02-09 14:45:40 +01:00
Philipp Crocoll
b96e8a5b2b add github support name 2021-02-09 14:45:17 +01:00
Philipp Crocoll
a37789fbfe minor changes adapting to VS upgrade 2021-02-09 14:44:50 +01:00
Philipp Crocoll
8d95fb1660 implement upload sessions for OneDrive to resolve file size restriction (closes https://github.com/PhilippC/keepass2android/issues/1036) 2021-02-09 14:44:02 +01:00
PhilippC
db83855fee New translations strings.xml (Russian) 2021-02-03 22:31:17 +01:00
PhilippC
56d896ca22 New translations strings.xml (Russian) 2021-02-03 21:25:30 +01:00
PhilippC
1169e7183e New translations strings.xml (French) 2021-02-02 22:13:42 +01:00
PhilippC
fe0b377561 New translations strings.xml (Hungarian) 2021-02-01 18:16:36 +01:00
PhilippC
5528d6bc8d New translations strings.xml (Hungarian) 2021-02-01 17:11:32 +01:00
PhilippC
7856ea8130 New translations strings.xml (Chinese Traditional) 2021-01-24 10:29:16 +01:00
PhilippC
dbc684306b New translations strings.xml (Chinese Traditional) 2021-01-24 09:26:46 +01:00
Philipp Crocoll
19187352b5 Merge branch 'master' of c:/ph/keepass2android 2021-01-24 07:06:12 +01:00
PhilippC
63ffd56fd9 New translations strings.xml (German) 2021-01-24 06:42:14 +01:00
PhilippC
4ea3da158d New translations strings.xml (Czech) 2021-01-23 03:14:44 +01:00
PhilippC
00daf35918 New translations strings.xml (Czech) 2021-01-23 02:17:08 +01:00
PhilippC
18bfc06091 New translations strings.xml (Czech) 2021-01-23 01:05:44 +01:00
PhilippC
0cf78f5c8e New translations strings.xml (Japanese) 2021-01-21 11:17:01 +01:00
PhilippC
1193b37a53 New translations strings.xml (Chinese Simplified) 2021-01-19 17:45:56 +01:00
Philipp Crocoll
6cb3fb2354 manifest for 1.08d-r4 2021-01-17 04:54:33 +01:00
PhilippC
1109b40af4 New translations strings.xml (Finnish) 2021-01-17 04:06:04 +01:00
PhilippC
479ab9377e New translations strings.xml (Finnish) 2021-01-17 04:06:02 +01:00
PhilippC
5e01b3c0ab New translations strings.xml (Hebrew) 2021-01-17 04:06:00 +01:00
PhilippC
3b876dde1c New translations strings.xml (Hebrew) 2021-01-17 04:05:58 +01:00
PhilippC
36fdc5e737 New translations strings.xml (Greek) 2021-01-17 04:05:52 +01:00
PhilippC
0410687ae1 New translations strings.xml (Italian) 2021-01-17 04:05:49 +01:00
PhilippC
d05be2b599 New translations strings.xml (Japanese) 2021-01-17 04:05:47 +01:00
PhilippC
39b0d4f0c2 New translations strings.xml (Japanese) 2021-01-17 04:05:45 +01:00
PhilippC
661aefaebc New translations strings.xml (Italian) 2021-01-17 04:05:36 +01:00
PhilippC
d25c951c88 New translations strings.xml (Portuguese) 2021-01-17 04:05:34 +01:00
PhilippC
6d5d8142b3 New translations strings.xml (German) 2021-01-17 04:05:30 +01:00
PhilippC
dee8bdec24 New translations strings.xml (Romanian) 2021-01-17 04:05:25 +01:00
PhilippC
e5010b761d New translations strings.xml (French) 2021-01-17 04:05:21 +01:00
PhilippC
8b345d4fac New translations strings.xml (Spanish) 2021-01-17 04:05:17 +01:00
PhilippC
d9679801e2 New translations strings.xml (Arabic) 2021-01-17 04:05:13 +01:00
PhilippC
293864e62b New translations strings.xml (German) 2021-01-17 04:05:12 +01:00
PhilippC
ddd1a25d93 New translations strings.xml (Arabic) 2021-01-17 04:05:09 +01:00
PhilippC
a44e3bae90 New translations strings.xml (Bulgarian) 2021-01-17 04:05:07 +01:00
PhilippC
97c682416c New translations strings.xml (Czech) 2021-01-17 04:05:01 +01:00
PhilippC
490d32e8d6 New translations strings.xml (Czech) 2021-01-17 04:04:59 +01:00
PhilippC
36739e1b11 New translations strings.xml (Danish) 2021-01-17 04:04:56 +01:00
PhilippC
8639cc313a New translations strings.xml (Danish) 2021-01-17 04:04:54 +01:00
PhilippC
20cacc6dd5 New translations strings.xml (Portuguese) 2021-01-17 04:04:50 +01:00
PhilippC
b60b0f2bf6 New translations strings.xml (Russian) 2021-01-17 04:04:48 +01:00
PhilippC
4db37a0f18 New translations strings.xml (Indonesian) 2021-01-17 04:04:43 +01:00
PhilippC
6a28219b7f New translations strings.xml (Norwegian Bokmal) 2021-01-17 04:04:23 +01:00
PhilippC
904885db6c New translations strings.xml (Norwegian Bokmal) 2021-01-17 04:04:21 +01:00
PhilippC
6a7b003246 New translations strings.xml (Galician) 2021-01-17 04:04:18 +01:00
PhilippC
a2aeb67d6d New translations strings.xml (Russian) 2021-01-17 04:04:16 +01:00
PhilippC
412b4f26b7 New translations strings.xml (Slovenian) 2021-01-17 04:04:10 +01:00
PhilippC
b95e380bd8 New translations strings.xml (Slovenian) 2021-01-17 04:04:08 +01:00
PhilippC
bf46b015a5 New translations strings.xml (Galician) 2021-01-17 04:04:02 +01:00
PhilippC
d23f4c8095 New translations strings.xml (Ukrainian) 2021-01-17 04:03:56 +01:00
PhilippC
70c6c59e74 New translations strings.xml (Ukrainian) 2021-01-17 04:03:54 +01:00
PhilippC
e69f662608 New translations strings.xml (Chinese Traditional) 2021-01-17 04:03:50 +01:00
PhilippC
8d934663c5 New translations strings.xml (Vietnamese) 2021-01-17 04:03:47 +01:00
PhilippC
12070b042f New translations strings.xml (Chinese Traditional) 2021-01-17 04:03:43 +01:00
148 changed files with 17008 additions and 3423 deletions

View File

@@ -139,9 +139,6 @@
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\jackson-core-2.7.4.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedJar Include="Jars\dropbox-core-sdk-3.1.1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\gson-2.8.1.jar" />
</ItemGroup>
@@ -154,4 +151,7 @@
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\okhttp-4.10.0-RC1.jar" />
</ItemGroup>
<ItemGroup>
<EmbeddedJar Include="Jars\dropbox-core-sdk-4.0.0.jar" />
</ItemGroup>
</Project>

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,8 +33,11 @@ namespace KeePassLib.Collections
private Dictionary<int, ProtectedBinary> m_d =
new Dictionary<int, ProtectedBinary>();
public ProtectedBinarySet()
private readonly bool m_bDedupAdd;
public ProtectedBinarySet(bool bDedupAdd)
{
m_bDedupAdd = bDedupAdd;
}
IEnumerator IEnumerable.GetEnumerator()
@@ -47,15 +50,10 @@ namespace KeePassLib.Collections
return m_d.GetEnumerator();
}
public void Clear()
{
m_d.Clear();
}
private int GetFreeID()
{
int i = m_d.Count;
while(m_d.ContainsKey(i)) { ++i; }
while (m_d.ContainsKey(i)) { ++i; }
Debug.Assert(i == m_d.Count); // m_d.Count should be free
return i;
}
@@ -63,7 +61,7 @@ namespace KeePassLib.Collections
public ProtectedBinary Get(int iID)
{
ProtectedBinary pb;
if(m_d.TryGetValue(iID, out pb)) return pb;
if (m_d.TryGetValue(iID, out pb)) return pb;
// Debug.Assert(false); // No assert
return null;
@@ -71,12 +69,12 @@ namespace KeePassLib.Collections
public int Find(ProtectedBinary pb)
{
if(pb == null) { Debug.Assert(false); return -1; }
if (pb == null) { Debug.Assert(false); return -1; }
// Fast search by reference
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
foreach (KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if(object.ReferenceEquals(pb, kvp.Value))
if (object.ReferenceEquals(pb, kvp.Value))
{
Debug.Assert(pb.Equals(kvp.Value));
return kvp.Key;
@@ -84,9 +82,9 @@ namespace KeePassLib.Collections
}
// Slow search by content
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
foreach (KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if(pb.Equals(kvp.Value)) return kvp.Key;
if (pb.Equals(kvp.Value)) return kvp.Key;
}
// Debug.Assert(false); // No assert
@@ -95,28 +93,26 @@ namespace KeePassLib.Collections
public void Set(int iID, ProtectedBinary pb)
{
if(iID < 0) { Debug.Assert(false); return; }
if(pb == null) { Debug.Assert(false); return; }
if (iID < 0) { Debug.Assert(false); return; }
if (pb == null) { Debug.Assert(false); return; }
m_d[iID] = pb;
}
public void Add(ProtectedBinary pb)
{
if(pb == null) { Debug.Assert(false); return; }
if (pb == null) { Debug.Assert(false); return; }
int i = Find(pb);
if(i >= 0) return; // Exists already
if (m_bDedupAdd && (Find(pb) >= 0)) return; // Exists already
i = GetFreeID();
m_d[i] = pb;
m_d[GetFreeID()] = pb;
}
public void AddFrom(ProtectedBinaryDictionary d)
{
if(d == null) { Debug.Assert(false); return; }
if (d == null) { Debug.Assert(false); return; }
foreach(KeyValuePair<string, ProtectedBinary> kvp in d)
foreach (KeyValuePair<string, ProtectedBinary> kvp in d)
{
Add(kvp.Value);
}
@@ -124,16 +120,16 @@ namespace KeePassLib.Collections
public void AddFrom(PwGroup pg)
{
if(pg == null) { Debug.Assert(false); return; }
if (pg == null) { Debug.Assert(false); return; }
EntryHandler eh = delegate(PwEntry pe)
EntryHandler eh = delegate (PwEntry pe)
{
if(pe == null) { Debug.Assert(false); return true; }
if (pe == null) { Debug.Assert(false); return true; }
AddFrom(pe.Binaries);
foreach(PwEntry peHistory in pe.History)
foreach (PwEntry peHistory in pe.History)
{
if(peHistory == null) { Debug.Assert(false); continue; }
if (peHistory == null) { Debug.Assert(false); continue; }
AddFrom(peHistory.Binaries);
}
@@ -148,9 +144,9 @@ namespace KeePassLib.Collections
int n = m_d.Count;
ProtectedBinary[] v = new ProtectedBinary[n];
foreach(KeyValuePair<int, ProtectedBinary> kvp in m_d)
foreach (KeyValuePair<int, ProtectedBinary> kvp in m_d)
{
if((kvp.Key < 0) || (kvp.Key >= n))
if ((kvp.Key < 0) || (kvp.Key >= n))
{
Debug.Assert(false);
throw new InvalidOperationException();
@@ -159,9 +155,9 @@ namespace KeePassLib.Collections
v[kvp.Key] = kvp.Value;
}
for(int i = 0; i < n; ++i)
for (int i = 0; i < n; ++i)
{
if(v[i] == null)
if (v[i] == null)
{
Debug.Assert(false);
throw new InvalidOperationException();

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,8 +20,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Text;
using KeePassLib.Interfaces;
@@ -34,48 +34,74 @@ namespace KeePassLib.Collections
public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
{
private SortedDictionary<string, string> m_dict =
private SortedDictionary<string, string> m_d =
new SortedDictionary<string, string>();
// Non-null if and only if last mod. times should be remembered
private Dictionary<string, DateTime> m_dLastMod = null;
public int Count
{
get { return m_dict.Count; }
get { return m_d.Count; }
}
public StringDictionaryEx()
{
}
internal StringDictionaryEx(bool bRememberLastMod)
{
if (bRememberLastMod) m_dLastMod = new Dictionary<string, DateTime>();
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_dict.GetEnumerator();
return m_d.GetEnumerator();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return m_dict.GetEnumerator();
return m_d.GetEnumerator();
}
public StringDictionaryEx CloneDeep()
{
StringDictionaryEx sdNew = new StringDictionaryEx();
foreach(KeyValuePair<string, string> kvp in m_dict)
sdNew.m_dict[kvp.Key] = kvp.Value; // Strings are immutable
foreach (KeyValuePair<string, string> kvp in m_d)
sdNew.m_d[kvp.Key] = kvp.Value;
if (m_dLastMod != null)
sdNew.m_dLastMod = new Dictionary<string, DateTime>(m_dLastMod);
Debug.Assert(Equals(sdNew));
return sdNew;
}
public bool Equals(StringDictionaryEx sdOther)
{
if(sdOther == null) { Debug.Assert(false); return false; }
if (sdOther == null) { Debug.Assert(false); return false; }
if(m_dict.Count != sdOther.m_dict.Count) return false;
if (m_d.Count != sdOther.m_d.Count) return false;
foreach(KeyValuePair<string, string> kvp in sdOther.m_dict)
foreach (KeyValuePair<string, string> kvp in sdOther.m_d)
{
string str = Get(kvp.Key);
if((str == null) || (str != kvp.Value)) return false;
if ((str == null) || (str != kvp.Value)) return false;
}
int cLastModT = ((m_dLastMod != null) ? m_dLastMod.Count : -1);
int cLastModO = ((sdOther.m_dLastMod != null) ? sdOther.m_dLastMod.Count : -1);
if (cLastModT != cLastModO) return false;
if (m_dLastMod != null)
{
foreach (KeyValuePair<string, DateTime> kvp in sdOther.m_dLastMod)
{
DateTime? odt = GetLastModificationTime(kvp.Key);
if (!odt.HasValue) return false;
if (odt.Value != kvp.Value) return false;
}
}
return true;
@@ -83,48 +109,62 @@ namespace KeePassLib.Collections
public string Get(string strName)
{
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
string s;
if(m_dict.TryGetValue(strName, out s)) return s;
string str;
m_d.TryGetValue(strName, out str);
return str;
}
internal DateTime? GetLastModificationTime(string strName)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (m_dLastMod == null) return null;
DateTime dt;
if (m_dLastMod.TryGetValue(strName, out dt)) return dt;
return null;
}
public bool Exists(string strName)
{
if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
return m_dict.ContainsKey(strName);
return m_d.ContainsKey(strName);
}
/// <summary>
/// Set a string.
/// </summary>
/// <param name="strField">Identifier of the string field to modify.</param>
/// <param name="strNewValue">New value. This parameter must not be <c>null</c>.</param>
/// <exception cref="System.ArgumentNullException">Thrown if one of the input
/// parameters is <c>null</c>.</exception>
public void Set(string strField, string strNewValue)
public void Set(string strName, string strValue)
{
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
if(strNewValue == null) { Debug.Assert(false); throw new ArgumentNullException("strNewValue"); }
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (strValue == null) { Debug.Assert(false); throw new ArgumentNullException("strValue"); }
m_dict[strField] = strNewValue;
m_d[strName] = strValue;
if (m_dLastMod != null) m_dLastMod[strName] = DateTime.UtcNow;
}
/// <summary>
/// Delete a string.
/// </summary>
/// <param name="strField">Name of the string field to delete.</param>
/// <returns>Returns <c>true</c> if the field has been successfully
/// removed, otherwise the return value is <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if the input
/// parameter is <c>null</c>.</exception>
public bool Remove(string strField)
internal void Set(string strName, string strValue, DateTime? odtLastMod)
{
if(strField == null) { Debug.Assert(false); throw new ArgumentNullException("strField"); }
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (strValue == null) { Debug.Assert(false); throw new ArgumentNullException("strValue"); }
return m_dict.Remove(strField);
m_d[strName] = strValue;
if (m_dLastMod != null)
{
if (odtLastMod.HasValue) m_dLastMod[strName] = odtLastMod.Value;
else m_dLastMod.Remove(strName);
}
}
public bool Remove(string strName)
{
if (strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
if (m_dLastMod != null) m_dLastMod.Remove(strName);
return m_d.Remove(strName);
}
}
}

View File

@@ -28,15 +28,29 @@ using KeePassLib.Resources;
namespace KeePassLib.Cryptography.Cipher
{
public sealed class ChaCha20Engine : ICipherEngine2
{
private PwUuid m_uuid = new PwUuid(new byte[] {
0xD6, 0x03, 0x8A, 0x2B, 0x8B, 0x6F, 0x4C, 0xB5,
0xA5, 0x24, 0x33, 0x9A, 0x31, 0xDB, 0xB5, 0x9A
});
{
private static PwUuid m_uuid = null;
internal static PwUuid ChaCha20Uuid
{
get
{
PwUuid pu = m_uuid;
if (pu == null)
{
pu = new PwUuid(new byte[] {
0xD6, 0x03, 0x8A, 0x2B, 0x8B, 0x6F, 0x4C, 0xB5,
0xA5, 0x24, 0x33, 0x9A, 0x31, 0xDB, 0xB5, 0x9A });
m_uuid = pu;
}
return pu;
}
}
public PwUuid CipherUuid
{
get { return m_uuid; }
get { return ChaCha20Uuid; }
}
public string DisplayName

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,17 +21,22 @@ using System;
namespace KeePassLib.Interfaces
{
public interface IStructureItem : ITimeLogger // Provides LocationChanged
{
PwUuid Uuid
{
get;
set;
}
public interface IStructureItem : ITimeLogger // Provides LocationChanged
{
PwUuid Uuid
{
get;
set;
}
PwGroup ParentGroup
{
get;
}
}
}
PwGroup ParentGroup
{
get;
}
PwUuid PreviousParentGroup
{
get;
}
}
}

View File

@@ -74,6 +74,7 @@
<Compile Include="IDatabaseFormat.cs" />
<Compile Include="Keys\KcpKeyFile.Xml.cs" />
<Compile Include="Kp2aLog.cs" />
<Compile Include="PwGroup.Search.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Resources\KLRes.Generated.cs" />
<Compile Include="Resources\KSRes.Generated.cs" />

View File

@@ -1,8 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2013 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,61 +18,106 @@
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
#if !KeePassUAP
using System.Drawing;
using System.IO;
#endif
using KeePassLib.Utility;
namespace KeePassLib
{
/// <summary>
/// Custom icon. <c>PwCustomIcon</c> objects are immutable.
/// </summary>
public sealed class PwCustomIcon
{
private PwUuid m_pwUuid;
private byte[] m_pbImageDataPng;
private Android.Graphics.Bitmap m_pCachedImage;
// Recommended maximum sizes, not obligatory
internal const int MaxWidth = 128;
internal const int MaxHeight = 128;
private readonly PwUuid m_uuid;
private readonly byte[] m_pbImageDataPng;
private string m_strName = string.Empty;
private DateTime? m_odtLastMod = null;
private Dictionary<long, Android.Graphics.Bitmap> m_dImageCache = new Dictionary<long, Android.Graphics.Bitmap>();
public PwUuid Uuid
{
get { return m_pwUuid; }
get { return m_uuid; }
}
public byte[] ImageDataPng
{
get { return m_pbImageDataPng; }
// When allowing 'set', do not copy the cache in 'Clone'
}
public string Name
{
get { return m_strName; }
set
{
if (value == null) throw new ArgumentNullException("value");
m_strName = value;
}
}
public DateTime? LastModificationTime
{
get { return m_odtLastMod; }
set { m_odtLastMod = value; }
}
[Obsolete("Use GetImage instead.")]
public Android.Graphics.Bitmap Image
{
get { return m_pCachedImage; }
get { return GetImage(); } // Backward compatibility
}
public PwCustomIcon(PwUuid pu, byte[] pbImageDataPng)
{
if (pu == null) { Debug.Assert(false); throw new ArgumentNullException("pu"); }
if (pu.Equals(PwUuid.Zero)) { Debug.Assert(false); throw new ArgumentOutOfRangeException("pu"); }
if (pbImageDataPng == null) { Debug.Assert(false); throw new ArgumentNullException("pbImageDataPng"); }
m_uuid = pu;
m_pbImageDataPng = pbImageDataPng;
}
public PwCustomIcon(PwUuid pwUuid, byte[] pbImageDataPng)
private static long GetKey(int w, int h)
{
Debug.Assert(pwUuid != null);
if(pwUuid == null) throw new ArgumentNullException("pwUuid");
Debug.Assert(!pwUuid.Equals(PwUuid.Zero));
if(pwUuid.Equals(PwUuid.Zero)) throw new ArgumentException("pwUuid == 0");
return (((long)w << 32) ^ (long)h);
}
Debug.Assert(pbImageDataPng != null);
if(pbImageDataPng == null) throw new ArgumentNullException("pbImageDataPng");
/// <summary>
/// Get the icon as an <c>Image</c> (original size).
/// </summary>
public Android.Graphics.Bitmap GetImage()
{
const long lKey = -1;
m_pwUuid = pwUuid;
m_pbImageDataPng = pbImageDataPng;
Android.Graphics.Bitmap img;
if (m_dImageCache.TryGetValue(lKey, out img)) return img;
#if !KeePassLibSD
// MemoryStream ms = new MemoryStream(m_pbImageDataPng, false);
// m_pCachedImage = Image.FromStream(ms);
// ms.Close();
m_pCachedImage = GfxUtil.LoadImage(m_pbImageDataPng);
#else
m_pCachedImage = null;
#endif
try { img = GfxUtil.LoadImage(m_pbImageDataPng); }
catch (Exception) { Debug.Assert(false); }
m_dImageCache[lKey] = img;
return img;
}
internal PwCustomIcon Clone()
{
PwCustomIcon ico = new PwCustomIcon(m_uuid, m_pbImageDataPng);
ico.m_strName = m_strName;
ico.m_odtLastMod = m_odtLastMod;
ico.m_dImageCache = m_dImageCache; // Same image data
return ico;
}
}
}

View File

@@ -97,7 +97,7 @@ namespace KeePassLib
private int m_nHistoryMaxItems = DefaultHistoryMaxItems;
private long m_lHistoryMaxSize = DefaultHistoryMaxSize; // In bytes
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
private StringDictionaryEx m_dCustomData = new StringDictionaryEx(true);
private VariantDictionary m_dPublicCustomData = new VariantDictionary();
private byte[] m_pbHashOfFileOnDisk = null;
@@ -690,9 +690,9 @@ namespace KeePassLib
public void MergeIn(PwDatabase pdSource, PwMergeMethod mm,
IStatusLogger slStatus)
{
if(pdSource == null) throw new ArgumentNullException("pdSource");
if (pdSource == null) throw new ArgumentNullException("pdSource");
if(mm == PwMergeMethod.CreateNewUuids)
if (mm == PwMergeMethod.CreateNewUuids)
{
pdSource.RootGroup.Uuid = new PwUuid(true);
pdSource.RootGroup.CreateNewItemUuids(true, true, true);
@@ -707,7 +707,7 @@ namespace KeePassLib
PwObjectPoolEx ppOrg = PwObjectPoolEx.FromGroup(m_pgRootGroup);
PwObjectPoolEx ppSrc = PwObjectPoolEx.FromGroup(pdSource.RootGroup);
GroupHandler ghSrc = delegate(PwGroup pg)
GroupHandler ghSrc = delegate (PwGroup pg)
{
// if(pg == pdSource.m_pgRootGroup) return true;
@@ -716,11 +716,11 @@ namespace KeePassLib
// pool should not be modified)
PwGroup pgLocal = m_pgRootGroup.FindGroup(pg.Uuid, true);
if(pgLocal == null)
if (pgLocal == null)
{
PwGroup pgSourceParent = pg.ParentGroup;
PwGroup pgLocalContainer;
if(pgSourceParent == null)
if (pgSourceParent == null)
{
// pg is the root group of pdSource, and no corresponding
// local group was found; create the group within the
@@ -728,17 +728,23 @@ namespace KeePassLib
Debug.Assert(pg == pdSource.m_pgRootGroup);
pgLocalContainer = m_pgRootGroup;
}
else if(pgSourceParent == pdSource.m_pgRootGroup)
else if (pgSourceParent == pdSource.m_pgRootGroup)
pgLocalContainer = m_pgRootGroup;
else
pgLocalContainer = m_pgRootGroup.FindGroup(pgSourceParent.Uuid, true);
Debug.Assert(pgLocalContainer != null);
if(pgLocalContainer == null) pgLocalContainer = m_pgRootGroup;
if (pgLocalContainer == null) pgLocalContainer = m_pgRootGroup;
PwGroup pgNew = new PwGroup(false, false);
pgNew.Uuid = pg.Uuid;
pgNew.AssignProperties(pg, false, true);
if (!pgLocalContainer.CanAddGroup(pgNew))
{
Debug.Assert(false);
pgLocalContainer = m_pgRootGroup;
pgLocalContainer.CheckCanAddGroup(pgNew);
}
// pgLocalContainer.AddGroup(pgNew, true);
InsertObjectAtBestPos<PwGroup>(pgLocalContainer.Groups, pgNew, ppSrc);
pgNew.ParentGroup = pgLocalContainer;
@@ -747,9 +753,9 @@ namespace KeePassLib
{
Debug.Assert(mm != PwMergeMethod.CreateNewUuids);
if(mm == PwMergeMethod.OverwriteExisting)
if (mm == PwMergeMethod.OverwriteExisting)
pgLocal.AssignProperties(pg, false, false);
else if((mm == PwMergeMethod.OverwriteIfNewer) ||
else if ((mm == PwMergeMethod.OverwriteIfNewer) ||
(mm == PwMergeMethod.Synchronize))
{
pgLocal.AssignProperties(pg, true, false);
@@ -760,23 +766,23 @@ namespace KeePassLib
return ((slStatus != null) ? slStatus.ContinueWork() : true);
};
EntryHandler ehSrc = delegate(PwEntry pe)
EntryHandler ehSrc = delegate (PwEntry pe)
{
// PwEntry peLocal = m_pgRootGroup.FindEntry(pe.Uuid, true);
PwEntry peLocal = (ppOrg.GetItemByUuid(pe.Uuid) as PwEntry);
Debug.Assert(object.ReferenceEquals(peLocal,
m_pgRootGroup.FindEntry(pe.Uuid, true)));
if(peLocal == null)
if (peLocal == null)
{
PwGroup pgSourceParent = pe.ParentGroup;
PwGroup pgLocalContainer;
if(pgSourceParent == pdSource.m_pgRootGroup)
if (pgSourceParent == pdSource.m_pgRootGroup)
pgLocalContainer = m_pgRootGroup;
else
pgLocalContainer = m_pgRootGroup.FindGroup(pgSourceParent.Uuid, true);
Debug.Assert(pgLocalContainer != null);
if(pgLocalContainer == null) pgLocalContainer = m_pgRootGroup;
if (pgLocalContainer == null) pgLocalContainer = m_pgRootGroup;
PwEntry peNew = new PwEntry(false, false);
peNew.Uuid = pe.Uuid;
@@ -796,19 +802,19 @@ namespace KeePassLib
bool bEquals = peLocal.EqualsEntry(pe, cmpOpt, MemProtCmpMode.None);
bool bOrgBackup = !bEquals;
if(mm != PwMergeMethod.OverwriteExisting)
if (mm != PwMergeMethod.OverwriteExisting)
bOrgBackup &= (TimeUtil.CompareLastMod(pe, peLocal, true) > 0);
bOrgBackup &= !pe.HasBackupOfData(peLocal, false, true);
if(bOrgBackup) peLocal.CreateBackup(null); // Maintain at end
if (bOrgBackup) peLocal.CreateBackup(null); // Maintain at end
bool bSrcBackup = !bEquals && (mm != PwMergeMethod.OverwriteExisting);
bSrcBackup &= (TimeUtil.CompareLastMod(peLocal, pe, true) > 0);
bSrcBackup &= !peLocal.HasBackupOfData(pe, false, true);
if(bSrcBackup) pe.CreateBackup(null); // Maintain at end
if (bSrcBackup) pe.CreateBackup(null); // Maintain at end
if(mm == PwMergeMethod.OverwriteExisting)
if (mm == PwMergeMethod.OverwriteExisting)
peLocal.AssignProperties(pe, false, false, false);
else if((mm == PwMergeMethod.OverwriteIfNewer) ||
else if ((mm == PwMergeMethod.OverwriteIfNewer) ||
(mm == PwMergeMethod.Synchronize))
{
peLocal.AssignProperties(pe, true, false, false);
@@ -822,13 +828,13 @@ namespace KeePassLib
};
ghSrc(pdSource.RootGroup);
if(!pdSource.RootGroup.TraverseTree(TraversalMethod.PreOrder, ghSrc, ehSrc))
if (!pdSource.RootGroup.TraverseTree(TraversalMethod.PreOrder, ghSrc, ehSrc))
throw new InvalidOperationException();
IStatusLogger slPrevStatus = m_slStatus;
m_slStatus = slStatus;
if(mm == PwMergeMethod.Synchronize)
if (mm == PwMergeMethod.Synchronize)
{
RelocateGroups(ppOrg, ppSrc);
RelocateEntries(ppOrg, ppSrc);
@@ -838,24 +844,24 @@ namespace KeePassLib
MergeInLocationChanged(m_pgRootGroup, ppOrg, ppSrc);
ppOrg = null; // Pools are now invalid, because the location
ppSrc = null; // changed times have been merged in
// Delete *after* relocating, because relocating might
// empty some groups that are marked for deletion (and
// objects that weren't relocated yet might prevent the
// deletion)
Dictionary<PwUuid, PwDeletedObject> dOrgDel = CreateDeletedObjectsPool();
MergeInDeletionInfo(pdSource.m_vDeletedObjects, dOrgDel);
ApplyDeletions(m_pgRootGroup, dOrgDel);
// The list and the dictionary should be kept in sync
Debug.Assert(m_vDeletedObjects.UCount == (uint)dOrgDel.Count);
}
// Delete *after* relocating, because relocating might empty
// some groups that are marked for deletion (and objects
// that weren't relocated yet might prevent the deletion)
Dictionary<PwUuid, PwDeletedObject> dDel = CreateDeletedObjectsPool();
if (mm == PwMergeMethod.Synchronize)
MergeInDeletionInfo(pdSource.m_vDeletedObjects, dDel);
ApplyDeletions(m_pgRootGroup, dDel);
// The list and the dictionary should be kept in sync
Debug.Assert(m_vDeletedObjects.UCount == (uint)dDel.Count);
// Must be called *after* merging groups, because group UUIDs
// are required for recycle bin and entry template UUIDs
MergeInDbProperties(pdSource, mm);
MergeInCustomIcons(pdSource);
MergeInCustomIcons(pdSource, dDel);
Debug.Assert(m_vDeletedObjects.UCount == (uint)dDel.Count);
MaintainBackups();
@@ -863,15 +869,79 @@ namespace KeePassLib
m_slStatus = slPrevStatus;
}
private void MergeInCustomIcons(PwDatabase pdSource)
{
foreach(PwCustomIcon pwci in pdSource.CustomIcons)
{
if(GetCustomIconIndex(pwci.Uuid) >= 0) continue;
m_vCustomIcons.Add(pwci); // PwCustomIcon is immutable
m_bUINeedsIconUpdate = true;
private void MergeInCustomIcons(PwDatabase pdSource,
Dictionary<PwUuid, PwDeletedObject> dDel)
{
bool bIconsMod = false;
Dictionary<PwUuid, int> d = new Dictionary<PwUuid, int>();
for (int i = m_vCustomIcons.Count - 1; i >= 0; --i)
d[m_vCustomIcons[i].Uuid] = i;
Debug.Assert(d.Count == m_vCustomIcons.Count); // UUIDs unique
foreach (PwCustomIcon ciS in pdSource.m_vCustomIcons)
{
int iT;
if (d.TryGetValue(ciS.Uuid, out iT))
{
PwCustomIcon ciT = m_vCustomIcons[iT];
DateTime? odtT = ciT.LastModificationTime;
DateTime? odtS = ciS.LastModificationTime;
if (odtT.HasValue && odtS.HasValue)
{
if (odtT.Value >= odtS.Value) continue;
}
else if (odtT.HasValue) continue;
else if (!odtS.HasValue) continue; // Both no time
m_vCustomIcons[iT] = ciS.Clone();
}
else
{
d[ciS.Uuid] = m_vCustomIcons.Count;
m_vCustomIcons.Add(ciS.Clone());
}
bIconsMod = true;
}
List<PwDeletedObject> lObsoleteDel = new List<PwDeletedObject>();
foreach (KeyValuePair<PwUuid, PwDeletedObject> kvpDel in dDel)
{
int iT;
if (d.TryGetValue(kvpDel.Key, out iT))
{
PwCustomIcon ci = m_vCustomIcons[iT];
if (ci == null) { Debug.Assert(false); continue; } // Dup. del. obj.?
DateTime? odt = ci.LastModificationTime;
if (odt.HasValue && (odt.Value > kvpDel.Value.DeletionTime))
lObsoleteDel.Add(kvpDel.Value);
else
{
m_vCustomIcons[iT] = null; // Preserve indices, removed below
bIconsMod = true;
}
}
}
Predicate<PwCustomIcon> f = delegate (PwCustomIcon ci) { return (ci == null); };
m_vCustomIcons.RemoveAll(f);
foreach (PwDeletedObject pdo in lObsoleteDel)
{
// Prevent future deletion attempts
if (!m_vDeletedObjects.Remove(pdo)) { Debug.Assert(false); }
if (!dDel.Remove(pdo.Uuid)) { Debug.Assert(false); }
}
if (bIconsMod) m_bUINeedsIconUpdate = true;
FixCustomIconRefs();
}
private Dictionary<PwUuid, PwDeletedObject> CreateDeletedObjectsPool()
@@ -1212,7 +1282,9 @@ namespace KeePassLib
PwObjectBlock<T> b = new PwObjectBlock<T>();
DateTime dtLoc;
PwObjectPoolEx pPool = GetBestPool(t, ppOrg, ppSrc, out dtLoc);
PwUuid puPrevParent;
PwObjectPoolEx pPool = GetBestPool(t, ppOrg, ppSrc, out dtLoc,
out puPrevParent);
b.Add(t, dtLoc, pPool);
lBlocks.Add(b);
@@ -1247,7 +1319,7 @@ namespace KeePassLib
}
if(idSrcNext == 0) break;
pPool = GetBestPool(tNext, ppOrg, ppSrc, out dtLoc);
pPool = GetBestPool(tNext, ppOrg, ppSrc, out dtLoc, out puPrevParent);
b.Add(tNext, dtLoc, pPool);
++u;
@@ -1260,28 +1332,31 @@ namespace KeePassLib
}
private static PwObjectPoolEx GetBestPool<T>(T t, PwObjectPoolEx ppOrg,
PwObjectPoolEx ppSrc, out DateTime dtLoc)
PwObjectPoolEx ppSrc, out DateTime dtLoc, out PwUuid puPrevParent)
where T : class, ITimeLogger, IStructureItem, IDeepCloneable<T>
{
PwObjectPoolEx p = null;
dtLoc = TimeUtil.SafeMinValueUtc;
PwObjectPoolEx p = null;
dtLoc = TimeUtil.SafeMinValueUtc;
puPrevParent = PwUuid.Zero;
IStructureItem ptOrg = ppOrg.GetItemByUuid(t.Uuid);
if(ptOrg != null)
{
dtLoc = ptOrg.LocationChanged;
p = ppOrg;
}
IStructureItem ptOrg = ppOrg.GetItemByUuid(t.Uuid);
if (ptOrg != null)
{
dtLoc = ptOrg.LocationChanged;
puPrevParent = ptOrg.PreviousParentGroup;
p = ppOrg;
}
IStructureItem ptSrc = ppSrc.GetItemByUuid(t.Uuid);
if((ptSrc != null) && (ptSrc.LocationChanged > dtLoc))
{
dtLoc = ptSrc.LocationChanged;
p = ppSrc;
}
IStructureItem ptSrc = ppSrc.GetItemByUuid(t.Uuid);
if ((ptSrc != null) && (ptSrc.LocationChanged > dtLoc))
{
dtLoc = ptSrc.LocationChanged;
puPrevParent = ptSrc.PreviousParentGroup;
p = ppSrc;
}
Debug.Assert(p != null);
return p;
Debug.Assert(p != null);
return p;
}
private static int FindLocationChangedPivot<T>(List<PwObjectBlock<T>> lBlocks,
@@ -1307,30 +1382,40 @@ namespace KeePassLib
return iPosMax;
}
private static void MergeInLocationChanged(PwGroup pg,
PwObjectPoolEx ppOrg, PwObjectPoolEx ppSrc)
{
GroupHandler gh = delegate(PwGroup pgSub)
{
DateTime dt;
if(GetBestPool<PwGroup>(pgSub, ppOrg, ppSrc, out dt) != null)
pgSub.LocationChanged = dt;
else { Debug.Assert(false); }
return true;
};
private static void MergeInLocationChanged(PwGroup pg,
PwObjectPoolEx ppOrg, PwObjectPoolEx ppSrc)
{
GroupHandler gh = delegate (PwGroup pgSub)
{
DateTime dt;
PwUuid puPrevParent;
if (GetBestPool<PwGroup>(pgSub, ppOrg, ppSrc, out dt,
out puPrevParent) != null)
{
pgSub.LocationChanged = dt;
pgSub.PreviousParentGroup = puPrevParent;
}
else { Debug.Assert(false); }
return true;
};
EntryHandler eh = delegate(PwEntry pe)
{
DateTime dt;
if(GetBestPool<PwEntry>(pe, ppOrg, ppSrc, out dt) != null)
pe.LocationChanged = dt;
else { Debug.Assert(false); }
return true;
};
EntryHandler eh = delegate (PwEntry pe)
{
DateTime dt;
PwUuid puPrevParent;
if (GetBestPool<PwEntry>(pe, ppOrg, ppSrc, out dt,
out puPrevParent) != null)
{
pe.LocationChanged = dt;
pe.PreviousParentGroup = puPrevParent;
}
else { Debug.Assert(false); }
return true;
};
gh(pg);
pg.TraverseTree(TraversalMethod.PreOrder, gh, eh);
}
gh(pg);
pg.TraverseTree(TraversalMethod.PreOrder, gh, eh);
}
private static void InsertObjectAtBestPos<T>(PwObjectList<T> lItems,
T tNew, PwObjectPoolEx ppSrc)
@@ -1445,12 +1530,18 @@ namespace KeePassLib
foreach(KeyValuePair<string, string> kvp in pdSource.m_dCustomData)
{
if(bSourceNewer || !m_dCustomData.Exists(kvp.Key))
m_dCustomData.Set(kvp.Key, kvp.Value);
m_dCustomData.Set(kvp.Key, kvp.Value, null);
}
VariantDictionary vdLocal = m_dPublicCustomData; // Backup
m_dPublicCustomData = (VariantDictionary)pdSource.m_dPublicCustomData.Clone();
if(!bSourceNewer) vdLocal.CopyTo(m_dPublicCustomData); // Merge
// 'Clone' duplicates deep values (e.g. byte arrays)
VariantDictionary vdS = (VariantDictionary)pdSource.m_dPublicCustomData.Clone();
if (bForce || bSourceNewer)
vdS.CopyTo(m_dPublicCustomData);
else
{
m_dPublicCustomData.CopyTo(vdS);
m_dPublicCustomData = vdS;
}
}
private void MergeEntryHistory(PwEntry pe, PwEntry peSource,
@@ -1543,12 +1634,12 @@ namespace KeePassLib
/// <returns>Index of the icon.</returns>
public int GetCustomIconIndex(PwUuid pwIconId)
{
for(int i = 0; i < m_vCustomIcons.Count; ++i)
{
PwCustomIcon pwci = m_vCustomIcons[i];
if(pwci.Uuid.Equals(pwIconId))
return i;
}
for (int i = 0; i < m_vCustomIcons.Count; ++i)
{
PwCustomIcon pwci = m_vCustomIcons[i];
if (pwci.Uuid.Equals(pwIconId))
return i;
}
// Debug.Assert(false); // Do not assert
return -1;
@@ -1558,15 +1649,15 @@ namespace KeePassLib
{
if(pbPngData == null) { Debug.Assert(false); return -1; }
for(int i = 0; i < m_vCustomIcons.Count; ++i)
{
PwCustomIcon pwci = m_vCustomIcons[i];
byte[] pbEx = pwci.ImageDataPng;
if(pbEx == null) { Debug.Assert(false); continue; }
for (int i = 0; i < m_vCustomIcons.Count; ++i)
{
PwCustomIcon pwci = m_vCustomIcons[i];
byte[] pbEx = pwci.ImageDataPng;
if (pbEx == null) { Debug.Assert(false); continue; }
if(MemUtil.ArraysEqual(pbEx, pbPngData))
return i;
}
if (MemUtil.ArraysEqual(pbEx, pbPngData))
return i;
}
return -1;
}
@@ -1585,68 +1676,64 @@ namespace KeePassLib
else { Debug.Assert(false); return null; }
}
public bool DeleteCustomIcons(List<PwUuid> vUuidsToDelete)
{
Debug.Assert(vUuidsToDelete != null);
if(vUuidsToDelete == null) throw new ArgumentNullException("vUuidsToDelete");
if(vUuidsToDelete.Count <= 0) return true;
public bool DeleteCustomIcons(List<PwUuid> lUuids)
{
if (lUuids == null) { Debug.Assert(false); throw new ArgumentNullException("lUuids"); }
if (lUuids.Count == 0) return false;
GroupHandler gh = delegate(PwGroup pg)
{
PwUuid uuidThis = pg.CustomIconUuid;
if(uuidThis.Equals(PwUuid.Zero)) return true;
Dictionary<PwUuid, bool> dToDel = new Dictionary<PwUuid, bool>();
foreach (PwUuid pu in lUuids) { dToDel[pu] = true; }
foreach(PwUuid uuidDelete in vUuidsToDelete)
{
if(uuidThis.Equals(uuidDelete))
{
pg.CustomIconUuid = PwUuid.Zero;
break;
}
}
DateTime dt = DateTime.UtcNow;
for (int i = m_vCustomIcons.Count - 1; i >= 0; --i)
{
PwUuid pu = m_vCustomIcons[i].Uuid;
if (dToDel.ContainsKey(pu))
{
m_vCustomIcons[i] = null; // Removed below
m_vDeletedObjects.Add(new PwDeletedObject(pu, dt));
}
}
return true;
};
Predicate<PwCustomIcon> f = delegate (PwCustomIcon ci) { return (ci == null); };
m_vCustomIcons.RemoveAll(f);
EntryHandler eh = delegate(PwEntry pe)
{
RemoveCustomIconUuid(pe, vUuidsToDelete);
return true;
};
FixCustomIconRefs();
return true;
}
gh(m_pgRootGroup);
if(!m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh))
{
Debug.Assert(false);
return false;
}
private void FixCustomIconRefs()
{
Dictionary<PwUuid, bool> d = new Dictionary<PwUuid, bool>();
foreach (PwCustomIcon ci in m_vCustomIcons) { d[ci.Uuid] = true; }
foreach(PwUuid pwUuid in vUuidsToDelete)
{
int nIndex = GetCustomIconIndex(pwUuid);
if(nIndex >= 0) m_vCustomIcons.RemoveAt(nIndex);
}
GroupHandler gh = delegate (PwGroup pg)
{
PwUuid pu = pg.CustomIconUuid;
if (pu.Equals(PwUuid.Zero)) return true;
if (!d.ContainsKey(pu)) pg.CustomIconUuid = PwUuid.Zero;
return true;
};
return true;
}
EntryHandler eh = delegate (PwEntry pe)
{
FixCustomIconRefs(pe, d);
return true;
};
private static void RemoveCustomIconUuid(PwEntry pe, List<PwUuid> vToDelete)
{
PwUuid uuidThis = pe.CustomIconUuid;
if(uuidThis.Equals(PwUuid.Zero)) return;
gh(m_pgRootGroup);
m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
}
foreach(PwUuid uuidDelete in vToDelete)
{
if(uuidThis.Equals(uuidDelete))
{
pe.CustomIconUuid = PwUuid.Zero;
break;
}
}
private void FixCustomIconRefs(PwEntry pe, Dictionary<PwUuid, bool> d)
{
PwUuid pu = pe.CustomIconUuid;
if (pu.Equals(PwUuid.Zero)) return;
if (!d.ContainsKey(pu)) pe.CustomIconUuid = PwUuid.Zero;
foreach (PwEntry peH in pe.History) FixCustomIconRefs(peH, d);
}
foreach(PwEntry peHistory in pe.History)
RemoveCustomIconUuid(peHistory, vToDelete);
}
private int GetTotalObjectUuidCount()
{
@@ -1935,61 +2022,119 @@ namespace KeePassLib
return uDeleted;
}
public uint DeleteUnusedCustomIcons()
{
List<PwUuid> lToDelete = new List<PwUuid>();
foreach(PwCustomIcon pwci in m_vCustomIcons)
lToDelete.Add(pwci.Uuid);
Dictionary<PwUuid, bool> dToDel = new Dictionary<PwUuid, bool>();
foreach (PwCustomIcon ci in m_vCustomIcons) { dToDel[ci.Uuid] = true; }
GroupHandler gh = delegate(PwGroup pg)
GroupHandler gh = delegate (PwGroup pg)
{
PwUuid pwUuid = pg.CustomIconUuid;
if((pwUuid == null) || pwUuid.Equals(PwUuid.Zero)) return true;
for(int i = 0; i < lToDelete.Count; ++i)
{
if(lToDelete[i].Equals(pwUuid))
{
lToDelete.RemoveAt(i);
break;
}
}
PwUuid pu = pg.CustomIconUuid;
if (!pu.Equals(PwUuid.Zero)) dToDel.Remove(pu);
return true;
};
EntryHandler eh = delegate(PwEntry pe)
EntryHandler eh = delegate (PwEntry pe)
{
PwUuid pwUuid = pe.CustomIconUuid;
if((pwUuid == null) || pwUuid.Equals(PwUuid.Zero)) return true;
for(int i = 0; i < lToDelete.Count; ++i)
{
if(lToDelete[i].Equals(pwUuid))
{
lToDelete.RemoveAt(i);
break;
}
}
RemoveCustomIconsFromDict(dToDel, pe);
return true;
};
gh(m_pgRootGroup);
m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
uint uDeleted = 0;
foreach(PwUuid pwDel in lToDelete)
uint cDel = (uint)dToDel.Count;
if (cDel != 0)
{
int nIndex = GetCustomIconIndex(pwDel);
if(nIndex < 0) { Debug.Assert(false); continue; }
m_vCustomIcons.RemoveAt(nIndex);
++uDeleted;
DeleteCustomIcons(new List<PwUuid>(dToDel.Keys));
m_bUINeedsIconUpdate = true;
}
if(uDeleted > 0) m_bUINeedsIconUpdate = true;
return uDeleted;
return cDel;
}
private static void RemoveCustomIconsFromDict(Dictionary<PwUuid, bool> d,
PwEntry pe)
{
PwUuid pu = pe.CustomIconUuid;
if (!pu.Equals(PwUuid.Zero)) d.Remove(pu);
foreach (PwEntry peH in pe.History) RemoveCustomIconsFromDict(d, peH);
}
internal static void CopyCustomIcons(PwDatabase pdFrom, PwDatabase pdTo,
PwGroup pgSelect, bool bResetIfUnknown)
{
if (pgSelect == null) { Debug.Assert(false); return; }
Dictionary<PwUuid, PwCustomIcon> dFrom = new Dictionary<PwUuid, PwCustomIcon>();
if (pdFrom != null)
{
foreach (PwCustomIcon ci in pdFrom.m_vCustomIcons)
dFrom[ci.Uuid] = ci;
}
Dictionary<PwUuid, int> dTo = new Dictionary<PwUuid, int>();
if (pdTo != null)
{
for (int i = pdTo.m_vCustomIcons.Count - 1; i >= 0; --i)
dTo[pdTo.m_vCustomIcons[i].Uuid] = i;
}
Func<PwUuid, bool> fEnsureIcon = delegate (PwUuid puIcon)
{
if (puIcon.Equals(PwUuid.Zero)) return true;
if (pdTo == null) { Debug.Assert(false); return false; }
PwCustomIcon ciFrom;
if (!dFrom.TryGetValue(puIcon, out ciFrom)) { Debug.Assert(false); return false; }
int iTo;
if (dTo.TryGetValue(puIcon, out iTo))
{
PwCustomIcon ciTo = pdTo.m_vCustomIcons[iTo];
DateTime? odtFrom = ciFrom.LastModificationTime;
DateTime? odtTo = ciTo.LastModificationTime;
if (odtFrom.HasValue && odtTo.HasValue)
{
if (odtFrom.Value <= odtTo.Value) return true;
}
else if (odtTo.HasValue) return true;
else if (!odtFrom.HasValue) return true; // Both no time
pdTo.m_vCustomIcons[iTo] = ciFrom.Clone();
}
else
{
dTo[puIcon] = pdTo.m_vCustomIcons.Count;
pdTo.m_vCustomIcons.Add(ciFrom.Clone());
}
pdTo.Modified = true;
pdTo.UINeedsIconUpdate = true;
return true;
};
GroupHandler gh = delegate (PwGroup pgCur)
{
bool bTo = fEnsureIcon(pgCur.CustomIconUuid);
if (!bTo && bResetIfUnknown) pgCur.CustomIconUuid = PwUuid.Zero;
return true;
};
EntryHandler eh = delegate (PwEntry peCur)
{
bool bTo = fEnsureIcon(peCur.CustomIconUuid);
if (!bTo && bResetIfUnknown) peCur.CustomIconUuid = PwUuid.Zero;
return true;
};
gh(pgSelect);
pgSelect.TraverseTree(TraversalMethod.PreOrder, gh, eh);
}
}
}

View File

@@ -228,6 +228,18 @@ namespace KeePassLib
/// </summary>
public sealed class SearchParameters
{
private string m_strName = string.Empty;
[DefaultValue("")]
public string Name
{
get { return m_strName; }
set
{
if (value == null) throw new ArgumentNullException("value");
m_strName = value;
}
}
private string m_strText = string.Empty;
[DefaultValue("")]
public string SearchString
@@ -235,17 +247,25 @@ namespace KeePassLib
get { return m_strText; }
set
{
if(value == null) throw new ArgumentNullException("value");
if (value == null) throw new ArgumentNullException("value");
m_strText = value;
}
}
private bool m_bRegex = false;
private PwSearchMode m_sm = PwSearchMode.Simple;
public PwSearchMode SearchMode
{
get { return m_sm; }
set { m_sm = value; }
}
[DefaultValue(false)]
[Obsolete]
[XmlIgnore]
public bool RegularExpression
{
get { return m_bRegex; }
set { m_bRegex = value; }
get { return (m_sm == PwSearchMode.Regular); }
set { m_sm = (value ? PwSearchMode.Regular : PwSearchMode.Simple); }
}
private bool m_bSearchInTitles = true;
@@ -296,6 +316,22 @@ namespace KeePassLib
set { m_bSearchInOther = value; }
}
private bool m_bSearchInStringNames = false;
[DefaultValue(false)]
public bool SearchInStringNames
{
get { return m_bSearchInStringNames; }
set { m_bSearchInStringNames = value; }
}
private bool m_bSearchInTags = true;
[DefaultValue(true)]
public bool SearchInTags
{
get { return m_bSearchInTags; }
set { m_bSearchInTags = value; }
}
private bool m_bSearchInUuids = false;
[DefaultValue(false)]
public bool SearchInUuids
@@ -304,6 +340,14 @@ namespace KeePassLib
set { m_bSearchInUuids = value; }
}
private bool m_bSearchInGroupPaths = false;
[DefaultValue(false)]
public bool SearchInGroupPaths
{
get { return m_bSearchInGroupPaths; }
set { m_bSearchInGroupPaths = value; }
}
private bool m_bSearchInGroupNames = false;
[DefaultValue(false)]
public bool SearchInGroupNames
@@ -312,12 +356,12 @@ namespace KeePassLib
set { m_bSearchInGroupNames = value; }
}
private bool m_bSearchInTags = true;
[DefaultValue(true)]
public bool SearchInTags
private bool m_bSearchInHistory = false;
[DefaultValue(false)]
public bool SearchInHistory
{
get { return m_bSearchInTags; }
set { m_bSearchInTags = value; }
get { return m_bSearchInHistory; }
set { m_bSearchInHistory = value; }
}
#if KeePassUAP
@@ -369,7 +413,7 @@ namespace KeePassLib
get { return m_strDataTrf; }
set
{
if(value == null) throw new ArgumentNullException("value");
if (value == null) throw new ArgumentNullException("value");
m_strDataTrf = value;
}
}
@@ -381,20 +425,24 @@ namespace KeePassLib
{
SearchParameters sp = new SearchParameters();
// sp.m_strText = string.Empty;
// sp.m_bRegex = false;
Debug.Assert(sp.m_strName.Length == 0);
Debug.Assert(sp.m_strText.Length == 0);
Debug.Assert(sp.m_sm == PwSearchMode.Simple);
sp.m_bSearchInTitles = false;
sp.m_bSearchInUserNames = false;
// sp.m_bSearchInPasswords = false;
Debug.Assert(!sp.m_bSearchInPasswords);
sp.m_bSearchInUrls = false;
sp.m_bSearchInNotes = false;
sp.m_bSearchInOther = false;
// sp.m_bSearchInUuids = false;
// sp.SearchInGroupNames = false;
Debug.Assert(!sp.m_bSearchInStringNames);
sp.m_bSearchInTags = false;
// sp.m_scType = StringComparison.InvariantCultureIgnoreCase;
// sp.m_bExcludeExpired = false;
// m_bRespectEntrySearchingDisabled = true;
Debug.Assert(!sp.m_bSearchInUuids);
Debug.Assert(!sp.m_bSearchInGroupPaths);
Debug.Assert(!sp.m_bSearchInGroupNames);
Debug.Assert(!sp.m_bSearchInHistory);
// Debug.Assert(sp.m_scType == StringComparison.InvariantCultureIgnoreCase);
Debug.Assert(!sp.m_bExcludeExpired);
Debug.Assert(sp.m_bRespectEntrySearchingDisabled);
return sp;
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -42,14 +42,15 @@ namespace KeePassLib
private PwUuid m_uuid = PwUuid.Zero;
private PwGroup m_pParentGroup = null;
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
private PwUuid m_puPrevParentGroup = PwUuid.Zero;
private ProtectedStringDictionary m_listStrings = new ProtectedStringDictionary();
private ProtectedBinaryDictionary m_listBinaries = new ProtectedBinaryDictionary();
private AutoTypeConfig m_listAutoType = new AutoTypeConfig();
private PwObjectList<PwEntry> m_listHistory = new PwObjectList<PwEntry>();
private ProtectedStringDictionary m_dStrings = new ProtectedStringDictionary();
private ProtectedBinaryDictionary m_dBinaries = new ProtectedBinaryDictionary();
private AutoTypeConfig m_cfgAutoType = new AutoTypeConfig();
private PwObjectList<PwEntry> m_lHistory = new PwObjectList<PwEntry>();
private PwIcon m_pwIcon = PwIcon.Key;
private PwUuid m_pwCustomIconID = PwUuid.Zero;
private PwUuid m_puCustomIcon = PwUuid.Zero;
private Color m_clrForeground = Color.Empty;
private Color m_clrBackground = Color.Empty;
@@ -62,20 +63,21 @@ namespace KeePassLib
private ulong m_uUsageCount = 0;
private string m_strOverrideUrl = string.Empty;
private bool m_bQualityCheck = true;
private List<string> m_vTags = new List<string>();
private List<string> m_lTags = new List<string>();
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
/// <summary>
/// UUID of this entry.
/// </summary>
/// </summary>
public PwUuid Uuid
{
get { return m_uuid; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_uuid = value;
}
}
@@ -87,7 +89,7 @@ namespace KeePassLib
{
get { return m_pParentGroup; }
/// Plugins: use <c>PwGroup.AddEntry</c> instead.
// Plugins: use <c>PwGroup.AddEntry</c> instead.
internal set { m_pParentGroup = value; }
}
@@ -100,17 +102,26 @@ namespace KeePassLib
set { m_tParentGroupLastMod = value; }
}
public PwUuid PreviousParentGroup
{
get { return m_puPrevParentGroup; }
set
{
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_puPrevParentGroup = value;
}
}
/// <summary>
/// Get or set all entry strings.
/// </summary>
public ProtectedStringDictionary Strings
{
get { return m_listStrings; }
get { return m_dStrings; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listStrings = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_dStrings = value;
}
}
@@ -119,11 +130,11 @@ namespace KeePassLib
/// </summary>
public ProtectedBinaryDictionary Binaries
{
get { return m_listBinaries; }
get { return m_dBinaries; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listBinaries = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_dBinaries = value;
}
}
@@ -132,11 +143,11 @@ namespace KeePassLib
/// </summary>
public AutoTypeConfig AutoType
{
get { return m_listAutoType; }
get { return m_cfgAutoType; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listAutoType = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_cfgAutoType = value;
}
}
@@ -145,11 +156,11 @@ namespace KeePassLib
/// </summary>
public PwObjectList<PwEntry> History
{
get { return m_listHistory; }
get { return m_lHistory; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_listHistory = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_lHistory = value;
}
}
@@ -169,11 +180,11 @@ namespace KeePassLib
/// </summary>
public PwUuid CustomIconUuid
{
get { return m_pwCustomIconID; }
get { return m_puCustomIcon; }
set
{
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_pwCustomIconID = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_puCustomIcon = value;
}
}
@@ -252,28 +263,34 @@ namespace KeePassLib
}
/// <summary>
/// Entry-specific override URL. If this string is non-empty,
/// Entry-specific override URL.
/// </summary>
public string OverrideUrl
{
get { return m_strOverrideUrl; }
set
{
if (value == null) throw new ArgumentNullException("value");
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_strOverrideUrl = value;
}
}
public bool QualityCheck
{
get { return m_bQualityCheck; }
set { m_bQualityCheck = value; }
}
/// <summary>
/// List of tags associated with this entry.
/// </summary>
public List<string> Tags
{
get { return m_vTags; }
get { StrUtil.NormalizeTags(m_lTags); return m_lTags; }
set
{
if (value == null) throw new ArgumentNullException("value");
m_vTags = value;
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
m_lTags = value;
}
}
@@ -349,19 +366,19 @@ namespace KeePassLib
}
#if DEBUG
/// <summary>
// For display in debugger
public override string ToString()
{
return (@"PwEntry '" + m_listStrings.ReadSafe(PwDefs.TitleField) + @"'");
return ("PwEntry '" + m_dStrings.ReadSafe(PwDefs.TitleField) + "'");
}
#endif
/// <summary>
/// Clone the current entry. The returned entry is an exact value copy
/// of the current entry (including UUID and parent group reference).
/// All mutable members are cloned.
/// </summary>
/// <returns>Exact value clone. All references to mutable values changed.</returns>
/// </summary>
/// <returns>Exact value clone. All references to mutable values changed.</returns>
public PwEntry CloneDeep()
{
PwEntry peNew = new PwEntry(false, false);
@@ -369,14 +386,15 @@ namespace KeePassLib
peNew.m_uuid = m_uuid; // PwUuid is immutable
peNew.m_pParentGroup = m_pParentGroup;
peNew.m_tParentGroupLastMod = m_tParentGroupLastMod;
peNew.m_puPrevParentGroup = m_puPrevParentGroup;
peNew.m_listStrings = m_listStrings.CloneDeep();
peNew.m_listBinaries = m_listBinaries.CloneDeep();
peNew.m_listAutoType = m_listAutoType.CloneDeep();
peNew.m_listHistory = m_listHistory.CloneDeep();
peNew.m_dStrings = m_dStrings.CloneDeep();
peNew.m_dBinaries = m_dBinaries.CloneDeep();
peNew.m_cfgAutoType = m_cfgAutoType.CloneDeep();
peNew.m_lHistory = m_lHistory.CloneDeep();
peNew.m_pwIcon = m_pwIcon;
peNew.m_pwCustomIconID = m_pwCustomIconID;
peNew.m_puCustomIcon = m_puCustomIcon;
peNew.m_clrForeground = m_clrForeground;
peNew.m_clrBackground = m_clrBackground;
@@ -389,8 +407,9 @@ namespace KeePassLib
peNew.m_uUsageCount = m_uUsageCount;
peNew.m_strOverrideUrl = m_strOverrideUrl;
peNew.m_bQualityCheck = m_bQualityCheck;
peNew.m_vTags = new List<string>(m_vTags);
peNew.m_lTags.AddRange(m_lTags);
peNew.m_dCustomData = m_dCustomData.CloneDeep();
@@ -457,27 +476,29 @@ namespace KeePassLib
if (m_pParentGroup != pe.m_pParentGroup) return false;
if (!bIgnoreLastMod && (m_tParentGroupLastMod != pe.m_tParentGroupLastMod))
return false;
if (!m_puPrevParentGroup.Equals(pe.m_puPrevParentGroup))
return false;
}
if (!m_listStrings.EqualsDictionary(pe.m_listStrings, pwOpt, mpCmpStr))
if (!m_dStrings.EqualsDictionary(pe.m_dStrings, pwOpt, mpCmpStr))
return false;
if (!m_listBinaries.EqualsDictionary(pe.m_listBinaries)) return false;
if (!m_dBinaries.EqualsDictionary(pe.m_dBinaries)) return false;
if (!m_listAutoType.Equals(pe.m_listAutoType)) return false;
if (!m_cfgAutoType.Equals(pe.m_cfgAutoType)) return false;
if ((pwOpt & PwCompareOptions.IgnoreHistory) == PwCompareOptions.None)
{
bool bIgnoreLastBackup = ((pwOpt & PwCompareOptions.IgnoreLastBackup) !=
PwCompareOptions.None);
if (!bIgnoreLastBackup && (m_listHistory.UCount != pe.m_listHistory.UCount))
if (!bIgnoreLastBackup && (m_lHistory.UCount != pe.m_lHistory.UCount))
return false;
if (bIgnoreLastBackup && (m_listHistory.UCount == 0))
if (bIgnoreLastBackup && (m_lHistory.UCount == 0))
{
Debug.Assert(false);
return false;
}
if (bIgnoreLastBackup && ((m_listHistory.UCount - 1) != pe.m_listHistory.UCount))
if (bIgnoreLastBackup && ((m_lHistory.UCount - 1) != pe.m_lHistory.UCount))
return false;
PwCompareOptions cmpSub = PwCompareOptions.IgnoreParentGroup;
@@ -485,16 +506,16 @@ namespace KeePassLib
if (bIgnoreLastMod) cmpSub |= PwCompareOptions.IgnoreLastMod;
if (bIgnoreLastAccess) cmpSub |= PwCompareOptions.IgnoreLastAccess;
for (uint uHist = 0; uHist < pe.m_listHistory.UCount; ++uHist)
for (uint uHist = 0; uHist < pe.m_lHistory.UCount; ++uHist)
{
if (!m_listHistory.GetAt(uHist).EqualsEntry(pe.m_listHistory.GetAt(
if (!m_lHistory.GetAt(uHist).EqualsEntry(pe.m_lHistory.GetAt(
uHist), cmpSub, MemProtCmpMode.None))
return false;
}
}
if (m_pwIcon != pe.m_pwIcon) return false;
if (!m_pwCustomIconID.Equals(pe.m_pwCustomIconID)) return false;
if (!m_puCustomIcon.Equals(pe.m_puCustomIcon)) return false;
if (m_clrForeground != pe.m_clrForeground) return false;
if (m_clrBackground != pe.m_clrBackground) return false;
@@ -507,12 +528,10 @@ namespace KeePassLib
if (!bIgnoreLastAccess && (m_uUsageCount != pe.m_uUsageCount)) return false;
if (m_strOverrideUrl != pe.m_strOverrideUrl) return false;
if (m_bQualityCheck != pe.m_bQualityCheck) return false;
if (m_vTags.Count != pe.m_vTags.Count) return false;
for (int iTag = 0; iTag < m_vTags.Count; ++iTag)
{
if (m_vTags[iTag] != pe.m_vTags[iTag]) return false;
}
// The Tags property normalizes
if (!MemUtil.ListsEqual<string>(this.Tags, pe.Tags)) return false;
if (!m_dCustomData.Equals(pe.m_dCustomData)) return false;
@@ -543,16 +562,19 @@ namespace KeePassLib
m_uuid = peTemplate.m_uuid;
if (bAssignLocationChanged)
{
m_tParentGroupLastMod = peTemplate.m_tParentGroupLastMod;
m_puPrevParentGroup = peTemplate.m_puPrevParentGroup;
}
m_listStrings = peTemplate.m_listStrings.CloneDeep();
m_listBinaries = peTemplate.m_listBinaries.CloneDeep();
m_listAutoType = peTemplate.m_listAutoType.CloneDeep();
m_dStrings = peTemplate.m_dStrings.CloneDeep();
m_dBinaries = peTemplate.m_dBinaries.CloneDeep();
m_cfgAutoType = peTemplate.m_cfgAutoType.CloneDeep();
if (bIncludeHistory)
m_listHistory = peTemplate.m_listHistory.CloneDeep();
m_lHistory = peTemplate.m_lHistory.CloneDeep();
m_pwIcon = peTemplate.m_pwIcon;
m_pwCustomIconID = peTemplate.m_pwCustomIconID; // Immutable
m_puCustomIcon = peTemplate.m_puCustomIcon; // Immutable
m_clrForeground = peTemplate.m_clrForeground;
m_clrBackground = peTemplate.m_clrBackground;
@@ -565,8 +587,9 @@ namespace KeePassLib
m_uUsageCount = peTemplate.m_uUsageCount;
m_strOverrideUrl = peTemplate.m_strOverrideUrl;
m_bQualityCheck = peTemplate.m_bQualityCheck;
m_vTags = new List<string>(peTemplate.m_vTags);
m_lTags = new List<string>(peTemplate.m_lTags);
m_dCustomData = peTemplate.m_dCustomData.CloneDeep();
}
@@ -629,9 +652,9 @@ namespace KeePassLib
public void CreateBackup(PwDatabase pwHistMntcSettings)
{
PwEntry peCopy = CloneDeep();
peCopy.History = new PwObjectList<PwEntry>(); // Remove history
peCopy.m_lHistory.Clear();
m_listHistory.Add(peCopy); // Must be added at end, see EqualsEntry
m_lHistory.Add(peCopy); // Must be added at end, see EqualsEntry
if (pwHistMntcSettings != null) MaintainBackups(pwHistMntcSettings);
}
@@ -658,12 +681,14 @@ namespace KeePassLib
/// This parameter may be <c>null</c> (no maintenance then).</param>
public void RestoreFromBackup(uint uBackupIndex, PwDatabase pwHistMntcSettings)
{
Debug.Assert(uBackupIndex < m_listHistory.UCount);
if (uBackupIndex >= m_listHistory.UCount)
if (uBackupIndex >= m_lHistory.UCount)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("uBackupIndex");
}
PwEntry pe = m_listHistory.GetAt(uBackupIndex);
Debug.Assert(pe != null); if (pe == null) throw new InvalidOperationException();
PwEntry pe = m_lHistory.GetAt(uBackupIndex);
if (pe == null) { Debug.Assert(false); throw new InvalidOperationException(); }
CreateBackup(pwHistMntcSettings); // Backup current data before restoring
AssignProperties(pe, false, false, false);
@@ -679,7 +704,7 @@ namespace KeePassLib
if (bIgnoreLastMod) cmpOpt |= PwCompareOptions.IgnoreLastMod;
if (bIgnoreLastAccess) cmpOpt |= PwCompareOptions.IgnoreLastAccess;
foreach (PwEntry pe in m_listHistory)
foreach (PwEntry pe in m_lHistory)
{
if (pe.EqualsEntry(peData, cmpOpt, MemProtCmpMode.None)) return true;
}
@@ -688,21 +713,28 @@ namespace KeePassLib
}
/// <summary>
/// Delete old history items if there are too many or the history
/// size is too large.
/// <returns>If one or more history items have been deleted, <c>true</c>
/// is returned. Otherwise <c>false</c>.</returns>
/// Delete old history entries if there are too many or the
/// history size is too large.
/// <returns>If one or more history entries have been deleted,
/// <c>true</c> is returned. Otherwise <c>false</c>.</returns>
/// </summary>
public bool MaintainBackups(PwDatabase pwSettings)
{
if (pwSettings == null) { Debug.Assert(false); return false; }
// Fix UUIDs of history entries; should not be necessary
PwUuid pu = m_uuid;
foreach (PwEntry pe in m_lHistory)
{
if (!pe.Uuid.Equals(pu)) { Debug.Assert(false); pe.Uuid = pu; }
}
bool bDeleted = false;
int nMaxItems = pwSettings.HistoryMaxItems;
if (nMaxItems >= 0)
{
while (m_listHistory.UCount > (uint)nMaxItems)
while (m_lHistory.UCount > (uint)nMaxItems)
{
RemoveOldestBackup();
bDeleted = true;
@@ -715,7 +747,7 @@ namespace KeePassLib
while (true)
{
ulong uHistSize = 0;
foreach (PwEntry pe in m_listHistory) { uHistSize += pe.GetSize(); }
foreach (PwEntry pe in m_lHistory) { uHistSize += pe.GetSize(); }
if (uHistSize > (ulong)lMaxSize)
{
@@ -734,9 +766,9 @@ namespace KeePassLib
DateTime dtMin = TimeUtil.SafeMaxValueUtc;
uint idxRemove = uint.MaxValue;
for (uint u = 0; u < m_listHistory.UCount; ++u)
for (uint u = 0; u < m_lHistory.UCount; ++u)
{
PwEntry pe = m_listHistory.GetAt(u);
PwEntry pe = m_lHistory.GetAt(u);
if (TimeUtil.Compare(pe.LastModificationTime, dtMin, true) < 0)
{
idxRemove = u;
@@ -744,12 +776,12 @@ namespace KeePassLib
}
}
if (idxRemove != uint.MaxValue) m_listHistory.RemoveAt(idxRemove);
if (idxRemove != uint.MaxValue) m_lHistory.RemoveAt(idxRemove);
}
public bool GetAutoTypeEnabled()
{
if (!m_listAutoType.Enabled) return false;
if (!m_cfgAutoType.Enabled) return false;
if (m_pParentGroup != null)
return m_pParentGroup.GetAutoTypeEnabledInherited();
@@ -759,7 +791,7 @@ namespace KeePassLib
public string GetAutoTypeSequence()
{
string strSeq = m_listAutoType.DefaultSequence;
string strSeq = m_cfgAutoType.DefaultSequence;
PwGroup pg = m_pParentGroup;
while (pg != null)
@@ -785,69 +817,67 @@ namespace KeePassLib
}
/// <summary>
/// Approximate the total size of this entry in bytes (including
/// strings, binaries and history entries).
/// Approximate the total size (in process memory) of this entry
/// in bytes (including strings, binaries and history entries).
/// </summary>
/// <returns>Size in bytes.</returns>
public ulong GetSize()
{
ulong uSize = 128; // Approx fixed length data
// This method assumes 64-bit pointers/references and Unicode
// strings (i.e. 2 bytes per character)
foreach (KeyValuePair<string, ProtectedString> kvpStr in m_listStrings)
ulong cb = 276; // Number of bytes; approx. fixed length data
ulong cc = 0; // Number of characters
cb += (ulong)m_dStrings.UCount * 40;
foreach (KeyValuePair<string, ProtectedString> kvpStr in m_dStrings)
cc += (ulong)kvpStr.Key.Length + (ulong)kvpStr.Value.Length;
cb += (ulong)m_dBinaries.UCount * 65;
foreach (KeyValuePair<string, ProtectedBinary> kvpBin in m_dBinaries)
{
uSize += (ulong)kvpStr.Key.Length;
uSize += (ulong)kvpStr.Value.Length;
cc += (ulong)kvpBin.Key.Length;
cb += (ulong)kvpBin.Value.Length;
}
foreach (KeyValuePair<string, ProtectedBinary> kvpBin in m_listBinaries)
{
uSize += (ulong)kvpBin.Key.Length;
uSize += kvpBin.Value.Length;
}
cc += (ulong)m_cfgAutoType.DefaultSequence.Length;
cb += (ulong)m_cfgAutoType.AssociationsCount * 24;
foreach (AutoTypeAssociation a in m_cfgAutoType.Associations)
cc += (ulong)a.WindowName.Length + (ulong)a.Sequence.Length;
uSize += (ulong)m_listAutoType.DefaultSequence.Length;
foreach (AutoTypeAssociation a in m_listAutoType.Associations)
{
uSize += (ulong)a.WindowName.Length;
uSize += (ulong)a.Sequence.Length;
}
cb += (ulong)m_lHistory.UCount * 8;
foreach (PwEntry peHistory in m_lHistory)
cb += peHistory.GetSize();
foreach (PwEntry peHistory in m_listHistory)
uSize += peHistory.GetSize();
cc += (ulong)m_strOverrideUrl.Length;
uSize += (ulong)m_strOverrideUrl.Length;
foreach (string strTag in m_vTags)
uSize += (ulong)strTag.Length;
cb += (ulong)m_lTags.Count * 8;
foreach (string strTag in m_lTags)
cc += (ulong)strTag.Length;
cb += (ulong)m_dCustomData.Count * 16;
foreach (KeyValuePair<string, string> kvp in m_dCustomData)
uSize += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
cc += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
return uSize;
return (cb + (cc << 1));
}
public bool HasTag(string strTag)
{
if (string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
for (int i = 0; i < m_vTags.Count; ++i)
{
if (m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp)) return true;
}
return false;
// this.Tags normalizes
return this.Tags.Contains(StrUtil.NormalizeTag(strTag));
}
public bool AddTag(string strTag)
{
if (string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
for (int i = 0; i < m_vTags.Count; ++i)
{
if (m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp)) return false;
}
strTag = StrUtil.NormalizeTag(strTag);
if (this.Tags.Contains(strTag)) return false; // this.Tags normalizes
m_vTags.Add(strTag);
m_lTags.Add(strTag);
return true;
}
@@ -855,16 +885,17 @@ namespace KeePassLib
{
if (string.IsNullOrEmpty(strTag)) { Debug.Assert(false); return false; }
for (int i = 0; i < m_vTags.Count; ++i)
{
if (m_vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp))
{
m_vTags.RemoveAt(i);
return true;
}
}
// this.Tags normalizes
return this.Tags.Remove(StrUtil.NormalizeTag(strTag));
}
return false;
internal List<string> GetTagsInherited()
{
List<string> l = ((m_pParentGroup != null) ?
m_pParentGroup.GetTagsInherited(false) : new List<string>());
l.AddRange(this.Tags);
StrUtil.NormalizeTags(l);
return l;
}
public bool IsContainedIn(PwGroup pgContainer)
@@ -886,10 +917,8 @@ namespace KeePassLib
if (bAlsoChangeHistoryUuids)
{
foreach (PwEntry peHist in m_listHistory)
{
foreach (PwEntry peHist in m_lHistory)
peHist.Uuid = pwNewUuid;
}
}
}

View File

@@ -316,4 +316,13 @@ namespace KeePassLib
Cinnamon,
Pantheon
}
public enum PwSearchMode
{
None = 0,
Simple,
Regular,
XPath
}
}

View File

@@ -0,0 +1,372 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.XPath;
using KeePassLib.Collections;
using KeePassLib.Delegates;
using KeePassLib.Interfaces;
using KeePassLib.Security;
using KeePassLib.Serialization;
using KeePassLib.Utility;
namespace KeePassLib
{
public sealed partial class PwGroup
{
private const int SearchContextStringMaxLength = 50; // Note, doesn't include elipsis, if added
public const string SearchContextUuid = "Uuid";
public const string SearchContextParentGroup = "Parent Group";
public const string SearchContextTags = "Tags";
/// <summary>
/// Search this group and all subgroups for entries.
/// </summary>
/// <param name="sp">Specifies the search method.</param>
/// <param name="listStorage">Entry list in which the search results will
/// be stored.</param>
public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> listStorage)
{
SearchEntries(sp, listStorage, null);
}
/// <summary>
/// Search this group and all subgroups for entries.
/// </summary>
/// <param name="sp">Specifies the search method.</param>
/// <param name="listStorage">Entry list in which the search results will
/// be stored.</param>
/// <param name="slStatus">Optional status reporting object.</param>
public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> listStorage,
IStatusLogger slStatus)
{
SearchEntries(sp, listStorage, null, slStatus);
}
/// <summary>
/// Search this group and all subgroups for entries.
/// </summary>
/// <param name="sp">Specifies the search method.</param>
/// <param name="listStorage">Entry list in which the search results will
/// be stored.</param>
/// <param name="resultContexts">Dictionary that will be populated with text fragments indicating the context of why each entry (keyed by Uuid) was returned</param>
public void SearchEntries(SearchParameters sp, PwObjectList<PwEntry> listStorage,
IDictionary<PwUuid, KeyValuePair<string, string>> resultContexts,
IStatusLogger slStatus)
{
if (sp == null)
{
Debug.Assert(false);
return;
}
if (listStorage == null)
{
Debug.Assert(false);
return;
}
ulong uCurEntries = 0, uTotalEntries = 0;
List<string> lTerms = StrUtil.SplitSearchTerms(sp.SearchString);
if ((lTerms.Count <= 1) || sp.RegularExpression)
{
if (slStatus != null) uTotalEntries = GetEntriesCount(true);
SearchEntriesSingle(sp, listStorage, resultContexts, slStatus, ref uCurEntries,
uTotalEntries);
return;
}
// Search longer strings first (for improved performance)
lTerms.Sort(StrUtil.CompareLengthGt);
string strFullSearch = sp.SearchString; // Backup
PwGroup pg = this;
for (int iTerm = 0; iTerm < lTerms.Count; ++iTerm)
{
// Update counters for a better state guess
if (slStatus != null)
{
ulong uRemRounds = (ulong) (lTerms.Count - iTerm);
uTotalEntries = uCurEntries + (uRemRounds *
pg.GetEntriesCount(true));
}
PwGroup pgNew = new PwGroup();
sp.SearchString = lTerms[iTerm];
bool bNegate = false;
if (sp.SearchString.StartsWith("-"))
{
sp.SearchString = sp.SearchString.Substring(1);
bNegate = (sp.SearchString.Length > 0);
}
if (!pg.SearchEntriesSingle(sp, pgNew.Entries, resultContexts, slStatus,
ref uCurEntries, uTotalEntries))
{
pg = null;
break;
}
if (bNegate)
{
PwObjectList<PwEntry> lCand = pg.GetEntries(true);
pg = new PwGroup();
foreach (PwEntry peCand in lCand)
{
if (pgNew.Entries.IndexOf(peCand) < 0) pg.Entries.Add(peCand);
}
}
else pg = pgNew;
}
if (pg != null) listStorage.Add(pg.Entries);
sp.SearchString = strFullSearch; // Restore
}
private bool SearchEntriesSingle(SearchParameters spIn,
PwObjectList<PwEntry> listStorage, IDictionary<PwUuid, KeyValuePair<string, string>> resultContexts,
IStatusLogger slStatus,
ref ulong uCurEntries, ulong uTotalEntries)
{
SearchParameters sp = spIn.Clone();
if (sp.SearchString == null)
{
Debug.Assert(false);
return true;
}
sp.SearchString = sp.SearchString.Trim();
bool bTitle = sp.SearchInTitles;
bool bUserName = sp.SearchInUserNames;
bool bPassword = sp.SearchInPasswords;
bool bUrl = sp.SearchInUrls;
bool bNotes = sp.SearchInNotes;
bool bOther = sp.SearchInOther;
bool bUuids = sp.SearchInUuids;
bool bGroupName = sp.SearchInGroupNames;
bool bTags = sp.SearchInTags;
bool bExcludeExpired = sp.ExcludeExpired;
bool bRespectEntrySearchingDisabled = sp.RespectEntrySearchingDisabled;
DateTime dtNow = DateTime.Now;
Regex rx = null;
if (sp.RegularExpression)
{
RegexOptions ro = RegexOptions.None; // RegexOptions.Compiled
if ((sp.ComparisonMode == StringComparison.CurrentCultureIgnoreCase) ||
#if !KeePassUAP
(sp.ComparisonMode == StringComparison.InvariantCultureIgnoreCase) ||
#endif
(sp.ComparisonMode == StringComparison.OrdinalIgnoreCase))
{
ro |= RegexOptions.IgnoreCase;
}
rx = new Regex(sp.SearchString, ro);
}
ulong uLocalCurEntries = uCurEntries;
EntryHandler eh = null;
if (sp.SearchString.Length <= 0) // Report all
{
eh = delegate(PwEntry pe)
{
if (slStatus != null)
{
if (!slStatus.SetProgress((uint) ((uLocalCurEntries *
100UL) / uTotalEntries))) return false;
++uLocalCurEntries;
}
if (bRespectEntrySearchingDisabled && !pe.GetSearchingEnabled())
return true; // Skip
if (bExcludeExpired && pe.Expires && (dtNow > pe.ExpiryTime))
return true; // Skip
listStorage.Add(pe);
return true;
};
}
else
{
eh = delegate(PwEntry pe)
{
if (slStatus != null)
{
if (!slStatus.SetProgress((uint) ((uLocalCurEntries *
100UL) / uTotalEntries))) return false;
++uLocalCurEntries;
}
if (bRespectEntrySearchingDisabled && !pe.GetSearchingEnabled())
return true; // Skip
if (bExcludeExpired && pe.Expires && (dtNow > pe.ExpiryTime))
return true; // Skip
uint uInitialResults = listStorage.UCount;
foreach (KeyValuePair<string, ProtectedString> kvp in pe.Strings)
{
string strKey = kvp.Key;
if (strKey == PwDefs.TitleField)
{
if (bTitle)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
}
else if (strKey == PwDefs.UserNameField)
{
if (bUserName)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
}
else if (strKey == PwDefs.PasswordField)
{
if (bPassword)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
}
else if (strKey == PwDefs.UrlField)
{
if (bUrl)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
}
else if (strKey == PwDefs.NotesField)
{
if (bNotes)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
}
else if (bOther)
SearchEvalAdd(sp, kvp.Value.ReadString(),
rx, pe, listStorage, resultContexts, strKey);
// An entry can match only once => break if we have added it
if (listStorage.UCount > uInitialResults) break;
}
if (bUuids && (listStorage.UCount == uInitialResults))
SearchEvalAdd(sp, pe.Uuid.ToHexString(), rx, pe, listStorage, resultContexts,
SearchContextTags);
if (bGroupName && (listStorage.UCount == uInitialResults) &&
(pe.ParentGroup != null))
SearchEvalAdd(sp, pe.ParentGroup.Name, rx, pe, listStorage, resultContexts,
SearchContextParentGroup);
if (bTags)
{
foreach (string strTag in pe.Tags)
{
if (listStorage.UCount != uInitialResults) break; // Match
SearchEvalAdd(sp, strTag, rx, pe, listStorage, resultContexts, SearchContextTags);
}
}
return true;
};
}
if (!PreOrderTraverseTree(null, eh)) return false;
uCurEntries = uLocalCurEntries;
return true;
}
private static void SearchEvalAdd(SearchParameters sp, string strDataField,
Regex rx, PwEntry pe, PwObjectList<PwEntry> lResults,
IDictionary<PwUuid, KeyValuePair<string, string>> resultContexts, string contextFieldName)
{
bool bMatch = false;
int matchPos;
if (rx == null)
{
matchPos = strDataField.IndexOf(sp.SearchString, sp.ComparisonMode);
bMatch = matchPos >= 0;
}
else
{
var match = rx.Match(strDataField);
bMatch = match.Success;
matchPos = match.Index;
}
if (!bMatch && (sp.DataTransformationFn != null))
{
string strCmp = sp.DataTransformationFn(strDataField, pe);
if (!object.ReferenceEquals(strCmp, strDataField))
{
if (rx == null)
{
matchPos = strCmp.IndexOf(sp.SearchString, sp.ComparisonMode);
bMatch = matchPos >= 0;
}
else
{
var match = rx.Match(strCmp);
bMatch = match.Success;
matchPos = match.Index;
}
}
}
if (bMatch)
{
lResults.Add(pe);
if (resultContexts != null)
{
// Trim the value if necessary
var contextString = strDataField;
if (contextString.Length > SearchContextStringMaxLength)
{
// Start 10% before actual data, and don't run over
var startPos = Math.Max(0,
Math.Min(matchPos - (SearchContextStringMaxLength / 10),
contextString.Length - SearchContextStringMaxLength));
contextString = "… " + contextString.Substring(startPos, SearchContextStringMaxLength) +
((startPos + SearchContextStringMaxLength < contextString.Length)
? " …"
: null);
}
resultContexts[pe.Uuid] = new KeyValuePair<string, string>(contextFieldName, contextString);
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ namespace KeePassLib.Resources
{
string strTemp;
if(dictNew.TryGetValue(strName, out strTemp))
if (dictNew.TryGetValue(strName, out strTemp))
return strTemp;
return strDefault;
@@ -24,8 +24,11 @@ namespace KeePassLib.Resources
public static void SetTranslatedStrings(Dictionary<string, string> dictNew)
{
if(dictNew == null) throw new ArgumentNullException("dictNew");
if (dictNew == null) throw new ArgumentNullException("dictNew");
m_strAlgorithmUnknown = TryGetEx(dictNew, "AlgorithmUnknown", m_strAlgorithmUnknown);
m_strCharSetInvalid = TryGetEx(dictNew, "CharSetInvalid", m_strCharSetInvalid);
m_strCharSetTooFewChars = TryGetEx(dictNew, "CharSetTooFewChars", m_strCharSetTooFewChars);
m_strCryptoStreamFailed = TryGetEx(dictNew, "CryptoStreamFailed", m_strCryptoStreamFailed);
m_strEncDataTooLarge = TryGetEx(dictNew, "EncDataTooLarge", m_strEncDataTooLarge);
m_strErrorInClipboard = TryGetEx(dictNew, "ErrorInClipboard", m_strErrorInClipboard);
@@ -41,7 +44,7 @@ namespace KeePassLib.Resources
m_strFileNewVerOrPlgReq = TryGetEx(dictNew, "FileNewVerOrPlgReq", m_strFileNewVerOrPlgReq);
m_strFileNewVerReq = TryGetEx(dictNew, "FileNewVerReq", m_strFileNewVerReq);
m_strFileSaveCorruptionWarning = TryGetEx(dictNew, "FileSaveCorruptionWarning", m_strFileSaveCorruptionWarning);
m_strFileSaveFailed = TryGetEx(dictNew, "FileSaveFailed", m_strFileSaveFailed);
m_strFileSaveFailed2 = TryGetEx(dictNew, "FileSaveFailed2", m_strFileSaveFailed2);
m_strFileSigInvalid = TryGetEx(dictNew, "FileSigInvalid", m_strFileSigInvalid);
m_strFileUnknownCipher = TryGetEx(dictNew, "FileUnknownCipher", m_strFileUnknownCipher);
m_strFileUnknownCompression = TryGetEx(dictNew, "FileUnknownCompression", m_strFileUnknownCompression);
@@ -55,12 +58,18 @@ namespace KeePassLib.Resources
m_strKeePass1xHint = TryGetEx(dictNew, "KeePass1xHint", m_strKeePass1xHint);
m_strKeyBits = TryGetEx(dictNew, "KeyBits", m_strKeyBits);
m_strKeyFileDbSel = TryGetEx(dictNew, "KeyFileDbSel", m_strKeyFileDbSel);
m_strKeyHashMismatch = TryGetEx(dictNew, "KeyHashMismatch", m_strKeyHashMismatch);
m_strMasterSeedLengthInvalid = TryGetEx(dictNew, "MasterSeedLengthInvalid", m_strMasterSeedLengthInvalid);
m_strOldFormat = TryGetEx(dictNew, "OldFormat", m_strOldFormat);
m_strPassive = TryGetEx(dictNew, "Passive", m_strPassive);
m_strPathBackslash = TryGetEx(dictNew, "PathBackslash", m_strPathBackslash);
m_strPatternInvalid = TryGetEx(dictNew, "PatternInvalid", m_strPatternInvalid);
m_strPreAuth = TryGetEx(dictNew, "PreAuth", m_strPreAuth);
m_strPwGenFailed = TryGetEx(dictNew, "PwGenFailed", m_strPwGenFailed);
m_strStructsTooDeep = TryGetEx(dictNew, "StructsTooDeep", m_strStructsTooDeep);
m_strTimeout = TryGetEx(dictNew, "Timeout", m_strTimeout);
m_strTryAgainSecs = TryGetEx(dictNew, "TryAgainSecs", m_strTryAgainSecs);
m_strUnknownError = TryGetEx(dictNew, "UnknownError", m_strUnknownError);
m_strUnknownHeaderId = TryGetEx(dictNew, "UnknownHeaderId", m_strUnknownHeaderId);
m_strUnknownKdf = TryGetEx(dictNew, "UnknownKdf", m_strUnknownKdf);
m_strUserAccountKeyError = TryGetEx(dictNew, "UserAccountKeyError", m_strUserAccountKeyError);
@@ -68,6 +77,9 @@ namespace KeePassLib.Resources
}
private static readonly string[] m_vKeyNames = {
"AlgorithmUnknown",
"CharSetInvalid",
"CharSetTooFewChars",
"CryptoStreamFailed",
"EncDataTooLarge",
"ErrorInClipboard",
@@ -83,7 +95,7 @@ namespace KeePassLib.Resources
"FileNewVerOrPlgReq",
"FileNewVerReq",
"FileSaveCorruptionWarning",
"FileSaveFailed",
"FileSaveFailed2",
"FileSigInvalid",
"FileUnknownCipher",
"FileUnknownCompression",
@@ -97,12 +109,18 @@ namespace KeePassLib.Resources
"KeePass1xHint",
"KeyBits",
"KeyFileDbSel",
"KeyHashMismatch",
"MasterSeedLengthInvalid",
"OldFormat",
"Passive",
"PathBackslash",
"PatternInvalid",
"PreAuth",
"PwGenFailed",
"StructsTooDeep",
"Timeout",
"TryAgainSecs",
"UnknownError",
"UnknownHeaderId",
"UnknownKdf",
"UserAccountKeyError",
@@ -114,6 +132,39 @@ namespace KeePassLib.Resources
return m_vKeyNames;
}
private static string m_strAlgorithmUnknown =
@"The algorithm is unknown.";
/// <summary>
/// Look up a localized string similar to
/// 'The algorithm is unknown.'.
/// </summary>
public static string AlgorithmUnknown
{
get { return m_strAlgorithmUnknown; }
}
private static string m_strCharSetInvalid =
@"The character set is invalid.";
/// <summary>
/// Look up a localized string similar to
/// 'The character set is invalid.'.
/// </summary>
public static string CharSetInvalid
{
get { return m_strCharSetInvalid; }
}
private static string m_strCharSetTooFewChars =
@"There are too few characters in the character set.";
/// <summary>
/// Look up a localized string similar to
/// 'There are too few characters in the character set.'.
/// </summary>
public static string CharSetTooFewChars
{
get { return m_strCharSetTooFewChars; }
}
private static string m_strCryptoStreamFailed =
@"Failed to initialize encryption/decryption stream!";
/// <summary>
@@ -279,15 +330,15 @@ namespace KeePassLib.Resources
get { return m_strFileSaveCorruptionWarning; }
}
private static string m_strFileSaveFailed =
@"Failed to save the current database to the specified location!";
private static string m_strFileSaveFailed2 =
@"Failed to save to the specified file!";
/// <summary>
/// Look up a localized string similar to
/// 'Failed to save the current database to the specified location!'.
/// 'Failed to save to the specified file!'.
/// </summary>
public static string FileSaveFailed
public static string FileSaveFailed2
{
get { return m_strFileSaveFailed; }
get { return m_strFileSaveFailed2; }
}
private static string m_strFileSigInvalid =
@@ -346,10 +397,10 @@ namespace KeePassLib.Resources
}
private static string m_strFrameworkNotImplExcp =
@"The .NET framework/runtime under which KeePass is currently running does not support this operation.";
@"The .NET Framework/runtime under which KeePass is currently running does not support this operation.";
/// <summary>
/// Look up a localized string similar to
/// 'The .NET framework/runtime under which KeePass is currently running does not support this operation.'.
/// 'The .NET Framework/runtime under which KeePass is currently running does not support this operation.'.
/// </summary>
public static string FrameworkNotImplExcp
{
@@ -368,10 +419,10 @@ namespace KeePassLib.Resources
}
private static string m_strInvalidCompositeKey =
@"The composite key is invalid!";
@"The master key is invalid!";
/// <summary>
/// Look up a localized string similar to
/// 'The composite key is invalid!'.
/// 'The master key is invalid!'.
/// </summary>
public static string InvalidCompositeKey
{
@@ -379,10 +430,10 @@ namespace KeePassLib.Resources
}
private static string m_strInvalidCompositeKeyHint =
@"Make sure the composite key is correct and try again.";
@"Make sure that the master key is correct and try it again.";
/// <summary>
/// Look up a localized string similar to
/// 'Make sure the composite key is correct and try again.'.
/// 'Make sure that the master key is correct and try it again.'.
/// </summary>
public static string InvalidCompositeKeyHint
{
@@ -433,6 +484,17 @@ namespace KeePassLib.Resources
get { return m_strKeyFileDbSel; }
}
private static string m_strKeyHashMismatch =
@"The key and the hash do not match, i.e. the key or the hash is invalid.";
/// <summary>
/// Look up a localized string similar to
/// 'The key and the hash do not match, i.e. the key or the hash is invalid.'.
/// </summary>
public static string KeyHashMismatch
{
get { return m_strKeyHashMismatch; }
}
private static string m_strMasterSeedLengthInvalid =
@"The length of the master key seed is invalid!";
/// <summary>
@@ -466,6 +528,28 @@ namespace KeePassLib.Resources
get { return m_strPassive; }
}
private static string m_strPathBackslash =
@"The path contains a backslash. Such paths are not supported (for security reasons).";
/// <summary>
/// Look up a localized string similar to
/// 'The path contains a backslash. Such paths are not supported (for security reasons).'.
/// </summary>
public static string PathBackslash
{
get { return m_strPathBackslash; }
}
private static string m_strPatternInvalid =
@"The pattern is invalid.";
/// <summary>
/// Look up a localized string similar to
/// 'The pattern is invalid.'.
/// </summary>
public static string PatternInvalid
{
get { return m_strPatternInvalid; }
}
private static string m_strPreAuth =
@"Pre-authenticate";
/// <summary>
@@ -477,6 +561,28 @@ namespace KeePassLib.Resources
get { return m_strPreAuth; }
}
private static string m_strPwGenFailed =
@"Failed to generate a password.";
/// <summary>
/// Look up a localized string similar to
/// 'Failed to generate a password.'.
/// </summary>
public static string PwGenFailed
{
get { return m_strPwGenFailed; }
}
private static string m_strStructsTooDeep =
@"Structures are nested too deeply.";
/// <summary>
/// Look up a localized string similar to
/// 'Structures are nested too deeply.'.
/// </summary>
public static string StructsTooDeep
{
get { return m_strStructsTooDeep; }
}
private static string m_strTimeout =
@"Timeout";
/// <summary>
@@ -499,6 +605,17 @@ namespace KeePassLib.Resources
get { return m_strTryAgainSecs; }
}
private static string m_strUnknownError =
@"An unknown error occurred.";
/// <summary>
/// Look up a localized string similar to
/// 'An unknown error occurred.'.
/// </summary>
public static string UnknownError
{
get { return m_strUnknownError; }
}
private static string m_strUnknownHeaderId =
@"Unknown header ID!";
/// <summary>

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,11 +33,11 @@ using KeePassLibSD;
namespace KeePassLib.Security
{
/// <summary>
/// Represents an in-memory encrypted string.
/// A string that is protected in process memory.
/// <c>ProtectedString</c> objects are immutable and thread-safe.
/// </summary>
#if (DEBUG && !KeePassLibSD)
[DebuggerDisplay(@"{ReadString()}")]
[DebuggerDisplay("{ReadString()}")]
#endif
public sealed class ProtectedString
{
@@ -48,11 +48,24 @@ namespace KeePassLib.Security
private bool m_bIsProtected;
private static readonly ProtectedString m_psEmpty = new ProtectedString();
/// <summary>
/// Get an empty <c>ProtectedString</c> object, without protection.
/// </summary>
public static ProtectedString Empty
{
get { return m_psEmpty; }
}
private static readonly ProtectedString m_psEmptyEx = new ProtectedString(
true, new byte[0]);
/// <summary>
/// Get an empty <c>ProtectedString</c> object, with protection turned on.
/// </summary>
public static ProtectedString EmptyEx
{
get { return m_psEmptyEx; }
}
/// <summary>
/// A flag specifying whether the <c>ProtectedString</c> object
/// has turned on memory protection or not.
@@ -66,8 +79,8 @@ namespace KeePassLib.Security
{
get
{
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
if(pBin != null) return (pBin.Length == 0);
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if (p != null) return (p.Length == 0);
Debug.Assert(m_strPlainText != null);
return (m_strPlainText.Length == 0);
@@ -75,18 +88,21 @@ namespace KeePassLib.Security
}
private int m_nCachedLength = -1;
/// <summary>
/// Length of the protected string, in characters.
/// </summary>
public int Length
{
get
{
if(m_nCachedLength >= 0) return m_nCachedLength;
if (m_nCachedLength >= 0) return m_nCachedLength;
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
if(pBin != null)
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if (p != null)
{
byte[] pbPlain = pBin.ReadData();
m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain);
MemUtil.ZeroByteArray(pbPlain);
byte[] pbPlain = p.ReadData();
try { m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain); }
finally { MemUtil.ZeroByteArray(pbPlain); }
}
else
{
@@ -140,53 +156,51 @@ namespace KeePassLib.Security
/// to the value passed in the <c>XorredBuffer</c> object.
/// </summary>
/// <param name="bEnableProtection">Enable protection or not.</param>
/// <param name="xbProtected"><c>XorredBuffer</c> object containing the
/// <param name="xb"><c>XorredBuffer</c> object containing the
/// string in UTF-8 representation. The UTF-8 string must not
/// be <c>null</c>-terminated.</param>
public ProtectedString(bool bEnableProtection, XorredBuffer xbProtected)
public ProtectedString(bool bEnableProtection, XorredBuffer xb)
{
Debug.Assert(xbProtected != null);
if(xbProtected == null) throw new ArgumentNullException("xbProtected");
if (xb == null) { Debug.Assert(false); throw new ArgumentNullException("xb"); }
byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb);
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
byte[] pb = xb.ReadPlainText();
try { Init(bEnableProtection, pb); }
finally { if (bEnableProtection) MemUtil.ZeroByteArray(pb); }
}
private void Init(bool bEnableProtection, string str)
{
if(str == null) throw new ArgumentNullException("str");
if (str == null) throw new ArgumentNullException("str");
m_bIsProtected = bEnableProtection;
// The string already is in memory and immutable,
// As the string already is in memory and immutable,
// protection would be useless
m_strPlainText = str;
}
private void Init(bool bEnableProtection, byte[] pbUtf8)
{
if(pbUtf8 == null) throw new ArgumentNullException("pbUtf8");
if (pbUtf8 == null) throw new ArgumentNullException("pbUtf8");
m_bIsProtected = bEnableProtection;
if(bEnableProtection)
if (bEnableProtection)
m_pbUtf8 = new ProtectedBinary(true, pbUtf8);
else
m_strPlainText = StrUtil.Utf8.GetString(pbUtf8, 0, pbUtf8.Length);
}
/// <summary>
/// Convert the protected string to a normal string object.
/// Be careful with this function, the returned string object
/// Convert the protected string to a standard string object.
/// Be careful with this function, as the returned string object
/// isn't protected anymore and stored in plain-text in the
/// process memory.
/// </summary>
/// <returns>Plain-text string. Is never <c>null</c>.</returns>
public string ReadString()
{
if(m_strPlainText != null) return m_strPlainText;
if (m_strPlainText != null) return m_strPlainText;
byte[] pb = ReadUtf8();
string str = ((pb.Length == 0) ? string.Empty :
@@ -194,82 +208,120 @@ namespace KeePassLib.Security
// No need to clear pb
// As the text is now visible in process memory anyway,
// there's no need to protect it anymore
// there's no need to protect it anymore (strings are
// immutable and thus cannot be overwritten)
m_strPlainText = str;
m_pbUtf8 = null; // Thread-safe order
return str;
}
/// <summary>
/// Read out the string and return it as a char array.
/// The returned array is not protected and should be cleared by
/// the caller.
/// </summary>
/// <returns>Plain-text char array.</returns>
public char[] ReadChars()
{
if (m_strPlainText != null) return m_strPlainText.ToCharArray();
byte[] pb = ReadUtf8();
char[] v;
try { v = StrUtil.Utf8.GetChars(pb); }
finally { MemUtil.ZeroByteArray(pb); }
return v;
}
/// <summary>
/// Read out the string and return a byte array that contains the
/// string encoded using UTF-8. The returned string is not protected
/// anymore!
/// string encoded using UTF-8.
/// The returned array is not protected and should be cleared by
/// the caller.
/// </summary>
/// <returns>Plain-text UTF-8 byte array.</returns>
public byte[] ReadUtf8()
{
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety
if(pBin != null) return pBin.ReadData();
ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if (p != null) return p.ReadData();
return StrUtil.Utf8.GetBytes(m_strPlainText);
}
/// <summary>
/// Read the protected string and return it protected with a sequence
/// of bytes generated by a random stream.
/// Get the string as an UTF-8 sequence xorred with bytes
/// from a <c>CryptoRandomStream</c>.
/// </summary>
/// <param name="crsRandomSource">Random number source.</param>
/// <returns>Protected string.</returns>
public byte[] ReadXorredString(CryptoRandomStream crsRandomSource)
{
Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) throw new ArgumentNullException("crsRandomSource");
if (crsRandomSource == null) { Debug.Assert(false); throw new ArgumentNullException("crsRandomSource"); }
byte[] pbData = ReadUtf8();
uint uLen = (uint)pbData.Length;
int cb = pbData.Length;
byte[] randomPad = crsRandomSource.GetRandomBytes(uLen);
Debug.Assert(randomPad.Length == pbData.Length);
byte[] pbPad = crsRandomSource.GetRandomBytes((uint)cb);
Debug.Assert(pbPad.Length == cb);
for(uint i = 0; i < uLen; ++i)
pbData[i] ^= randomPad[i];
for (int i = 0; i < cb; ++i)
pbData[i] ^= pbPad[i];
MemUtil.ZeroByteArray(pbPad);
return pbData;
}
public ProtectedString WithProtection(bool bProtect)
{
if(bProtect == m_bIsProtected) return this;
if (bProtect == m_bIsProtected) return this;
byte[] pb = ReadUtf8();
ProtectedString ps = new ProtectedString(bProtect, pb);
if(bProtect) MemUtil.ZeroByteArray(pb);
return ps;
// No need to clear pb; either the current or the new object is unprotected
return new ProtectedString(bProtect, pb);
}
public bool Equals(ProtectedString ps, bool bCheckProtEqual)
{
if (ps == null) throw new ArgumentNullException("ps");
if (object.ReferenceEquals(this, ps)) return true; // Perf. opt.
bool bPA = m_bIsProtected, bPB = ps.m_bIsProtected;
if (bCheckProtEqual && (bPA != bPB)) return false;
if (!bPA && !bPB) return (ReadString() == ps.ReadString());
byte[] pbA = ReadUtf8(), pbB = null;
bool bEq;
try
{
pbB = ps.ReadUtf8();
bEq = MemUtil.ArraysEqual(pbA, pbB);
}
finally
{
if (bPA) MemUtil.ZeroByteArray(pbA);
if (bPB && (pbB != null)) MemUtil.ZeroByteArray(pbB);
}
return bEq;
}
public ProtectedString Insert(int iStart, string strInsert)
{
if(iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if(strInsert == null) throw new ArgumentNullException("strInsert");
if(strInsert.Length == 0) return this;
if (iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if (strInsert == null) throw new ArgumentNullException("strInsert");
if (strInsert.Length == 0) return this;
// Only operate directly with strings when m_bIsProtected is
// false, not in the case of non-null m_strPlainText, because
// the operation creates a new sequence in memory
if(!m_bIsProtected)
if (!m_bIsProtected)
return new ProtectedString(false, ReadString().Insert(
iStart, strInsert));
UTF8Encoding utf8 = StrUtil.Utf8;
byte[] pb = ReadUtf8();
char[] v = utf8.GetChars(pb);
char[] vNew;
char[] v = ReadChars(), vNew = null;
byte[] pbNew = null;
ProtectedString ps;
try
{
if(iStart > v.Length)
if (iStart > v.Length)
throw new ArgumentOutOfRangeException("iStart");
char[] vIns = strInsert.ToCharArray();
@@ -279,68 +331,104 @@ namespace KeePassLib.Security
Array.Copy(vIns, 0, vNew, iStart, vIns.Length);
Array.Copy(v, iStart, vNew, iStart + vIns.Length,
v.Length - iStart);
pbNew = utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Insert(iStart, strInsert));
}
finally
{
MemUtil.ZeroArray<char>(v);
MemUtil.ZeroByteArray(pb);
if (vNew != null) MemUtil.ZeroArray<char>(vNew);
if (pbNew != null) MemUtil.ZeroByteArray(pbNew);
}
byte[] pbNew = utf8.GetBytes(vNew);
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Insert(iStart, strInsert));
MemUtil.ZeroArray<char>(vNew);
MemUtil.ZeroByteArray(pbNew);
return ps;
}
public ProtectedString Remove(int iStart, int nCount)
{
if(iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
if(nCount == 0) return this;
if (iStart < 0) throw new ArgumentOutOfRangeException("iStart");
if (nCount < 0) throw new ArgumentOutOfRangeException("nCount");
if (nCount == 0) return this;
// Only operate directly with strings when m_bIsProtected is
// false, not in the case of non-null m_strPlainText, because
// the operation creates a new sequence in memory
if(!m_bIsProtected)
if (!m_bIsProtected)
return new ProtectedString(false, ReadString().Remove(
iStart, nCount));
UTF8Encoding utf8 = StrUtil.Utf8;
byte[] pb = ReadUtf8();
char[] v = utf8.GetChars(pb);
char[] vNew;
char[] v = ReadChars(), vNew = null;
byte[] pbNew = null;
ProtectedString ps;
try
{
if((iStart + nCount) > v.Length)
throw new ArgumentException("iStart + nCount");
if ((iStart + nCount) > v.Length)
throw new ArgumentException("(iStart + nCount) > v.Length");
vNew = new char[v.Length - nCount];
Array.Copy(v, 0, vNew, 0, iStart);
Array.Copy(v, iStart + nCount, vNew, iStart, v.Length -
(iStart + nCount));
pbNew = utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount));
}
finally
{
MemUtil.ZeroArray<char>(v);
MemUtil.ZeroByteArray(pb);
if (vNew != null) MemUtil.ZeroArray<char>(vNew);
if (pbNew != null) MemUtil.ZeroByteArray(pbNew);
}
byte[] pbNew = utf8.GetBytes(vNew);
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount));
MemUtil.ZeroArray<char>(vNew);
MemUtil.ZeroByteArray(pbNew);
return ps;
}
public static ProtectedString operator +(ProtectedString a, ProtectedString b)
{
if (a == null) throw new ArgumentNullException("a");
if (b == null) throw new ArgumentNullException("b");
if (b.IsEmpty) return a.WithProtection(a.IsProtected || b.IsProtected);
if (a.IsEmpty) return b.WithProtection(a.IsProtected || b.IsProtected);
if (!a.IsProtected && !b.IsProtected)
return new ProtectedString(false, a.ReadString() + b.ReadString());
char[] vA = a.ReadChars(), vB = null, vNew = null;
byte[] pbNew = null;
ProtectedString ps;
try
{
vB = b.ReadChars();
vNew = new char[vA.Length + vB.Length];
Array.Copy(vA, vNew, vA.Length);
Array.Copy(vB, 0, vNew, vA.Length, vB.Length);
pbNew = StrUtil.Utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
}
finally
{
MemUtil.ZeroArray<char>(vA);
if (vB != null) MemUtil.ZeroArray<char>(vB);
if (vNew != null) MemUtil.ZeroArray<char>(vNew);
if (pbNew != null) MemUtil.ZeroByteArray(pbNew);
}
return ps;
}
public static ProtectedString operator +(ProtectedString a, string b)
{
ProtectedString psB = new ProtectedString(false, b);
return (a + psB);
}
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,97 +20,90 @@
using System;
using System.Diagnostics;
using KeePassLib.Utility;
namespace KeePassLib.Security
{
/// <summary>
/// Represents an object that is encrypted using a XOR pad until
/// it is read. <c>XorredBuffer</c> objects are immutable and
/// thread-safe.
/// A <c>XorredBuffer</c> object stores data that is encrypted
/// using a XOR pad.
/// </summary>
public sealed class XorredBuffer
public sealed class XorredBuffer : IDisposable
{
private byte[] m_pbData; // Never null
private byte[] m_pbXorPad; // Always valid for m_pbData
private byte[] m_pbCT;
private byte[] m_pbXorPad;
/// <summary>
/// Length of the protected data in bytes.
/// </summary>
public uint Length
{
get { return (uint)m_pbData.Length; }
get
{
if (m_pbCT == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
return (uint)m_pbCT.Length;
}
}
/// <summary>
/// Construct a new XOR-protected object using a protected byte array
/// and a XOR pad that decrypts the protected data. The
/// <paramref name="pbProtectedData" /> byte array must have the same size
/// as the <paramref name="pbXorPad" /> byte array.
/// Construct a new <c>XorredBuffer</c> object.
/// The <paramref name="pbCT" /> byte array must have the same
/// length as the <paramref name="pbXorPad" /> byte array.
/// The <c>XorredBuffer</c> object takes ownership of the two byte
/// arrays, i.e. the caller must not use or modify them afterwards.
/// arrays, i.e. the caller must not use them afterwards.
/// </summary>
/// <param name="pbProtectedData">Protected data (XOR pad applied).</param>
/// <param name="pbCT">Data with XOR pad applied.</param>
/// <param name="pbXorPad">XOR pad that can be used to decrypt the
/// <paramref name="pbProtectedData" /> parameter.</param>
/// <exception cref="System.ArgumentNullException">Thrown if one of the input
/// parameters is <c>null</c>.</exception>
/// <exception cref="System.ArgumentException">Thrown if the byte arrays are
/// of different size.</exception>
public XorredBuffer(byte[] pbProtectedData, byte[] pbXorPad)
/// <paramref name="pbCT" /> byte array.</param>
public XorredBuffer(byte[] pbCT, byte[] pbXorPad)
{
if(pbProtectedData == null) { Debug.Assert(false); throw new ArgumentNullException("pbProtectedData"); }
if(pbXorPad == null) { Debug.Assert(false); throw new ArgumentNullException("pbXorPad"); }
if (pbCT == null) { Debug.Assert(false); throw new ArgumentNullException("pbCT"); }
if (pbXorPad == null) { Debug.Assert(false); throw new ArgumentNullException("pbXorPad"); }
if (pbCT.Length != pbXorPad.Length)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("pbXorPad");
}
Debug.Assert(pbProtectedData.Length == pbXorPad.Length);
if(pbProtectedData.Length != pbXorPad.Length) throw new ArgumentException();
m_pbData = pbProtectedData;
m_pbCT = pbCT;
m_pbXorPad = pbXorPad;
}
#if DEBUG
~XorredBuffer()
{
Debug.Assert((m_pbCT == null) && (m_pbXorPad == null));
}
#endif
public void Dispose()
{
if (m_pbCT == null) return;
MemUtil.ZeroByteArray(m_pbCT);
m_pbCT = null;
MemUtil.ZeroByteArray(m_pbXorPad);
m_pbXorPad = null;
}
/// <summary>
/// Get a copy of the plain-text. The caller is responsible
/// for clearing the byte array safely after using it.
/// </summary>
/// <returns>Unprotected plain-text byte array.</returns>
/// <returns>Plain-text byte array.</returns>
public byte[] ReadPlainText()
{
byte[] pbPlain = new byte[m_pbData.Length];
for(int i = 0; i < pbPlain.Length; ++i)
pbPlain[i] = (byte)(m_pbData[i] ^ m_pbXorPad[i]);
return pbPlain;
}
/* public bool EqualsValue(XorredBuffer xb)
{
if(xb == null) { Debug.Assert(false); throw new ArgumentNullException("xb"); }
if(xb.m_pbData.Length != m_pbData.Length) return false;
for(int i = 0; i < m_pbData.Length; ++i)
byte[] pbCT = m_pbCT, pbX = m_pbXorPad;
if ((pbCT == null) || (pbX == null) || (pbCT.Length != pbX.Length))
{
byte bt1 = (byte)(m_pbData[i] ^ m_pbXorPad[i]);
byte bt2 = (byte)(xb.m_pbData[i] ^ xb.m_pbXorPad[i]);
if(bt1 != bt2) return false;
Debug.Assert(false);
throw new ObjectDisposedException(null);
}
return true;
byte[] pbPT = new byte[pbCT.Length];
for (int i = 0; i < pbPT.Length; ++i)
pbPT[i] = (byte)(pbCT[i] ^ pbX[i]);
return pbPT;
}
public bool EqualsValue(byte[] pb)
{
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
if(pb.Length != m_pbData.Length) return false;
for(int i = 0; i < m_pbData.Length; ++i)
{
if((byte)(m_pbData[i] ^ m_pbXorPad[i]) != pb[i]) return false;
}
return true;
} */
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -78,9 +78,9 @@ namespace KeePassLib.Serialization
public void Load(Stream sSource, KdbxFormat fmt, IStatusLogger slLogger)
{
Debug.Assert(sSource != null);
if(sSource == null) throw new ArgumentNullException("sSource");
if (sSource == null) throw new ArgumentNullException("sSource");
if(m_bUsedOnce)
if (m_bUsedOnce)
throw new InvalidOperationException("Do not reuse KdbxFile objects!");
m_bUsedOnce = true;
@@ -91,7 +91,8 @@ namespace KeePassLib.Serialization
m_format = fmt;
m_slLogger = slLogger;
m_pbsBinaries.Clear();
// Other applications might not perform a deduplication
m_pbsBinaries = new ProtectedBinarySet(false);
UTF8Encoding encNoBom = StrUtil.Utf8;
byte[] pbCipherKey = null;
@@ -103,153 +104,156 @@ namespace KeePassLib.Serialization
HashingStreamEx sHashing = new HashingStreamEx(sSource, false, null);
lStreams.Add(sHashing);
try
{
Stream sXml;
if (fmt == KdbxFormat.Default || fmt == KdbxFormat.ProtocolBuffers)
{
BinaryReaderEx br = new BinaryReaderEx(sHashing,
encNoBom, KLRes.FileCorrupted);
byte[] pbHeader = LoadHeader(br);
m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
try
{
Stream sXml;
if (fmt == KdbxFormat.Default || fmt == KdbxFormat.ProtocolBuffers)
{
BinaryReaderEx br = new BinaryReaderEx(sHashing,
encNoBom, KLRes.FileCorrupted);
byte[] pbHeader = LoadHeader(br);
m_pbHashOfHeader = CryptoUtil.HashSha256(pbHeader);
int cbEncKey, cbEncIV;
ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo);
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
int cbEncKey, cbEncIV;
ICipherEngine iCipher = GetCipher(out cbEncKey, out cbEncIV);
string strIncomplete = KLRes.FileHeaderCorrupted + " " +
KLRes.FileIncomplete;
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_TransformingKey", LogStatusType.AdditionalInfo);
Stream sPlain;
if(m_uFileVersion < FileVersion32_4)
{
Stream sDecrypted = EncryptStream(sHashing, iCipher,
pbCipherKey, cbEncIV, false);
if((sDecrypted == null) || (sDecrypted == sHashing))
throw new SecurityException(KLRes.CryptoStreamFailed);
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
string strIncomplete = KLRes.FileHeaderCorrupted + " " +
KLRes.FileIncomplete;
lStreams.Add(sDecrypted);
Stream sPlain;
if (m_uFileVersion < FileVersion32_4)
{
Stream sDecrypted = EncryptStream(sHashing, iCipher,
pbCipherKey, cbEncIV, false);
if ((sDecrypted == null) || (sDecrypted == sHashing))
throw new SecurityException(KLRes.CryptoStreamFailed);
BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
encNoBom, strIncomplete);
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
throw new EndOfStreamException(strIncomplete);
if(!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes))
throw new InvalidCompositeKeyException();
lStreams.Add(sDecrypted);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
BinaryReaderEx brDecrypted = new BinaryReaderEx(sDecrypted,
encNoBom, strIncomplete);
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
if ((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
throw new EndOfStreamException(strIncomplete);
if (!MemUtil.ArraysEqual(pbStoredStartBytes, m_pbStreamStartBytes))
throw new InvalidCompositeKeyException();
sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode);
}
else // KDBX >= 4
{
byte[] pbStoredHash = MemUtil.Read(sHashing, 32);
if((pbStoredHash == null) || (pbStoredHash.Length != 32))
throw new EndOfStreamException(strIncomplete);
if(!MemUtil.ArraysEqual(m_pbHashOfHeader, pbStoredHash))
throw new InvalidDataException(KLRes.FileHeaderCorrupted);
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64);
byte[] pbStoredHmac = MemUtil.Read(sHashing, 32);
if((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
throw new EndOfStreamException(strIncomplete);
if(!MemUtil.ArraysEqual(pbHeaderHmac, pbStoredHmac))
throw new InvalidCompositeKeyException();
sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode);
}
else // KDBX >= 4
{
byte[] pbStoredHash = MemUtil.Read(sHashing, 32);
if ((pbStoredHash == null) || (pbStoredHash.Length != 32))
throw new EndOfStreamException(strIncomplete);
if (!MemUtil.ArraysEqual(m_pbHashOfHeader, pbStoredHash))
throw new InvalidDataException(KLRes.FileHeaderCorrupted);
HmacBlockStream sBlocks = new HmacBlockStream(sHashing,
false, !m_bRepairMode, pbHmacKey64);
lStreams.Add(sBlocks);
byte[] pbHeaderHmac = ComputeHeaderHmac(pbHeader, pbHmacKey64);
byte[] pbStoredHmac = MemUtil.Read(sHashing, 32);
if ((pbStoredHmac == null) || (pbStoredHmac.Length != 32))
throw new EndOfStreamException(strIncomplete);
if (!MemUtil.ArraysEqual(pbHeaderHmac, pbStoredHmac))
throw new InvalidCompositeKeyException();
sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
cbEncIV, false);
if((sPlain == null) || (sPlain == sBlocks))
throw new SecurityException(KLRes.CryptoStreamFailed);
}
lStreams.Add(sPlain);
HmacBlockStream sBlocks = new HmacBlockStream(sHashing,
false, !m_bRepairMode, pbHmacKey64);
lStreams.Add(sBlocks);
if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
{
sXml = new GZipStream(sPlain, CompressionMode.Decompress);
lStreams.Add(sXml);
}
else sXml = sPlain;
sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
cbEncIV, false);
if ((sPlain == null) || (sPlain == sBlocks))
throw new SecurityException(KLRes.CryptoStreamFailed);
}
if(m_uFileVersion >= FileVersion32_4)
LoadInnerHeader(sXml); // Binary header before XML
}
else if(fmt == KdbxFormat.PlainXml)
sXml = sHashing;
else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); }
lStreams.Add(sPlain);
if(fmt == KdbxFormat.Default)
{
if(m_pbInnerRandomStreamKey == null)
{
Debug.Assert(false);
throw new SecurityException("Invalid inner random stream key!");
}
if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
{
sXml = new GZipStream(sPlain, CompressionMode.Decompress);
lStreams.Add(sXml);
}
else sXml = sPlain;
if (m_uFileVersion >= FileVersion32_4)
LoadInnerHeader(sXml); // Binary header before XML
}
else if (fmt == KdbxFormat.PlainXml)
sXml = sHashing;
else
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("fmt");
}
if (fmt == KdbxFormat.Default)
{
if (m_pbInnerRandomStreamKey == null)
{
Debug.Assert(false);
throw new SecurityException("Invalid inner random stream key!");
}
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_pbInnerRandomStreamKey);
}
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_ParsingDatabase", LogStatusType.AdditionalInfo);
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_pbInnerRandomStreamKey);
}
if (m_slLogger != null)
m_slLogger.SetText("KP2AKEY_ParsingDatabase", LogStatusType.AdditionalInfo);
#if KeePassDebug_WriteXml
// FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
// FileAccess.Write, FileShare.None);
// try
// {
// while(true)
// {
// int b = sXml.ReadByte();
// if(b == -1) break;
// fsOut.WriteByte((byte)b);
// }
// }
// catch(Exception) { }
// fsOut.Close();
#warning XML output is enabled!
/* using(FileStream fsOut = new FileStream("Raw.xml", FileMode.Create,
FileAccess.Write, FileShare.None))
{
while(true)
{
int b = sXml.ReadByte();
if(b == -1) throw new EndOfStreamException();
fsOut.WriteByte((byte)b);
}
} */
#endif
var stopWatch = Stopwatch.StartNew();
if (fmt == KdbxFormat.ProtocolBuffers)
{
KdbpFile.ReadDocument(m_pwDatabase, sXml, m_pbInnerRandomStreamKey, m_pbHashOfHeader);
var stopWatch = Stopwatch.StartNew();
Kp2aLog.Log(String.Format("KdbpFile.ReadDocument: {0}ms", stopWatch.ElapsedMilliseconds));
if (fmt == KdbxFormat.ProtocolBuffers)
{
KdbpFile.ReadDocument(m_pwDatabase, sXml, m_pbInnerRandomStreamKey, m_pbHashOfHeader);
}
else
{
Kp2aLog.Log(String.Format("KdbpFile.ReadDocument: {0}ms", stopWatch.ElapsedMilliseconds));
ReadXmlStreamed(sXml, sHashing);
}
else
{
Kp2aLog.Log(String.Format("ReadXmlStreamed: {0}ms", stopWatch.ElapsedMilliseconds));
}
// ReadXmlDom(sXml);
}
catch(CryptographicException) // Thrown on invalid padding
{
throw new CryptographicException(KLRes.FileCorrupted);
}
finally
{
if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
ReadXmlStreamed(sXml, sHashing);
CommonCleanUpRead(lStreams, sHashing);
}
Kp2aLog.Log(String.Format("ReadXmlStreamed: {0}ms", stopWatch.ElapsedMilliseconds));
}
}
// ReadXmlDom(sXml);
catch (CryptographicException) // Thrown on invalid padding
{
throw new CryptographicException(KLRes.FileCorrupted);
}
finally
{
if (pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
if (pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
CommonCleanUpRead(lStreams, sHashing);
}
#if KDBX_BENCHMARK
swTime.Stop();
@@ -267,9 +271,9 @@ namespace KeePassLib.Serialization
Debug.Assert(m_pbHashOfFileOnDisk != null);
CleanUpInnerRandomStream();
// Reset memory protection settings (to always use reasonable
// defaults)
// defaults)
m_pwDatabase.MemoryProtection = new MemoryProtectionConfig();
// Remove old backups (this call is required here in order to apply
@@ -282,7 +286,7 @@ namespace KeePassLib.Serialization
// Expand the root group, such that in case the user accidently
// collapses the root group he can simply reopen the database
PwGroup pgRoot = m_pwDatabase.RootGroup;
if(pgRoot != null) pgRoot.IsExpanded = true;
if (pgRoot != null) pgRoot.IsExpanded = true;
else { Debug.Assert(false); }
m_pbHashOfHeader = null;
@@ -303,25 +307,25 @@ namespace KeePassLib.Serialization
byte[] pbSig2 = br.ReadBytes(4);
uint uSig2 = MemUtil.BytesToUInt32(pbSig2);
if((uSig1 == FileSignatureOld1) && (uSig2 == FileSignatureOld2))
if ((uSig1 == FileSignatureOld1) && (uSig2 == FileSignatureOld2))
throw new OldFormatException(PwDefs.ShortProductName + @" 1.x",
OldFormatException.OldFormatType.KeePass1x);
if((uSig1 == FileSignature1) && (uSig2 == FileSignature2)) { }
else if((uSig1 == FileSignaturePreRelease1) && (uSig2 ==
if ((uSig1 == FileSignature1) && (uSig2 == FileSignature2)) { }
else if ((uSig1 == FileSignaturePreRelease1) && (uSig2 ==
FileSignaturePreRelease2)) { }
else throw new FormatException(KLRes.FileSigInvalid);
byte[] pb = br.ReadBytes(4);
uint uVersion = MemUtil.BytesToUInt32(pb);
if((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask))
if ((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask))
throw new FormatException(KLRes.FileVersionUnsupported +
MessageService.NewParagraph + KLRes.FileNewVerReq);
m_uFileVersion = uVersion;
while(true)
while (true)
{
if(!ReadHeaderField(br)) break;
if (!ReadHeaderField(br)) break;
}
br.CopyDataTo = null;
@@ -335,23 +339,23 @@ namespace KeePassLib.Serialization
private bool ReadHeaderField(BinaryReaderEx brSource)
{
Debug.Assert(brSource != null);
if(brSource == null) throw new ArgumentNullException("brSource");
if (brSource == null) throw new ArgumentNullException("brSource");
byte btFieldID = brSource.ReadByte();
int cbSize;
Debug.Assert(m_uFileVersion > 0);
if(m_uFileVersion < FileVersion32_4)
if (m_uFileVersion < FileVersion32_4)
cbSize = (int)MemUtil.BytesToUInt16(brSource.ReadBytes(2));
else cbSize = MemUtil.BytesToInt32(brSource.ReadBytes(4));
if(cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
if (cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
byte[] pbData = MemUtil.EmptyByteArray;
if(cbSize > 0) pbData = brSource.ReadBytes(cbSize);
if (cbSize > 0) pbData = brSource.ReadBytes(cbSize);
bool bResult = true;
KdbxHeaderFieldID kdbID = (KdbxHeaderFieldID)btFieldID;
switch(kdbID)
switch (kdbID)
{
case KdbxHeaderFieldID.EndOfHeader:
bResult = false; // Returning false indicates end of header
@@ -375,7 +379,7 @@ namespace KeePassLib.Serialization
Debug.Assert(m_uFileVersion < FileVersion32_4);
AesKdf kdfS = new AesKdf();
if(!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfS.Uuid))
if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfS.Uuid))
m_pwDatabase.KdfParameters = kdfS.GetDefaultParameters();
// m_pbTransformSeed = pbData;
@@ -389,7 +393,7 @@ namespace KeePassLib.Serialization
Debug.Assert(m_uFileVersion < FileVersion32_4);
AesKdf kdfR = new AesKdf();
if(!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfR.Uuid))
if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfR.Uuid))
m_pwDatabase.KdfParameters = kdfR.GetDefaultParameters();
// m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData);
@@ -429,8 +433,8 @@ namespace KeePassLib.Serialization
default:
Debug.Assert(false);
if(m_slLogger != null)
m_slLogger.SetText(KLRes.UnknownHeaderId + @": " +
if (m_slLogger != null)
m_slLogger.SetText(KLRes.UnknownHeaderId + ": " +
kdbID.ToString() + "!", LogStatusType.Warning);
break;
}
@@ -443,28 +447,28 @@ namespace KeePassLib.Serialization
BinaryReaderEx br = new BinaryReaderEx(s, StrUtil.Utf8,
KLRes.FileCorrupted + " " + KLRes.FileIncompleteExpc);
while(true)
while (true)
{
if(!ReadInnerHeaderField(br)) break;
if (!ReadInnerHeaderField(br)) break;
}
}
private bool ReadInnerHeaderField(BinaryReaderEx br)
{
Debug.Assert(br != null);
if(br == null) throw new ArgumentNullException("br");
if (br == null) throw new ArgumentNullException("br");
byte btFieldID = br.ReadByte();
int cbSize = MemUtil.BytesToInt32(br.ReadBytes(4));
if(cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
if (cbSize < 0) throw new FormatException(KLRes.FileCorrupted);
byte[] pbData = MemUtil.EmptyByteArray;
if(cbSize > 0) pbData = br.ReadBytes(cbSize);
if (cbSize > 0) pbData = br.ReadBytes(cbSize);
bool bResult = true;
KdbxInnerHeaderFieldID kdbID = (KdbxInnerHeaderFieldID)btFieldID;
switch(kdbID)
switch (kdbID)
{
case KdbxInnerHeaderFieldID.EndOfHeader:
bResult = false; // Returning false indicates end of header
@@ -481,15 +485,16 @@ namespace KeePassLib.Serialization
break;
case KdbxInnerHeaderFieldID.Binary:
if(pbData.Length < 1) throw new FormatException();
if (pbData.Length < 1) throw new FormatException();
KdbxBinaryFlags f = (KdbxBinaryFlags)pbData[0];
bool bProt = ((f & KdbxBinaryFlags.Protected) != KdbxBinaryFlags.None);
ProtectedBinary pb = new ProtectedBinary(bProt, pbData,
1, pbData.Length - 1);
Debug.Assert(m_pbsBinaries.Find(pb) < 0); // No deduplication?
m_pbsBinaries.Add(pb);
if(bProt) MemUtil.ZeroByteArray(pbData);
if (bProt) MemUtil.ZeroByteArray(pbData);
break;
default:
@@ -502,7 +507,7 @@ namespace KeePassLib.Serialization
private void SetCipher(byte[] pbID)
{
if((pbID == null) || (pbID.Length != (int)PwUuid.UuidSize))
if ((pbID == null) || (pbID.Length != (int)PwUuid.UuidSize))
throw new FormatException(KLRes.FileUnknownCipher);
m_pwDatabase.DataCipherUuid = new PwUuid(pbID);
@@ -511,7 +516,7 @@ namespace KeePassLib.Serialization
private void SetCompressionFlags(byte[] pbFlags)
{
int nID = (int)MemUtil.BytesToUInt32(pbFlags);
if((nID < 0) || (nID >= (int)PwCompressionAlgorithm.Count))
if ((nID < 0) || (nID >= (int)PwCompressionAlgorithm.Count))
throw new FormatException(KLRes.FileUnknownCompression);
m_pwDatabase.Compression = (PwCompressionAlgorithm)nID;
@@ -520,12 +525,35 @@ namespace KeePassLib.Serialization
private void SetInnerRandomStreamID(byte[] pbID)
{
uint uID = MemUtil.BytesToUInt32(pbID);
if(uID >= (uint)CrsAlgorithm.Count)
if (uID >= (uint)CrsAlgorithm.Count)
throw new FormatException(KLRes.FileUnknownCipher);
m_craInnerRandomStream = (CrsAlgorithm)uID;
}
internal static PwGroup ReadGroup(Stream msData, PwDatabase pdContext,
bool bCopyIcons, bool bNewUuids, bool bSetCreatedNow)
{
PwDatabase pd = new PwDatabase();
pd.New(new IOConnectionInfo(), new CompositeKey(), "");
KdbxFile f = new KdbxFile(pd);
f.Load(msData, KdbxFormat.PlainXml, null);
if (bCopyIcons)
PwDatabase.CopyCustomIcons(pd, pdContext, pd.RootGroup, true);
if (bNewUuids)
{
pd.RootGroup.Uuid = new PwUuid(true);
pd.RootGroup.CreateNewItemUuids(true, true, true);
}
if (bSetCreatedNow) pd.RootGroup.SetCreatedNow(true);
return pd.RootGroup;
}
[Obsolete]
public static List<PwEntry> ReadEntries(Stream msData)
{
@@ -537,81 +565,14 @@ namespace KeePassLib.Serialization
{
return ReadEntries(msData, pdContext, true);
}
/// <summary>
/// Read entries from a stream.
/// </summary>
/// <param name="msData">Input stream to read the entries from.</param>
/// <returns>Extracted entries.</returns>
/// <param name="pdContext">Context database (e.g. for storing icons).</param>
/// <param name="bCopyIcons">If <c>true</c>, custom icons required by
/// the loaded entries are copied to the context database.</param>
/// <returns>Loaded entries.</returns>
public static List<PwEntry> ReadEntries(Stream msData, PwDatabase pdContext,
bool bCopyIcons)
{
List<PwEntry> lEntries = new List<PwEntry>();
/* KdbxFile f = new KdbxFile(pwDatabase);
if(msData == null) { Debug.Assert(false); return lEntries; }
f.m_format = KdbxFormat.PlainXml;
if (msData == null) { Debug.Assert(false); return new List<PwEntry>(); }
XmlDocument doc = new XmlDocument();
doc.Load(msData);
XmlElement el = doc.DocumentElement;
if(el.Name != ElemRoot) throw new FormatException();
List<PwEntry> vEntries = new List<PwEntry>();
foreach(XmlNode xmlChild in el.ChildNodes)
{
if(xmlChild.Name == ElemEntry)
{
PwEntry pe = f.ReadEntry(xmlChild);
pe.Uuid = new PwUuid(true);
foreach(PwEntry peHistory in pe.History)
peHistory.Uuid = pe.Uuid;
vEntries.Add(pe);
}
else { Debug.Assert(false); }
}
return vEntries; */
PwDatabase pd = new PwDatabase();
pd.New(new IOConnectionInfo(), new CompositeKey(), "");
KdbxFile f = new KdbxFile(pd);
f.Load(msData, KdbxFormat.PlainXml, null);
foreach(PwEntry pe in pd.RootGroup.Entries)
{
pe.SetUuid(new PwUuid(true), true);
lEntries.Add(pe);
if(bCopyIcons && (pdContext != null))
{
PwUuid pu = pe.CustomIconUuid;
if(!pu.Equals(PwUuid.Zero))
{
int iSrc = pd.GetCustomIconIndex(pu);
int iDst = pdContext.GetCustomIconIndex(pu);
if(iSrc < 0) { Debug.Assert(false); }
else if(iDst < 0)
{
pdContext.CustomIcons.Add(pd.CustomIcons[iSrc]);
pdContext.Modified = true;
pdContext.UINeedsIconUpdate = true;
}
}
}
}
return lEntries;
PwGroup pg = ReadGroup(msData, pdContext, bCopyIcons, true, true);
return pg.GetEntries(true).CloneShallowToList();
}
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@ using System.IO;
using System.Security;
using System.Text;
using System.Xml;
using keepass2android;
#if !KeePassUAP
using System.Drawing;
using System.Security.Cryptography;
@@ -48,8 +48,6 @@ using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Utility;
using keepass2android;
namespace KeePassLib.Serialization
{
/// <summary>
@@ -81,14 +79,15 @@ namespace KeePassLib.Serialization
IStatusLogger slLogger)
{
Debug.Assert(sSaveTo != null);
if(sSaveTo == null) throw new ArgumentNullException("sSaveTo");
if (sSaveTo == null) throw new ArgumentNullException("sSaveTo");
if(m_bUsedOnce)
if (m_bUsedOnce)
throw new InvalidOperationException("Do not reuse KdbxFile objects!");
m_bUsedOnce = true;
m_format = fmt;
m_slLogger = slLogger;
m_xmlWriter = null;
PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup);
UTF8Encoding encNoBom = StrUtil.Utf8;
@@ -96,7 +95,7 @@ namespace KeePassLib.Serialization
byte[] pbCipherKey = null;
byte[] pbHmacKey64 = null;
m_pbsBinaries.Clear();
m_pbsBinaries = new ProtectedBinarySet(true);
m_pbsBinaries.AddFrom(pgRoot);
List<Stream> lStreams = new List<Stream>();
@@ -107,6 +106,10 @@ namespace KeePassLib.Serialization
try
{
// Fix history entries (should not be necessary; just for safety,
// as e.g. XPath searches depend on correct history entry UUIDs)
if (m_pwDatabase.MaintainBackups()) { Debug.Assert(false); }
m_uFileVersion = GetMinKdbxVersion();
int cbEncKey, cbEncIV;
@@ -118,30 +121,30 @@ namespace KeePassLib.Serialization
// m_pbTransformSeed = cr.GetRandomBytes(32);
PwUuid puKdf = m_pwDatabase.KdfParameters.KdfUuid;
KdfEngine kdf = KdfPool.Get(puKdf);
if(kdf == null)
if (kdf == null)
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
// KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
"UUID: " + puKdf.ToHexString() + ".");
kdf.Randomize(m_pwDatabase.KdfParameters);
if(m_format == KdbxFormat.Default)
if (m_format == KdbxFormat.Default)
{
if(m_uFileVersion < FileVersion32_4)
if (m_uFileVersion < FileVersion32_4)
{
m_craInnerRandomStream = CrsAlgorithm.Salsa20;
m_craInnerRandomStream = CrsAlgorithm.Salsa20;
m_pbInnerRandomStreamKey = cr.GetRandomBytes(32);
}
else // KDBX >= 4
{
m_craInnerRandomStream = CrsAlgorithm.ChaCha20;
}
else // KDBX >= 4
{
m_craInnerRandomStream = CrsAlgorithm.ChaCha20;
m_pbInnerRandomStreamKey = cr.GetRandomBytes(64);
}
}
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
m_pbInnerRandomStreamKey);
}
if(m_uFileVersion < FileVersion32_4)
if (m_uFileVersion < FileVersion32_4)
m_pbStreamStartBytes = cr.GetRandomBytes(32);
Stream sXml;
@@ -156,11 +159,11 @@ namespace KeePassLib.Serialization
ComputeKeys(out pbCipherKey, cbEncKey, out pbHmacKey64);
Stream sPlain;
if(m_uFileVersion < FileVersion32_4)
if (m_uFileVersion < FileVersion32_4)
{
Stream sEncrypted = EncryptStream(sHashing, iCipher,
pbCipherKey, cbEncIV, true);
if((sEncrypted == null) || (sEncrypted == sHashing))
if ((sEncrypted == null) || (sEncrypted == sHashing))
throw new SecurityException(KLRes.CryptoStreamFailed);
lStreams.Add(sEncrypted);
@@ -182,22 +185,22 @@ namespace KeePassLib.Serialization
sPlain = EncryptStream(sBlocks, iCipher, pbCipherKey,
cbEncIV, true);
if((sPlain == null) || (sPlain == sBlocks))
if ((sPlain == null) || (sPlain == sBlocks))
throw new SecurityException(KLRes.CryptoStreamFailed);
}
lStreams.Add(sPlain);
if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
{
sXml = new GZipStream(sPlain, CompressionMode.Compress);
lStreams.Add(sXml);
}
else sXml = sPlain;
if(m_uFileVersion >= FileVersion32_4)
if (m_uFileVersion >= FileVersion32_4)
WriteInnerHeader(sXml); // Binary header before XML
}
else if(m_format == KdbxFormat.PlainXml)
else if (m_format == KdbxFormat.PlainXml)
sXml = sHashing;
else
{
@@ -251,6 +254,8 @@ namespace KeePassLib.Serialization
private void CommonCleanUpWrite(List<Stream> lStreams, HashingStreamEx sHashing)
{
if (m_xmlWriter != null) { m_xmlWriter.Close(); m_xmlWriter = null; }
CloseStreams(lStreams);
Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
@@ -259,14 +264,13 @@ namespace KeePassLib.Serialization
CleanUpInnerRandomStream();
m_xmlWriter = null;
m_pbHashOfHeader = null;
}
private byte[] GenerateHeader()
{
byte[] pbHeader;
using(MemoryStream ms = new MemoryStream())
using (MemoryStream ms = new MemoryStream())
{
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature1));
MemUtil.Write(ms, MemUtil.UInt32ToBytes(FileSignature2));
@@ -281,7 +285,7 @@ namespace KeePassLib.Serialization
WriteHeaderField(ms, KdbxHeaderFieldID.MasterSeed, m_pbMasterSeed);
if(m_uFileVersion < FileVersion32_4)
if (m_uFileVersion < FileVersion32_4)
{
Debug.Assert(m_pwDatabase.KdfParameters.KdfUuid.Equals(
(new AesKdf()).Uuid));
@@ -295,10 +299,10 @@ namespace KeePassLib.Serialization
WriteHeaderField(ms, KdbxHeaderFieldID.KdfParameters,
KdfParameters.SerializeExt(m_pwDatabase.KdfParameters));
if(m_pbEncryptionIV.Length > 0)
if (m_pbEncryptionIV.Length > 0)
WriteHeaderField(ms, KdbxHeaderFieldID.EncryptionIV, m_pbEncryptionIV);
if(m_uFileVersion < FileVersion32_4)
if (m_uFileVersion < FileVersion32_4)
{
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamKey,
m_pbInnerRandomStreamKey);
@@ -306,14 +310,14 @@ namespace KeePassLib.Serialization
WriteHeaderField(ms, KdbxHeaderFieldID.StreamStartBytes,
m_pbStreamStartBytes);
int nIrsID = (int)m_craInnerRandomStream;
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
MemUtil.Int32ToBytes(nIrsID));
int nIrsID = (int)m_craInnerRandomStream;
WriteHeaderField(ms, KdbxHeaderFieldID.InnerRandomStreamID,
MemUtil.Int32ToBytes(nIrsID));
}
// Write public custom data only when there is at least one item,
// because KDBX 3.1 didn't support this field yet
if(m_pwDatabase.PublicCustomData.Count > 0)
if (m_pwDatabase.PublicCustomData.Count > 0)
WriteHeaderField(ms, KdbxHeaderFieldID.PublicCustomData,
VariantDictionary.Serialize(m_pwDatabase.PublicCustomData));
@@ -333,12 +337,12 @@ namespace KeePassLib.Serialization
byte[] pb = (pbData ?? MemUtil.EmptyByteArray);
int cb = pb.Length;
if(cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
if (cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
Debug.Assert(m_uFileVersion > 0);
if(m_uFileVersion < FileVersion32_4)
if (m_uFileVersion < FileVersion32_4)
{
if(cb > (int)ushort.MaxValue)
if (cb > (int)ushort.MaxValue)
{
Debug.Assert(false);
throw new ArgumentOutOfRangeException("pbData");
@@ -361,13 +365,13 @@ namespace KeePassLib.Serialization
m_pbInnerRandomStreamKey, null);
ProtectedBinary[] vBin = m_pbsBinaries.ToArray();
for(int i = 0; i < vBin.Length; ++i)
for (int i = 0; i < vBin.Length; ++i)
{
ProtectedBinary pb = vBin[i];
if(pb == null) throw new InvalidOperationException();
if (pb == null) throw new InvalidOperationException();
KdbxBinaryFlags f = KdbxBinaryFlags.None;
if(pb.IsProtected) f |= KdbxBinaryFlags.Protected;
if (pb.IsProtected) f |= KdbxBinaryFlags.Protected;
byte[] pbFlags = new byte[1] { (byte)f };
byte[] pbData = pb.ReadData();
@@ -375,7 +379,7 @@ namespace KeePassLib.Serialization
WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.Binary,
pbFlags, pbData);
if(pb.IsProtected) MemUtil.ZeroByteArray(pbData);
if (pb.IsProtected) MemUtil.ZeroByteArray(pbData);
}
WriteInnerHeaderField(s, KdbxInnerHeaderFieldID.EndOfHeader,
@@ -391,7 +395,7 @@ namespace KeePassLib.Serialization
byte[] pb2 = (pbData2 ?? MemUtil.EmptyByteArray);
int cb = pb1.Length + pb2.Length;
if(cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
if (cb < 0) { Debug.Assert(false); throw new OutOfMemoryException(); }
MemUtil.Write(s, MemUtil.Int32ToBytes(cb));
MemUtil.Write(s, pb1);
@@ -401,7 +405,7 @@ namespace KeePassLib.Serialization
private void WriteDocument(PwGroup pgRoot)
{
Debug.Assert(m_xmlWriter != null);
if(m_xmlWriter == null) throw new InvalidOperationException();
if (m_xmlWriter == null) throw new InvalidOperationException();
uint uNumGroups, uNumEntries, uCurEntry = 0;
pgRoot.GetCounts(true, out uNumGroups, out uNumEntries);
@@ -417,14 +421,14 @@ namespace KeePassLib.Serialization
Stack<PwGroup> groupStack = new Stack<PwGroup>();
groupStack.Push(pgRoot);
GroupHandler gh = delegate(PwGroup pg)
GroupHandler gh = delegate (PwGroup pg)
{
Debug.Assert(pg != null);
if(pg == null) throw new ArgumentNullException("pg");
if (pg == null) throw new ArgumentNullException("pg");
while(true)
while (true)
{
if(pg.ParentGroup == groupStack.Peek())
if (pg.ParentGroup == groupStack.Peek())
{
groupStack.Push(pg);
StartGroup(pg);
@@ -433,7 +437,7 @@ namespace KeePassLib.Serialization
else
{
groupStack.Pop();
if(groupStack.Count <= 0) return false;
if (groupStack.Count <= 0) return false;
EndGroup();
}
@@ -442,23 +446,25 @@ namespace KeePassLib.Serialization
return true;
};
EntryHandler eh = delegate(PwEntry pe)
EntryHandler eh = delegate (PwEntry pe)
{
Debug.Assert(pe != null);
WriteEntry(pe, false);
++uCurEntry;
if(m_slLogger != null)
if(!m_slLogger.SetProgress((100 * uCurEntry) / uNumEntries))
if (m_slLogger != null)
{
if (!m_slLogger.SetProgress((100 * uCurEntry) / uNumEntries))
return false;
}
return true;
};
if(!pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, eh))
throw new InvalidOperationException();
if (!pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, eh))
throw new OperationCanceledException();
while(groupStack.Count > 1)
while (groupStack.Count > 1)
{
m_xmlWriter.WriteEndElement();
groupStack.Pop();
@@ -479,11 +485,11 @@ namespace KeePassLib.Serialization
WriteObject(ElemGenerator, PwDatabase.LocalizedAppName, false);
if((m_pbHashOfHeader != null) && (m_uFileVersion < FileVersion32_4))
if ((m_pbHashOfHeader != null) && (m_uFileVersion < FileVersion32_4))
WriteObject(ElemHeaderHash, Convert.ToBase64String(
m_pbHashOfHeader), false);
if(m_uFileVersion >= FileVersion32_4)
if (m_uFileVersion >= FileVersion32_4)
WriteObject(ElemSettingsChanged, m_pwDatabase.SettingsChanged);
WriteObject(ElemDbName, m_pwDatabase.Name, true);
@@ -497,7 +503,7 @@ namespace KeePassLib.Serialization
WriteObject(ElemDbKeyChanged, m_pwDatabase.MasterKeyChanged);
WriteObject(ElemDbKeyChangeRec, m_pwDatabase.MasterKeyChangeRec);
WriteObject(ElemDbKeyChangeForce, m_pwDatabase.MasterKeyChangeForce);
if(m_pwDatabase.MasterKeyChangeForceOnce)
if (m_pwDatabase.MasterKeyChangeForceOnce)
WriteObject(ElemDbKeyChangeForceOnce, true);
WriteList(ElemMemoryProt, m_pwDatabase.MemoryProtection);
@@ -515,8 +521,8 @@ namespace KeePassLib.Serialization
WriteObject(ElemLastSelectedGroup, m_pwDatabase.LastSelectedGroup);
WriteObject(ElemLastTopVisibleGroup, m_pwDatabase.LastTopVisibleGroup);
if((m_format != KdbxFormat.Default) || (m_uFileVersion < FileVersion32_4))
WriteBinPool();
if ((m_format != KdbxFormat.Default) || (m_uFileVersion < FileVersion32_4))
WriteBinPool();
WriteList(ElemCustomData, m_pwDatabase.CustomData);
@@ -530,10 +536,10 @@ namespace KeePassLib.Serialization
WriteObject(ElemName, pg.Name, true);
WriteObject(ElemNotes, pg.Notes, true);
WriteObject(ElemIcon, (int)pg.IconId);
if(!pg.CustomIconUuid.Equals(PwUuid.Zero))
if (!pg.CustomIconUuid.Equals(PwUuid.Zero))
WriteObject(ElemCustomIconID, pg.CustomIconUuid);
WriteList(ElemTimes, pg);
WriteObject(ElemIsExpanded, pg.IsExpanded);
WriteObject(ElemGroupDefaultAutoTypeSeq, pg.DefaultAutoTypeSequence, true);
@@ -541,7 +547,17 @@ namespace KeePassLib.Serialization
WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false);
WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry);
if(pg.CustomData.Count > 0)
if (m_uFileVersion >= FileVersion32_4_1)
{
if (!pg.PreviousParentGroup.Equals(PwUuid.Zero))
WriteObject(ElemPreviousParentGroup, pg.PreviousParentGroup);
List<string> lTags = pg.Tags;
if (lTags.Count != 0)
WriteObject(ElemTags, StrUtil.TagsToString(lTags, false), true);
}
if (pg.CustomData.Count > 0)
WriteList(ElemCustomData, pg.CustomData);
}
@@ -552,31 +568,39 @@ namespace KeePassLib.Serialization
private void WriteEntry(PwEntry pe, bool bIsHistory)
{
Debug.Assert(pe != null); if(pe == null) throw new ArgumentNullException("pe");
Debug.Assert(pe != null); if (pe == null) throw new ArgumentNullException("pe");
m_xmlWriter.WriteStartElement(ElemEntry);
WriteObject(ElemUuid, pe.Uuid);
WriteObject(ElemIcon, (int)pe.IconId);
if(!pe.CustomIconUuid.Equals(PwUuid.Zero))
if (!pe.CustomIconUuid.Equals(PwUuid.Zero))
WriteObject(ElemCustomIconID, pe.CustomIconUuid);
WriteObject(ElemFgColor, StrUtil.ColorToUnnamedHtml(pe.ForegroundColor, true), false);
WriteObject(ElemBgColor, StrUtil.ColorToUnnamedHtml(pe.BackgroundColor, true), false);
WriteObject(ElemOverrideUrl, pe.OverrideUrl, true);
if ((m_uFileVersion >= FileVersion32_4_1) && !pe.QualityCheck)
WriteObject(ElemQualityCheck, false);
WriteObject(ElemTags, StrUtil.TagsToString(pe.Tags, false), true);
if ((m_uFileVersion >= FileVersion32_4_1) &&
!pe.PreviousParentGroup.Equals(PwUuid.Zero))
WriteObject(ElemPreviousParentGroup, pe.PreviousParentGroup);
WriteList(ElemTimes, pe);
WriteList(pe.Strings, true);
WriteList(pe.Binaries);
WriteList(ElemAutoType, pe.AutoType);
if(pe.CustomData.Count > 0)
if (pe.CustomData.Count > 0)
WriteList(ElemCustomData, pe.CustomData);
if(!bIsHistory) WriteList(ElemHistory, pe.History, true);
if (!bIsHistory) WriteList(ElemHistory, pe.History, true);
else { Debug.Assert(pe.History.UCount == 0); }
m_xmlWriter.WriteEndElement();
@@ -585,18 +609,18 @@ namespace KeePassLib.Serialization
private void WriteList(ProtectedStringDictionary dictStrings, bool bEntryStrings)
{
Debug.Assert(dictStrings != null);
if(dictStrings == null) throw new ArgumentNullException("dictStrings");
if (dictStrings == null) throw new ArgumentNullException("dictStrings");
foreach(KeyValuePair<string, ProtectedString> kvp in dictStrings)
foreach (KeyValuePair<string, ProtectedString> kvp in dictStrings)
WriteObject(kvp.Key, kvp.Value, bEntryStrings);
}
private void WriteList(ProtectedBinaryDictionary dictBinaries)
{
Debug.Assert(dictBinaries != null);
if(dictBinaries == null) throw new ArgumentNullException("dictBinaries");
if (dictBinaries == null) throw new ArgumentNullException("dictBinaries");
foreach(KeyValuePair<string, ProtectedBinary> kvp in dictBinaries)
foreach (KeyValuePair<string, ProtectedBinary> kvp in dictBinaries)
WriteObject(kvp.Key, kvp.Value, true);
}
@@ -604,19 +628,19 @@ namespace KeePassLib.Serialization
{
Debug.Assert(name != null);
Debug.Assert(cfgAutoType != null);
if(cfgAutoType == null) throw new ArgumentNullException("cfgAutoType");
if (cfgAutoType == null) throw new ArgumentNullException("cfgAutoType");
m_xmlWriter.WriteStartElement(name);
WriteObject(ElemAutoTypeEnabled, cfgAutoType.Enabled);
WriteObject(ElemAutoTypeObfuscation, (int)cfgAutoType.ObfuscationOptions);
if(cfgAutoType.DefaultSequence.Length > 0)
if (cfgAutoType.DefaultSequence.Length > 0)
WriteObject(ElemAutoTypeDefaultSeq, cfgAutoType.DefaultSequence, true);
foreach(AutoTypeAssociation a in cfgAutoType.Associations)
foreach (AutoTypeAssociation a in cfgAutoType.Associations)
WriteObject(ElemAutoTypeItem, ElemWindow, ElemKeystrokeSequence,
new KeyValuePair<string, string>(a.WindowName, a.Sequence));
new KeyValuePair<string, string>(a.WindowName, a.Sequence), null);
m_xmlWriter.WriteEndElement();
}
@@ -624,7 +648,7 @@ namespace KeePassLib.Serialization
private void WriteList(string name, ITimeLogger times)
{
Debug.Assert(name != null);
Debug.Assert(times != null); if(times == null) throw new ArgumentNullException("times");
Debug.Assert(times != null); if (times == null) throw new ArgumentNullException("times");
m_xmlWriter.WriteStartElement(name);
@@ -636,17 +660,17 @@ namespace KeePassLib.Serialization
WriteObject(ElemUsageCount, times.UsageCount);
WriteObject(ElemLocationChanged, times.LocationChanged);
m_xmlWriter.WriteEndElement(); // Name
m_xmlWriter.WriteEndElement();
}
private void WriteList(string name, PwObjectList<PwEntry> value, bool bIsHistory)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(name);
foreach(PwEntry pe in value)
foreach (PwEntry pe in value)
WriteEntry(pe, bIsHistory);
m_xmlWriter.WriteEndElement();
@@ -655,11 +679,11 @@ namespace KeePassLib.Serialization
private void WriteList(string name, PwObjectList<PwDeletedObject> value)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(name);
foreach(PwDeletedObject pdo in value)
foreach (PwDeletedObject pdo in value)
WriteObject(ElemDeletedObject, pdo);
m_xmlWriter.WriteEndElement();
@@ -685,31 +709,45 @@ namespace KeePassLib.Serialization
private void WriteList(string name, StringDictionaryEx value)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(name);
foreach(KeyValuePair<string, string> kvp in value)
WriteObject(ElemStringDictExItem, ElemKey, ElemValue, kvp);
foreach (KeyValuePair<string, string> kvp in value)
{
DateTime? odtLastMod = null;
if (m_uFileVersion >= FileVersion32_4_1)
odtLastMod = value.GetLastModificationTime(kvp.Key);
WriteObject(ElemStringDictExItem, ElemKey, ElemValue, kvp, odtLastMod);
}
m_xmlWriter.WriteEndElement();
}
private void WriteCustomIconList()
{
if(m_pwDatabase.CustomIcons.Count == 0) return;
if (m_pwDatabase.CustomIcons.Count == 0) return;
m_xmlWriter.WriteStartElement(ElemCustomIcons);
foreach(PwCustomIcon pwci in m_pwDatabase.CustomIcons)
foreach (PwCustomIcon ci in m_pwDatabase.CustomIcons)
{
m_xmlWriter.WriteStartElement(ElemCustomIconItem);
WriteObject(ElemCustomIconItemID, pwci.Uuid);
WriteObject(ElemCustomIconItemID, ci.Uuid);
string strData = Convert.ToBase64String(pwci.ImageDataPng);
string strData = Convert.ToBase64String(ci.ImageDataPng);
WriteObject(ElemCustomIconItemData, strData, false);
if (m_uFileVersion >= FileVersion32_4_1)
{
if (ci.Name.Length != 0)
WriteObject(ElemName, ci.Name, true);
if (ci.LastModificationTime.HasValue)
WriteObject(ElemLastModTime, ci.LastModificationTime.Value);
}
m_xmlWriter.WriteEndElement();
}
@@ -724,7 +762,7 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(name);
if(bFilterValueXmlChars)
if (bFilterValueXmlChars)
m_xmlWriter.WriteString(StrUtil.SafeXmlString(value));
else m_xmlWriter.WriteString(value);
@@ -741,7 +779,7 @@ namespace KeePassLib.Serialization
private void WriteObject(string name, PwUuid value)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
WriteObject(name, Convert.ToBase64String(value.UuidBytes), false);
}
@@ -788,7 +826,7 @@ namespace KeePassLib.Serialization
Debug.Assert(value.Kind == DateTimeKind.Utc);
// Cf. ReadTime
if((m_format == KdbxFormat.Default) && (m_uFileVersion >= FileVersion32_4))
if ((m_format == KdbxFormat.Default) && (m_uFileVersion >= FileVersion32_4))
{
DateTime dt = TimeUtil.ToUtc(value, false);
@@ -805,29 +843,33 @@ namespace KeePassLib.Serialization
byte[] pb = MemUtil.Int64ToBytes(lSec);
WriteObject(name, Convert.ToBase64String(pb), false);
}
}
else WriteObject(name, TimeUtil.SerializeUtc(value), false);
}
private void WriteObject(string name, string strKeyName,
string strValueName, KeyValuePair<string, string> kvp)
private void WriteObject(string name, string strKeyName, string strValueName,
KeyValuePair<string, string> kvp, DateTime? odtLastMod)
{
m_xmlWriter.WriteStartElement(name);
m_xmlWriter.WriteStartElement(strKeyName);
m_xmlWriter.WriteString(StrUtil.SafeXmlString(kvp.Key));
m_xmlWriter.WriteEndElement();
m_xmlWriter.WriteStartElement(strValueName);
m_xmlWriter.WriteString(StrUtil.SafeXmlString(kvp.Value));
m_xmlWriter.WriteEndElement();
if (odtLastMod.HasValue)
WriteObject(ElemLastModTime, odtLastMod.Value);
m_xmlWriter.WriteEndElement();
}
private void WriteObject(string name, ProtectedString value, bool bIsEntryString)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(ElemString);
m_xmlWriter.WriteStartElement(ElemKey);
@@ -836,30 +878,30 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(ElemValue);
bool bProtected = value.IsProtected;
if(bIsEntryString)
if (bIsEntryString)
{
// Adjust memory protection setting (which might be different
// from the database default, e.g. due to an import which
// didn't specify the correct setting)
if(name == PwDefs.TitleField)
if (name == PwDefs.TitleField)
bProtected = m_pwDatabase.MemoryProtection.ProtectTitle;
else if(name == PwDefs.UserNameField)
else if (name == PwDefs.UserNameField)
bProtected = m_pwDatabase.MemoryProtection.ProtectUserName;
else if(name == PwDefs.PasswordField)
else if (name == PwDefs.PasswordField)
bProtected = m_pwDatabase.MemoryProtection.ProtectPassword;
else if(name == PwDefs.UrlField)
else if (name == PwDefs.UrlField)
bProtected = m_pwDatabase.MemoryProtection.ProtectUrl;
else if(name == PwDefs.NotesField)
else if (name == PwDefs.NotesField)
bProtected = m_pwDatabase.MemoryProtection.ProtectNotes;
}
if(bProtected && (m_format == KdbxFormat.Default))
if (bProtected && (m_format == KdbxFormat.Default))
{
m_xmlWriter.WriteAttributeString(AttrProtected, ValTrue);
byte[] pbEncoded = value.ReadXorredString(m_randomStream);
if(pbEncoded.Length > 0)
m_xmlWriter.WriteBase64(pbEncoded, 0, pbEncoded.Length);
byte[] pbEnc = value.ReadXorredString(m_randomStream);
if (pbEnc.Length > 0)
m_xmlWriter.WriteBase64(pbEnc, 0, pbEnc.Length);
}
else
{
@@ -869,25 +911,24 @@ namespace KeePassLib.Serialization
// string transformation here. By default, language-dependent conversions
// should be applied, otherwise characters could be rendered incorrectly
// (code page problems).
if(m_bLocalizedNames)
if (g_bLocalizedNames)
{
StringBuilder sb = new StringBuilder();
foreach(char ch in strValue)
foreach (char ch in strValue)
{
char chMapped = ch;
// Symbols and surrogates must be moved into the correct code
// page area
if(char.IsSymbol(ch) || char.IsSurrogate(ch))
if (char.IsSymbol(ch) || char.IsSurrogate(ch))
{
System.Globalization.UnicodeCategory cat =
CharUnicodeInfo.GetUnicodeCategory(ch);
UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory(ch);
// Map character to correct position in code page
chMapped = (char)((int)cat * 32 + ch);
}
else if(char.IsControl(ch))
else if (char.IsControl(ch))
{
if(ch >= 256) // Control character in high ANSI code page
if (ch >= 256) // Control character in high ANSI code page
{
// Some of the control characters map to corresponding ones
// in the low ANSI range (up to 255) when calling
@@ -907,7 +948,7 @@ namespace KeePassLib.Serialization
strValue = sb.ToString(); // Correct string for current code page
}
if((m_format == KdbxFormat.PlainXml) && bProtected)
if ((m_format == KdbxFormat.PlainXml) && bProtected)
m_xmlWriter.WriteAttributeString(AttrProtectedInMemPlainXml, ValTrue);
m_xmlWriter.WriteString(StrUtil.SafeXmlString(strValue));
@@ -920,7 +961,7 @@ namespace KeePassLib.Serialization
private void WriteObject(string name, ProtectedBinary value, bool bAllowRef)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(ElemBinary);
m_xmlWriter.WriteStartElement(ElemKey);
@@ -929,13 +970,13 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(ElemValue);
string strRef = null;
if(bAllowRef)
if (bAllowRef)
{
int iRef = m_pbsBinaries.Find(value);
if(iRef >= 0) strRef = iRef.ToString(NumberFormatInfo.InvariantInfo);
if (iRef >= 0) strRef = iRef.ToString(NumberFormatInfo.InvariantInfo);
else { Debug.Assert(false); }
}
if(strRef != null)
if (strRef != null)
m_xmlWriter.WriteAttributeString(AttrRef, strRef);
else SubWriteValue(value);
@@ -945,17 +986,17 @@ namespace KeePassLib.Serialization
private void SubWriteValue(ProtectedBinary value)
{
if(value.IsProtected && (m_format == KdbxFormat.Default))
if (value.IsProtected && (m_format == KdbxFormat.Default))
{
m_xmlWriter.WriteAttributeString(AttrProtected, ValTrue);
byte[] pbEncoded = value.ReadXorredData(m_randomStream);
if(pbEncoded.Length > 0)
m_xmlWriter.WriteBase64(pbEncoded, 0, pbEncoded.Length);
byte[] pbEnc = value.ReadXorredData(m_randomStream);
if (pbEnc.Length > 0)
m_xmlWriter.WriteBase64(pbEnc, 0, pbEnc.Length);
}
else
{
if(m_pwDatabase.Compression != PwCompressionAlgorithm.None)
if (m_pwDatabase.Compression != PwCompressionAlgorithm.None)
{
m_xmlWriter.WriteAttributeString(AttrCompressed, ValTrue);
@@ -963,18 +1004,18 @@ namespace KeePassLib.Serialization
byte[] pbCmp = MemUtil.Compress(pbRaw);
m_xmlWriter.WriteBase64(pbCmp, 0, pbCmp.Length);
if(value.IsProtected)
if (value.IsProtected)
{
MemUtil.ZeroByteArray(pbRaw);
MemUtil.ZeroByteArray(pbCmp);
}
}
}
else
{
byte[] pbRaw = value.ReadData();
m_xmlWriter.WriteBase64(pbRaw, 0, pbRaw.Length);
if(value.IsProtected) MemUtil.ZeroByteArray(pbRaw);
if (value.IsProtected) MemUtil.ZeroByteArray(pbRaw);
}
}
}
@@ -982,7 +1023,7 @@ namespace KeePassLib.Serialization
private void WriteObject(string name, PwDeletedObject value)
{
Debug.Assert(name != null);
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
m_xmlWriter.WriteStartElement(name);
WriteObject(ElemUuid, value.Uuid);
@@ -995,7 +1036,7 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteStartElement(ElemBinaries);
ProtectedBinary[] v = m_pbsBinaries.ToArray();
for(int i = 0; i < v.Length; ++i)
for (int i = 0; i < v.Length; ++i)
{
m_xmlWriter.WriteStartElement(ElemBinary);
m_xmlWriter.WriteAttributeString(AttrId,
@@ -1007,6 +1048,25 @@ namespace KeePassLib.Serialization
m_xmlWriter.WriteEndElement();
}
internal static void WriteGroup(Stream msOutput, PwDatabase pdContext,
PwGroup pg)
{
if (msOutput == null) throw new ArgumentNullException("msOutput");
// pdContext may be null
if (pg == null) throw new ArgumentNullException("pg");
PwDatabase pd = new PwDatabase();
pd.New(new IOConnectionInfo(), new CompositeKey(), pg.Name);
pd.RootGroup = pg.CloneDeep();
pd.RootGroup.ParentGroup = null;
PwDatabase.CopyCustomIcons(pdContext, pd, pd.RootGroup, true);
KdbxFile f = new KdbxFile(pd);
f.Save(msOutput, null, KdbxFormat.PlainXml, null);
}
[Obsolete]
public static bool WriteEntries(Stream msOutput, PwEntry[] vEntries)
{
@@ -1014,66 +1074,21 @@ namespace KeePassLib.Serialization
}
public static bool WriteEntries(Stream msOutput, PwDatabase pdContext,
PwEntry[] vEntries)
PwEntry[] vEntries)
{
if (msOutput == null) { Debug.Assert(false); return false; }
// pdContext may be null
if (vEntries == null) { Debug.Assert(false); return false; }
/* KdbxFile f = new KdbxFile(pwDatabase);
f.m_format = KdbxFormat.PlainXml;
XmlTextWriter xtw = null;
try { xtw = new XmlTextWriter(msOutput, StrUtil.Utf8); }
catch(Exception) { Debug.Assert(false); return false; }
if(xtw == null) { Debug.Assert(false); return false; }
f.m_xmlWriter = xtw;
xtw.Formatting = Formatting.Indented;
xtw.IndentChar = '\t';
xtw.Indentation = 1;
xtw.WriteStartDocument(true);
xtw.WriteStartElement(ElemRoot);
foreach(PwEntry pe in vEntries)
f.WriteEntry(pe, false);
xtw.WriteEndElement();
xtw.WriteEndDocument();
xtw.Flush();
xtw.Close();
return true; */
PwDatabase pd = new PwDatabase();
pd.New(new IOConnectionInfo(), new CompositeKey(), "");
PwGroup pg = pd.RootGroup;
if (pg == null) { Debug.Assert(false); return false; }
PwGroup pg = new PwGroup(true, true);
foreach (PwEntry pe in vEntries)
{
PwUuid pu = pe.CustomIconUuid;
if (!pu.Equals(PwUuid.Zero) && (pd.GetCustomIconIndex(pu) < 0))
{
int i = -1;
if (pdContext != null) i = pdContext.GetCustomIconIndex(pu);
if (i >= 0)
{
PwCustomIcon ci = pdContext.CustomIcons[i];
pd.CustomIcons.Add(ci);
}
else { Debug.Assert(pdContext == null); }
}
PwEntry peCopy = pe.CloneDeep();
pg.AddEntry(peCopy, true);
}
KdbxFile f = new KdbxFile(pd);
f.Save(msOutput, null, KdbxFormat.PlainXml, null);
WriteGroup(msOutput, pdContext, pg);
return true;
}
}

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -67,47 +67,51 @@ namespace KeePassLib.Serialization
/// </summary>
public sealed partial class KdbxFile
{
private class ColorTranslator
{
public static Color FromHtml(String colorString)
{
Color color;
/// <summary>
/// System.Drawing.ColorTranslator is not supported on Android. Provide a custom implementation here for loading colors from file.
/// </summary>
private class ColorTranslator
{
public static Color FromHtml(String colorString)
{
Color color;
if (colorString.StartsWith("#"))
{
colorString = colorString.Substring(1);
}
if (colorString.EndsWith(";"))
{
colorString = colorString.Substring(0, colorString.Length - 1);
}
if (colorString.StartsWith("#"))
{
colorString = colorString.Substring(1);
}
if (colorString.EndsWith(";"))
{
colorString = colorString.Substring(0, colorString.Length - 1);
}
int red, green, blue;
switch (colorString.Length)
{
case 6:
red = int.Parse(colorString.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 3:
red = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(1, 1), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(2, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 1:
red = green = blue = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
default:
throw new ArgumentException("Invalid color: " + colorString);
}
return color;
}
int red, green, blue;
switch (colorString.Length)
{
case 6:
red = int.Parse(colorString.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 3:
red = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(1, 1), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(2, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 1:
red = green = blue = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
default:
throw new ArgumentException("Invalid color: " + colorString);
}
return color;
}
}
}
/// <summary>
/// File identifier, first 32-bit value.
/// </summary>
@@ -119,16 +123,17 @@ namespace KeePassLib.Serialization
internal const uint FileSignature2 = 0xB54BFB67;
/// <summary>
/// File version of files saved by the current <c>KdbxFile</c> class.
/// Maximum supported version of database files.
/// KeePass 2.07 has version 1.01, 2.08 has 1.02, 2.09 has 2.00,
/// 2.10 has 2.02, 2.11 has 2.04, 2.15 has 3.00, 2.20 has 3.01.
/// The first 2 bytes are critical (i.e. loading will fail, if the
/// file version is too high), the last 2 bytes are informational.
/// </summary>
private const uint FileVersion32 = 0x00040000;
private const uint FileVersion32 = 0x00040001;
public const uint FileVersion32_4 = 0x00040000; // First of 4.x series
public const uint FileVersion32_3 = 0x00030001; // Old format 3.1
public const uint FileVersion32_4_1 = 0x00040001; // 4.1
public const uint FileVersion32_4 = 0x00040000; // 4.0
public const uint FileVersion32_3_1 = 0x00030001; // 3.1
private const uint FileVersionCriticalMask = 0xFFFF0000;
@@ -143,7 +148,7 @@ namespace KeePassLib.Serialization
private const string ElemMeta = "Meta";
private const string ElemRoot = "Root";
private const string ElemGroup = "Group";
private const string ElemEntry = "Entry";
internal const string ElemEntry = "Entry";
private const string ElemGenerator = "Generator";
private const string ElemHeaderHash = "HeaderHash";
@@ -188,12 +193,13 @@ namespace KeePassLib.Serialization
private const string ElemName = "Name";
private const string ElemNotes = "Notes";
private const string ElemUuid = "UUID";
internal const string ElemUuid = "UUID";
private const string ElemIcon = "IconID";
private const string ElemCustomIconID = "CustomIconUUID";
private const string ElemFgColor = "ForegroundColor";
private const string ElemBgColor = "BackgroundColor";
private const string ElemOverrideUrl = "OverrideURL";
private const string ElemQualityCheck = "QualityCheck";
private const string ElemTimes = "Times";
private const string ElemTags = "Tags";
@@ -205,6 +211,8 @@ namespace KeePassLib.Serialization
private const string ElemUsageCount = "UsageCount";
private const string ElemLocationChanged = "LocationChanged";
private const string ElemPreviousParentGroup = "PreviousParentGroup";
private const string ElemGroupDefaultAutoTypeSeq = "DefaultAutoTypeSequence";
private const string ElemEnableAutoType = "EnableAutoType";
private const string ElemEnableSearching = "EnableSearching";
@@ -261,7 +269,7 @@ namespace KeePassLib.Serialization
private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant;
private byte[] m_pbInnerRandomStreamKey = null;
private ProtectedBinarySet m_pbsBinaries = new ProtectedBinarySet();
private ProtectedBinarySet m_pbsBinaries = null;
private byte[] m_pbHashOfHeader = null;
private byte[] m_pbHashOfFileOnDisk = null;
@@ -271,7 +279,7 @@ namespace KeePassLib.Serialization
private const uint NeutralLanguageOffset = 0x100000; // 2^20, see 32-bit Unicode specs
private const uint NeutralLanguageIDSec = 0x7DC5C; // See 32-bit Unicode specs
private const uint NeutralLanguageID = NeutralLanguageOffset + NeutralLanguageIDSec;
private static bool m_bLocalizedNames = false;
private static bool g_bLocalizedNames = false;
private enum KdbxHeaderFieldID : byte
{
@@ -345,7 +353,7 @@ namespace KeePassLib.Serialization
public KdbxFile(PwDatabase pwDataStore)
{
Debug.Assert(pwDataStore != null);
if(pwDataStore == null) throw new ArgumentNullException("pwDataStore");
if (pwDataStore == null) throw new ArgumentNullException("pwDataStore");
m_pwDatabase = pwDataStore;
}
@@ -356,57 +364,92 @@ namespace KeePassLib.Serialization
public static void DetermineLanguageId()
{
// Test if localized names should be used. If localized names are used,
// the m_bLocalizedNames value must be set to true. By default, localized
// names should be used! (Otherwise characters could be corrupted
// the g_bLocalizedNames value must be set to true. By default, localized
// names should be used (otherwise characters could be corrupted
// because of different code pages).
unchecked
{
uint uTest = 0;
foreach(char ch in PwDatabase.LocalizedAppName)
foreach (char ch in PwDatabase.LocalizedAppName)
uTest = uTest * 5 + ch;
m_bLocalizedNames = (uTest != NeutralLanguageID);
g_bLocalizedNames = (uTest != NeutralLanguageID);
}
}
private uint GetMinKdbxVersion()
private uint GetMinKdbxVersion()
{
if (m_uForceVersion != 0) return m_uForceVersion;
uint minVersionForKeys = m_pwDatabase.MasterKey.UserKeys.Select(key => key.GetMinKdbxVersion()).Max();
uint minRequiredVersion = Math.Max(minVersionForKeys, m_uFileVersion); //don't save a version lower than what we read
return Math.Max(minRequiredVersion, GetMinKdbxVersionOrig());
}
private uint GetMinKdbxVersionOrig()
{
if(m_uForceVersion != 0) return m_uForceVersion;
if (m_uForceVersion != 0) return m_uForceVersion;
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
uint minVersionForKeys = m_pwDatabase.MasterKey.UserKeys.Select(key => key.GetMinKdbxVersion()).Max();
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
uint uMin = 0;
uint minRequiredVersion = Math.Max(minVersionForKeys, m_uFileVersion); //don't save a version lower than what we read
AesKdf kdfAes = new AesKdf();
if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
return Math.Max(FileVersion32, minRequiredVersion);
if(m_pwDatabase.PublicCustomData.Count > 0)
return Math.Max(FileVersion32, minRequiredVersion);
bool bCustomData = false;
GroupHandler gh = delegate(PwGroup pg)
GroupHandler gh = delegate (PwGroup pg)
{
if(pg == null) { Debug.Assert(false); return true; }
if(pg.CustomData.Count > 0) { bCustomData = true; return false; }
if (pg == null) { Debug.Assert(false); return true; }
if (pg.Tags.Count != 0)
uMin = Math.Max(uMin, FileVersion32_4_1);
if (pg.CustomData.Count != 0)
uMin = Math.Max(uMin, FileVersion32_4);
return true;
};
EntryHandler eh = delegate(PwEntry pe)
EntryHandler eh = delegate (PwEntry pe)
{
if(pe == null) { Debug.Assert(false); return true; }
if(pe.CustomData.Count > 0) { bCustomData = true; return false; }
if (pe == null) { Debug.Assert(false); return true; }
if (!pe.QualityCheck)
uMin = Math.Max(uMin, FileVersion32_4_1);
if (pe.CustomData.Count != 0)
uMin = Math.Max(uMin, FileVersion32_4);
return true;
};
gh(m_pwDatabase.RootGroup);
m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
if(bCustomData)
return Math.Max(FileVersion32, minRequiredVersion);
return Math.Max(FileVersion32_3, minRequiredVersion); ; // KDBX 3.1 is sufficient
if (uMin >= FileVersion32_4_1) return uMin; // All below is <= 4.1
foreach (PwCustomIcon ci in m_pwDatabase.CustomIcons)
{
if ((ci.Name.Length != 0) || ci.LastModificationTime.HasValue)
return FileVersion32_4_1;
}
foreach (KeyValuePair<string, string> kvp in m_pwDatabase.CustomData)
{
DateTime? odt = m_pwDatabase.CustomData.GetLastModificationTime(kvp.Key);
if (odt.HasValue) return FileVersion32_4_1;
}
if (uMin >= FileVersion32_4) return uMin; // All below is <= 4
if (m_pwDatabase.DataCipherUuid.Equals(ChaCha20Engine.ChaCha20Uuid))
return FileVersion32_4;
AesKdf kdfAes = new AesKdf();
if (!m_pwDatabase.KdfParameters.KdfUuid.Equals(kdfAes.Uuid))
return FileVersion32_4;
if (m_pwDatabase.PublicCustomData.Count != 0)
return FileVersion32_4;
return FileVersion32_3_1; // KDBX 3.1 is sufficient
}
private void ComputeKeys(out byte[] pbCipherKey, int cbCipherKey,
@@ -416,12 +459,12 @@ namespace KeePassLib.Serialization
try
{
Debug.Assert(m_pbMasterSeed != null);
if(m_pbMasterSeed == null)
if (m_pbMasterSeed == null)
throw new ArgumentNullException("m_pbMasterSeed");
Debug.Assert(m_pbMasterSeed.Length == 32);
if(m_pbMasterSeed.Length != 32)
if (m_pbMasterSeed.Length != 32)
throw new FormatException(KLRes.MasterSeedLengthInvalid);
Array.Copy(m_pbMasterSeed, 0, pbCmp, 0, 32);
Debug.Assert(m_pwDatabase != null);
Debug.Assert(m_pwDatabase.MasterKey != null);
@@ -431,10 +474,10 @@ namespace KeePassLib.Serialization
Array.Copy(m_pbMasterSeed, 0, pbCmp, 0, 32);
Debug.Assert(pbinUser != null);
if(pbinUser == null)
if (pbinUser == null)
throw new SecurityException(KLRes.InvalidCompositeKey);
byte[] pUserKey32 = pbinUser.ReadData();
if((pUserKey32 == null) || (pUserKey32.Length != 32))
if ((pUserKey32 == null) || (pUserKey32.Length != 32))
throw new SecurityException(KLRes.InvalidCompositeKey);
Array.Copy(pUserKey32, 0, pbCmp, 32, 32);
MemUtil.ZeroByteArray(pUserKey32);
@@ -442,7 +485,7 @@ namespace KeePassLib.Serialization
pbCipherKey = CryptoUtil.ResizeKey(pbCmp, 0, 64, cbCipherKey);
pbCmp[64] = 1;
using(SHA512Managed h = new SHA512Managed())
using (SHA512Managed h = new SHA512Managed())
{
pbHmacKey64 = h.ComputeHash(pbCmp);
}
@@ -454,19 +497,19 @@ namespace KeePassLib.Serialization
{
PwUuid pu = m_pwDatabase.DataCipherUuid;
ICipherEngine iCipher = CipherPool.GlobalPool.GetCipher(pu);
if(iCipher == null) // CryptographicExceptions are translated to "file corrupted"
if (iCipher == null) // CryptographicExceptions are translated to "file corrupted"
throw new Exception(KLRes.FileUnknownCipher +
MessageService.NewParagraph + KLRes.FileNewVerOrPlgReq +
MessageService.NewParagraph + "UUID: " + pu.ToHexString() + ".");
ICipherEngine2 iCipher2 = (iCipher as ICipherEngine2);
if(iCipher2 != null)
if (iCipher2 != null)
{
cbEncKey = iCipher2.KeyLength;
if(cbEncKey < 0) throw new InvalidOperationException("EncKey.Length");
if (cbEncKey < 0) throw new InvalidOperationException("EncKey.Length");
cbEncIV = iCipher2.IVLength;
if(cbEncIV < 0) throw new InvalidOperationException("EncIV.Length");
if (cbEncIV < 0) throw new InvalidOperationException("EncIV.Length");
}
else
{
@@ -481,13 +524,13 @@ namespace KeePassLib.Serialization
byte[] pbKey, int cbIV, bool bEncrypt)
{
byte[] pbIV = (m_pbEncryptionIV ?? MemUtil.EmptyByteArray);
if(pbIV.Length != cbIV)
if (pbIV.Length != cbIV)
{
Debug.Assert(false);
throw new Exception(KLRes.FileCorrupted);
}
if(bEncrypt)
if (bEncrypt)
return iCipher.EncryptStream(s, pbKey, pbIV);
return iCipher.DecryptStream(s, pbKey, pbIV);
}
@@ -497,7 +540,7 @@ namespace KeePassLib.Serialization
byte[] pbHeaderHmac;
byte[] pbBlockKey = HmacBlockStream.GetHmacKey64(
pbKey, ulong.MaxValue);
using(HMACSHA256 h = new HMACSHA256(pbBlockKey))
using (HMACSHA256 h = new HMACSHA256(pbBlockKey))
{
pbHeaderHmac = h.ComputeHash(pbHeader);
}
@@ -508,7 +551,7 @@ namespace KeePassLib.Serialization
private void CloseStreams(List<Stream> lStreams)
{
if(lStreams == null) { Debug.Assert(false); return; }
if (lStreams == null) { Debug.Assert(false); return; }
// Typically, closing a stream also closes its base
// stream; however, there may be streams that do not
@@ -516,14 +559,14 @@ namespace KeePassLib.Serialization
// we close all streams manually, from the innermost
// to the outermost
for(int i = lStreams.Count - 1; i >= 0; --i)
for (int i = lStreams.Count - 1; i >= 0; --i)
{
// Check for duplicates
Debug.Assert((lStreams.IndexOf(lStreams[i]) == i) &&
(lStreams.LastIndexOf(lStreams[i]) == i));
try { lStreams[i].Close(); }
catch(Exception) { Debug.Assert(false); }
catch (Exception) { Debug.Assert(false); }
}
// Do not clear the list
@@ -531,18 +574,18 @@ namespace KeePassLib.Serialization
private void CleanUpInnerRandomStream()
{
if(m_randomStream != null) m_randomStream.Dispose();
if (m_randomStream != null) m_randomStream.Dispose();
if(m_pbInnerRandomStreamKey != null)
if (m_pbInnerRandomStreamKey != null)
MemUtil.ZeroByteArray(m_pbInnerRandomStreamKey);
}
private static void SaveBinary(string strName, ProtectedBinary pb,
string strSaveDir)
{
if(pb == null) { Debug.Assert(false); return; }
if (pb == null) { Debug.Assert(false); return; }
if(string.IsNullOrEmpty(strName)) strName = "File.bin";
strName = UrlUtil.GetSafeFileName(strName);
string strPath;
int iTry = 1;
@@ -550,31 +593,23 @@ namespace KeePassLib.Serialization
{
strPath = UrlUtil.EnsureTerminatingSeparator(strSaveDir, false);
string strExt = UrlUtil.GetExtension(strName);
string strDesc = UrlUtil.StripExtension(strName);
string strExt = UrlUtil.GetExtension(strName);
strPath += strDesc;
if(iTry > 1)
if (iTry > 1)
strPath += " (" + iTry.ToString(NumberFormatInfo.InvariantInfo) +
")";
if(!string.IsNullOrEmpty(strExt)) strPath += "." + strExt;
if (!string.IsNullOrEmpty(strExt)) strPath += "." + strExt;
++iTry;
}
while(File.Exists(strPath));
while (File.Exists(strPath));
#if !KeePassLibSD
byte[] pbData = pb.ReadData();
File.WriteAllBytes(strPath, pbData);
MemUtil.ZeroByteArray(pbData);
#else
FileStream fs = new FileStream(strPath, FileMode.Create,
FileAccess.Write, FileShare.None);
byte[] pbData = pb.ReadData();
fs.Write(pbData, 0, pbData.Length);
fs.Close();
#endif
try { File.WriteAllBytes(strPath, pbData); }
finally { if (pb.IsProtected) MemUtil.ZeroByteArray(pbData); }
}
}
}

View File

@@ -786,5 +786,28 @@ namespace KeePassLib.Utility
yield break;
}
internal static bool ListsEqual<T>(List<T> a, List<T> b)
where T : class, IEquatable<T>
{
if (object.ReferenceEquals(a, b)) return true;
if ((a == null) || (b == null)) return false;
int n = a.Count;
if (n != b.Count) return false;
for (int i = 0; i < n; ++i)
{
T tA = a[i], tB = b[i];
if (tA == null)
{
if (tB != null) return false;
}
else if (tB == null) return false;
else if (!tA.Equals(tB)) return false;
}
return true;
}
}
}

View File

@@ -429,7 +429,7 @@ Clipboard.SetText(ObjectsToMessage(vLines, true));*/
if ((strFilePath != null) && (strFilePath.Length > 0))
str += strFilePath + MessageService.NewParagraph;
str += KLRes.FileSaveFailed;
str += KLRes.FileSaveFailed2;
if ((ex != null) && (ex.Message != null) && (ex.Message.Length > 0))
str += MessageService.NewParagraph + ex.Message;

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de>
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,8 +20,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using KeePassLib.Native;
@@ -33,9 +35,7 @@ namespace KeePassLib.Utility
/// </summary>
public static class UrlUtil
{
private static readonly char[] m_vDirSeps = new char[] {
'\\', '/', UrlUtil.LocalDirSepChar };
private static readonly char[] m_vPathTrimCharsWs = new char[] {
private static readonly char[] g_vPathTrimCharsWs = new char[] {
'\"', ' ', '\t', '\r', '\n' };
public static char LocalDirSepChar
@@ -43,6 +43,32 @@ namespace KeePassLib.Utility
get { return Path.DirectorySeparatorChar; }
}
private static char[] g_vDirSepChars = null;
private static char[] DirSepChars
{
get
{
if (g_vDirSepChars == null)
{
List<char> l = new List<char>();
l.Add('/'); // For URLs, also on Windows
// On Unix-like systems, '\\' is not a separator
if (!NativeLib.IsUnix()) l.Add('\\');
if (!l.Contains(UrlUtil.LocalDirSepChar))
{
Debug.Assert(false);
l.Add(UrlUtil.LocalDirSepChar);
}
g_vDirSepChars = l.ToArray();
}
return g_vDirSepChars;
}
}
/// <summary>
/// Get the directory (path) of a file name. The returned string may be
/// terminated by a directory separator character. Example:
@@ -63,16 +89,16 @@ namespace KeePassLib.Utility
bool bEnsureValidDirSpec)
{
Debug.Assert(strFile != null);
if(strFile == null) throw new ArgumentNullException("strFile");
if (strFile == null) throw new ArgumentNullException("strFile");
int nLastSep = strFile.LastIndexOfAny(m_vDirSeps);
if(nLastSep < 0) return string.Empty; // No directory
int nLastSep = strFile.LastIndexOfAny(UrlUtil.DirSepChars);
if (nLastSep < 0) return string.Empty; // No directory
if(bEnsureValidDirSpec && (nLastSep == 2) && (strFile[1] == ':') &&
if (bEnsureValidDirSpec && (nLastSep == 2) && (strFile[1] == ':') &&
(strFile[2] == '\\')) // Length >= 3 and Windows root directory
bAppendTerminatingChar = true;
if(!bAppendTerminatingChar) return strFile.Substring(0, nLastSep);
if (!bAppendTerminatingChar) return strFile.Substring(0, nLastSep);
return EnsureTerminatingSeparator(strFile.Substring(0, nLastSep),
(strFile[nLastSep] == '/'));
}
@@ -87,12 +113,12 @@ namespace KeePassLib.Utility
/// an empty string (<c>""</c>) if the input parameter is <c>null</c>.</returns>
public static string GetFileName(string strPath)
{
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
Debug.Assert(strPath != null); if (strPath == null) throw new ArgumentNullException("strPath");
int nLastSep = strPath.LastIndexOfAny(m_vDirSeps);
int nLastSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
if(nLastSep < 0) return strPath;
if(nLastSep >= (strPath.Length - 1)) return string.Empty;
if (nLastSep < 0) return strPath;
if (nLastSep >= (strPath.Length - 1)) return string.Empty;
return strPath.Substring(nLastSep + 1);
}
@@ -104,12 +130,12 @@ namespace KeePassLib.Utility
/// <returns>File name without extension.</returns>
public static string StripExtension(string strPath)
{
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
Debug.Assert(strPath != null); if (strPath == null) throw new ArgumentNullException("strPath");
int nLastDirSep = strPath.LastIndexOfAny(m_vDirSeps);
int nLastDirSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
int nLastExtDot = strPath.LastIndexOf('.');
if(nLastExtDot <= nLastDirSep) return strPath;
if (nLastExtDot <= nLastDirSep) return strPath;
return strPath.Substring(0, nLastExtDot);
}
@@ -121,13 +147,13 @@ namespace KeePassLib.Utility
/// <returns>Extension without prepending dot.</returns>
public static string GetExtension(string strPath)
{
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
Debug.Assert(strPath != null); if (strPath == null) throw new ArgumentNullException("strPath");
int nLastDirSep = strPath.LastIndexOfAny(m_vDirSeps);
int nLastDirSep = strPath.LastIndexOfAny(UrlUtil.DirSepChars);
int nLastExtDot = strPath.LastIndexOf('.');
if(nLastExtDot <= nLastDirSep) return string.Empty;
if(nLastExtDot == (strPath.Length - 1)) return string.Empty;
if (nLastExtDot <= nLastDirSep) return string.Empty;
if (nLastExtDot == (strPath.Length - 1)) return string.Empty;
return strPath.Substring(nLastExtDot + 1);
}
@@ -142,19 +168,16 @@ namespace KeePassLib.Utility
/// <returns>Path having a directory separator as last character.</returns>
public static string EnsureTerminatingSeparator(string strPath, bool bUrl)
{
Debug.Assert(strPath != null); if(strPath == null) throw new ArgumentNullException("strPath");
Debug.Assert(strPath != null); if (strPath == null) throw new ArgumentNullException("strPath");
int nLength = strPath.Length;
if(nLength <= 0) return string.Empty;
if (nLength <= 0) return string.Empty;
char chLast = strPath[nLength - 1];
if (Array.IndexOf<char>(UrlUtil.DirSepChars, chLast) >= 0)
return strPath;
for(int i = 0; i < m_vDirSeps.Length; ++i)
{
if(chLast == m_vDirSeps[i]) return strPath;
}
if(bUrl) return (strPath + '/');
if (bUrl) return (strPath + '/');
return (strPath + UrlUtil.LocalDirSepChar);
}
@@ -216,55 +239,78 @@ namespace KeePassLib.Utility
return false;
} */
internal static int IndexOfSecondEnclQuote(string str)
{
if (str == null) { Debug.Assert(false); return -1; }
if (str.Length <= 1) return -1;
if (str[0] != '\"') { Debug.Assert(false); return -1; }
if (NativeLib.IsUnix())
{
// Find non-escaped quote
string strFlt = str.Replace("\\\\", new string(
StrUtil.GetUnusedChar(str + "\\\""), 2)); // Same length
Match m = Regex.Match(strFlt, "[^\\\\]\\u0022");
int i = (((m != null) && m.Success) ? m.Index : -1);
return ((i >= 0) ? (i + 1) : -1); // Index of quote
}
// Windows does not allow quotes in folder/file names
return str.IndexOf('\"', 1);
}
public static string GetQuotedAppPath(string strPath)
{
if(strPath == null) { Debug.Assert(false); return string.Empty; }
// int nFirst = strPath.IndexOf('\"');
// int nSecond = strPath.IndexOf('\"', nFirst + 1);
// if((nFirst >= 0) && (nSecond >= 0))
// return strPath.Substring(nFirst + 1, nSecond - nFirst - 1);
// return strPath;
if (strPath == null) { Debug.Assert(false); return string.Empty; }
string str = strPath.Trim();
if(str.Length <= 1) return str;
if(str[0] != '\"') return str;
if (str.Length <= 1) return str;
if (str[0] != '\"') return str;
int iSecond = str.IndexOf('\"', 1);
if(iSecond <= 0) return str;
int iSecond = IndexOfSecondEnclQuote(str);
if (iSecond <= 0) return str;
return str.Substring(1, iSecond - 1);
}
public static string FileUrlToPath(string strUrl)
{
Debug.Assert(strUrl != null);
if(strUrl == null) throw new ArgumentNullException("strUrl");
if (strUrl == null) { Debug.Assert(false); throw new ArgumentNullException("strUrl"); }
if (strUrl.Length == 0) { Debug.Assert(false); return string.Empty; }
string str = strUrl;
if(str.StartsWith(@"file:///", StrUtil.CaseIgnoreCmp))
str = str.Substring(8, str.Length - 8);
if (!strUrl.StartsWith(Uri.UriSchemeFile + ":", StrUtil.CaseIgnoreCmp))
{
Debug.Assert(false);
return strUrl;
}
str = str.Replace('/', UrlUtil.LocalDirSepChar);
try
{
Uri uri = new Uri(strUrl);
string str = uri.LocalPath;
if (!string.IsNullOrEmpty(str)) return str;
}
catch (Exception) { Debug.Assert(false); }
return str;
Debug.Assert(false);
return strUrl;
}
public static bool UnhideFile(string strFile)
{
#if (KeePassLibSD || KeePassRT)
#if KeePassLibSD
return false;
#else
if(strFile == null) throw new ArgumentNullException("strFile");
if (strFile == null) throw new ArgumentNullException("strFile");
try
{
FileAttributes fa = File.GetAttributes(strFile);
if((long)(fa & FileAttributes.Hidden) == 0) return false;
if ((long)(fa & FileAttributes.Hidden) == 0) return false;
return HideFile(strFile, false);
}
catch(Exception) { }
catch (Exception) { }
return false;
#endif
@@ -272,26 +318,26 @@ namespace KeePassLib.Utility
public static bool HideFile(string strFile, bool bHide)
{
#if (KeePassLibSD || KeePassRT)
#if KeePassLibSD
return false;
#else
if(strFile == null) throw new ArgumentNullException("strFile");
if (strFile == null) throw new ArgumentNullException("strFile");
try
{
FileAttributes fa = File.GetAttributes(strFile);
if(bHide) fa = ((fa & ~FileAttributes.Normal) | FileAttributes.Hidden);
if (bHide) fa = ((fa & ~FileAttributes.Normal) | FileAttributes.Hidden);
else // Unhide
{
fa &= ~FileAttributes.Hidden;
if((long)fa == 0) fa = FileAttributes.Normal;
if ((long)fa == 0) fa = FileAttributes.Normal;
}
File.SetAttributes(strFile, fa);
return true;
}
catch(Exception) { }
catch (Exception) { }
return false;
#endif
@@ -299,48 +345,47 @@ namespace KeePassLib.Utility
public static string MakeRelativePath(string strBaseFile, string strTargetFile)
{
if(strBaseFile == null) throw new ArgumentNullException("strBasePath");
if(strTargetFile == null) throw new ArgumentNullException("strTargetPath");
if(strBaseFile.Length == 0) return strTargetFile;
if(strTargetFile.Length == 0) return string.Empty;
if (strBaseFile == null) throw new ArgumentNullException("strBasePath");
if (strTargetFile == null) throw new ArgumentNullException("strTargetPath");
if (strBaseFile.Length == 0) return strTargetFile;
if (strTargetFile.Length == 0) return string.Empty;
// Test whether on different Windows drives
if((strBaseFile.Length >= 3) && (strTargetFile.Length >= 3))
if ((strBaseFile.Length >= 3) && (strTargetFile.Length >= 3))
{
if((strBaseFile[1] == ':') && (strTargetFile[1] == ':') &&
if ((strBaseFile[1] == ':') && (strTargetFile[1] == ':') &&
(strBaseFile[2] == '\\') && (strTargetFile[2] == '\\') &&
(strBaseFile[0] != strTargetFile[0]))
return strTargetFile;
}
#if (!KeePassLibSD && !KeePassUAP)
if(NativeLib.IsUnix())
#endif
if (NativeLib.IsUnix())
{
#endif
bool bBaseUnc = IsUncPath(strBaseFile);
bool bTargetUnc = IsUncPath(strTargetFile);
if((!bBaseUnc && bTargetUnc) || (bBaseUnc && !bTargetUnc))
if ((!bBaseUnc && bTargetUnc) || (bBaseUnc && !bTargetUnc))
return strTargetFile;
string strBase = GetShortestAbsolutePath(strBaseFile);
string strTarget = GetShortestAbsolutePath(strTargetFile);
string[] vBase = strBase.Split(m_vDirSeps);
string[] vTarget = strTarget.Split(m_vDirSeps);
string[] vBase = strBase.Split(UrlUtil.DirSepChars);
string[] vTarget = strTarget.Split(UrlUtil.DirSepChars);
int i = 0;
while((i < (vBase.Length - 1)) && (i < (vTarget.Length - 1)) &&
while ((i < (vBase.Length - 1)) && (i < (vTarget.Length - 1)) &&
(vBase[i] == vTarget[i])) { ++i; }
StringBuilder sbRel = new StringBuilder();
for(int j = i; j < (vBase.Length - 1); ++j)
for (int j = i; j < (vBase.Length - 1); ++j)
{
if(sbRel.Length > 0) sbRel.Append(UrlUtil.LocalDirSepChar);
if (sbRel.Length > 0) sbRel.Append(UrlUtil.LocalDirSepChar);
sbRel.Append("..");
}
for(int k = i; k < vTarget.Length; ++k)
for (int k = i; k < vTarget.Length; ++k)
{
if(sbRel.Length > 0) sbRel.Append(UrlUtil.LocalDirSepChar);
if (sbRel.Length > 0) sbRel.Append(UrlUtil.LocalDirSepChar);
sbRel.Append(vTarget[k]);
}
@@ -352,28 +397,28 @@ namespace KeePassLib.Utility
{
const int nMaxPath = NativeMethods.MAX_PATH * 2;
StringBuilder sb = new StringBuilder(nMaxPath + 2);
if(NativeMethods.PathRelativePathTo(sb, strBaseFile, 0,
strTargetFile, 0) == false)
if (!NativeMethods.PathRelativePathTo(sb, strBaseFile, 0,
strTargetFile, 0))
return strTargetFile;
string str = sb.ToString();
while(str.StartsWith(".\\")) str = str.Substring(2, str.Length - 2);
while (str.StartsWith(".\\")) str = str.Substring(2, str.Length - 2);
return str;
}
catch(Exception) { Debug.Assert(false); }
catch (Exception) { Debug.Assert(false); }
return strTargetFile;
#endif
}
public static string MakeAbsolutePath(string strBaseFile, string strTargetFile)
{
if(strBaseFile == null) throw new ArgumentNullException("strBasePath");
if(strTargetFile == null) throw new ArgumentNullException("strTargetPath");
if(strBaseFile.Length == 0) return strTargetFile;
if(strTargetFile.Length == 0) return string.Empty;
if (strBaseFile == null) throw new ArgumentNullException("strBasePath");
if (strTargetFile == null) throw new ArgumentNullException("strTargetPath");
if (strBaseFile.Length == 0) return strTargetFile;
if (strTargetFile.Length == 0) return string.Empty;
if(IsAbsolutePath(strTargetFile)) return strTargetFile;
if (IsAbsolutePath(strTargetFile)) return strTargetFile;
string strBaseDir = GetFileDirectory(strBaseFile, true, false);
return GetShortestAbsolutePath(strBaseDir + strTargetFile);
@@ -381,55 +426,56 @@ namespace KeePassLib.Utility
public static bool IsAbsolutePath(string strPath)
{
if(strPath == null) throw new ArgumentNullException("strPath");
if(strPath.Length == 0) return false;
if (strPath == null) throw new ArgumentNullException("strPath");
if (strPath.Length == 0) return false;
if(IsUncPath(strPath)) return true;
if (IsUncPath(strPath)) return true;
try { return Path.IsPathRooted(strPath); }
catch(Exception) { Debug.Assert(false); }
catch (Exception) { Debug.Assert(false); }
return true;
}
public static string GetShortestAbsolutePath(string strPath)
{
if(strPath == null) throw new ArgumentNullException("strPath");
if(strPath.Length == 0) return string.Empty;
if (strPath == null) throw new ArgumentNullException("strPath");
if (strPath.Length == 0) return string.Empty;
// Path.GetFullPath is incompatible with UNC paths traversing over
// different server shares (which are created by PathRelativePathTo);
// we need to build the absolute path on our own...
if(IsUncPath(strPath))
if (IsUncPath(strPath))
{
char chSep = strPath[0];
Debug.Assert(Array.IndexOf<char>(m_vDirSeps, chSep) >= 0);
char[] vSep = ((chSep == '/') ? (new char[] { '/' }) :
(new char[] { '\\', '/' }));
List<string> l = new List<string>();
#if !KeePassLibSD
string[] v = strPath.Split(m_vDirSeps, StringSplitOptions.None);
string[] v = strPath.Split(vSep, StringSplitOptions.None);
#else
string[] v = strPath.Split(m_vDirSeps);
string[] v = strPath.Split(vSep);
#endif
Debug.Assert((v.Length >= 3) && (v[0].Length == 0) &&
(v[1].Length == 0));
foreach(string strPart in v)
foreach (string strPart in v)
{
if(strPart.Equals(".")) continue;
else if(strPart.Equals(".."))
if (strPart.Equals(".")) continue;
else if (strPart.Equals(".."))
{
if(l.Count > 0) l.RemoveAt(l.Count - 1);
if (l.Count > 0) l.RemoveAt(l.Count - 1);
else { Debug.Assert(false); }
}
else l.Add(strPart); // Do not ignore zero length parts
}
StringBuilder sb = new StringBuilder();
for(int i = 0; i < l.Count; ++i)
for (int i = 0; i < l.Count; ++i)
{
// Don't test length of sb, might be 0 due to initial UNC seps
if(i > 0) sb.Append(chSep);
if (i > 0) sb.Append(chSep);
sb.Append(l[i]);
}
@@ -438,20 +484,11 @@ namespace KeePassLib.Utility
}
string str;
try
{
#if KeePassRT
var dirT = Windows.Storage.StorageFolder.GetFolderFromPathAsync(
strPath).AwaitEx();
str = dirT.Path;
#else
str = Path.GetFullPath(strPath);
#endif
}
catch(Exception) { Debug.Assert(false); return strPath; }
try { str = Path.GetFullPath(strPath); }
catch (Exception) { Debug.Assert(false); return strPath; }
Debug.Assert(str.IndexOf("\\..\\") < 0);
foreach(char ch in m_vDirSeps)
Debug.Assert((str.IndexOf("\\..\\") < 0) || NativeLib.IsUnix());
foreach (char ch in UrlUtil.DirSepChars)
{
string strSep = new string(ch, 1);
str = str.Replace(strSep + "." + strSep, strSep);
@@ -462,17 +499,17 @@ namespace KeePassLib.Utility
public static int GetUrlLength(string strText, int nOffset)
{
if(strText == null) throw new ArgumentNullException("strText");
if(nOffset > strText.Length) throw new ArgumentException(); // Not >= (0 len)
if (strText == null) throw new ArgumentNullException("strText");
if (nOffset > strText.Length) throw new ArgumentException(); // Not >= (0 len)
int iPosition = nOffset, nLength = 0, nStrLen = strText.Length;
while(iPosition < nStrLen)
while (iPosition < nStrLen)
{
char ch = strText[iPosition];
++iPosition;
if((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
break;
++nLength;
@@ -481,24 +518,30 @@ namespace KeePassLib.Utility
return nLength;
}
internal static string GetScheme(string strUrl)
{
if (string.IsNullOrEmpty(strUrl)) return string.Empty;
int i = strUrl.IndexOf(':');
if (i > 0) return strUrl.Substring(0, i);
return string.Empty;
}
public static string RemoveScheme(string strUrl)
{
if(string.IsNullOrEmpty(strUrl)) return string.Empty;
if (string.IsNullOrEmpty(strUrl)) return string.Empty;
int nNetScheme = strUrl.IndexOf(@"://", StrUtil.CaseIgnoreCmp);
int nShScheme = strUrl.IndexOf(@":/", StrUtil.CaseIgnoreCmp);
int nSmpScheme = strUrl.IndexOf(@":", StrUtil.CaseIgnoreCmp);
int i = strUrl.IndexOf(':');
if (i < 0) return strUrl; // No scheme to remove
++i;
if((nNetScheme < 0) && (nShScheme < 0) && (nSmpScheme < 0))
return strUrl; // No scheme
// A single '/' indicates a path (absolute) and should not be removed
if (((i + 1) < strUrl.Length) && (strUrl[i] == '/') &&
(strUrl[i + 1] == '/'))
i += 2; // Skip authority prefix
int nMin = Math.Min(Math.Min((nNetScheme >= 0) ? nNetScheme : int.MaxValue,
(nShScheme >= 0) ? nShScheme : int.MaxValue),
(nSmpScheme >= 0) ? nSmpScheme : int.MaxValue);
if(nMin == nNetScheme) return strUrl.Substring(nMin + 3);
if(nMin == nShScheme) return strUrl.Substring(nMin + 2);
return strUrl.Substring(nMin + 1);
return strUrl.Substring(i);
}
public static string ConvertSeparators(string strPath)
@@ -508,7 +551,7 @@ namespace KeePassLib.Utility
public static string ConvertSeparators(string strPath, char chSeparator)
{
if(string.IsNullOrEmpty(strPath)) return string.Empty;
if (string.IsNullOrEmpty(strPath)) return string.Empty;
strPath = strPath.Replace('/', chSeparator);
strPath = strPath.Replace('\\', chSeparator);
@@ -518,33 +561,61 @@ namespace KeePassLib.Utility
public static bool IsUncPath(string strPath)
{
if(strPath == null) throw new ArgumentNullException("strPath");
if (strPath == null) throw new ArgumentNullException("strPath");
return (strPath.StartsWith("\\\\") || strPath.StartsWith("//"));
}
public static string FilterFileName(string strName)
{
if(strName == null) { Debug.Assert(false); return string.Empty; }
if (string.IsNullOrEmpty(strName)) { Debug.Assert(false); return string.Empty; }
string str = strName;
// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
str = str.Replace('/', '-');
str = str.Replace('\\', '-');
str = str.Replace(":", string.Empty);
str = str.Replace("*", string.Empty);
str = str.Replace("?", string.Empty);
str = str.Replace("\"", string.Empty);
str = str.Replace(@"'", string.Empty);
str = str.Replace('<', '(');
str = str.Replace('>', ')');
str = str.Replace('|', '-');
StringBuilder sb = new StringBuilder(strName.Length);
foreach (char ch in strName)
{
if (ch < '\u0020') continue;
return str;
switch (ch)
{
case '\"':
case '*':
case ':':
case '?':
break;
case '/':
case '\\':
case '|':
sb.Append('-');
break;
case '<':
sb.Append('(');
break;
case '>':
sb.Append(')');
break;
default: sb.Append(ch); break;
}
}
// Trim trailing spaces and periods
for (int i = sb.Length - 1; i >= 0; --i)
{
char ch = sb[i];
if ((ch == ' ') || (ch == '.')) sb.Remove(i, 1);
else break;
}
return sb.ToString();
}
/// <summary>
/// Get the host component of an URL.
/// Get the host component of a URL.
/// This method is faster and more fault-tolerant than creating
/// an <code>Uri</code> object and querying its <code>Host</code>
/// property.
@@ -555,60 +626,60 @@ namespace KeePassLib.Utility
/// </example>
public static string GetHost(string strUrl)
{
if(strUrl == null) { Debug.Assert(false); return string.Empty; }
if (strUrl == null) { Debug.Assert(false); return string.Empty; }
StringBuilder sb = new StringBuilder();
bool bInExtHost = false;
for(int i = 0; i < strUrl.Length; ++i)
for (int i = 0; i < strUrl.Length; ++i)
{
char ch = strUrl[i];
if(bInExtHost)
if (bInExtHost)
{
if(ch == '/')
if (ch == '/')
{
if(sb.Length == 0) { } // Ignore leading '/'s
if (sb.Length == 0) { } // Ignore leading '/'s
else break;
}
else sb.Append(ch);
}
else // !bInExtHost
{
if(ch == ':') bInExtHost = true;
if (ch == ':') bInExtHost = true;
}
}
string str = sb.ToString();
if(str.Length == 0) str = strUrl;
if (str.Length == 0) str = strUrl;
// Remove the login part
int nLoginLen = str.IndexOf('@');
if(nLoginLen >= 0) str = str.Substring(nLoginLen + 1);
if (nLoginLen >= 0) str = str.Substring(nLoginLen + 1);
// Remove the port
int iPort = str.LastIndexOf(':');
if(iPort >= 0) str = str.Substring(0, iPort);
if (iPort >= 0) str = str.Substring(0, iPort);
return str;
}
public static bool AssemblyEquals(string strExt, string strShort)
{
if((strExt == null) || (strShort == null)) { Debug.Assert(false); return false; }
if ((strExt == null) || (strShort == null)) { Debug.Assert(false); return false; }
if(strExt.Equals(strShort, StrUtil.CaseIgnoreCmp) ||
if (strExt.Equals(strShort, StrUtil.CaseIgnoreCmp) ||
strExt.StartsWith(strShort + ",", StrUtil.CaseIgnoreCmp))
return true;
if(!strShort.EndsWith(".dll", StrUtil.CaseIgnoreCmp))
if (!strShort.EndsWith(".dll", StrUtil.CaseIgnoreCmp))
{
if(strExt.Equals(strShort + ".dll", StrUtil.CaseIgnoreCmp) ||
if (strExt.Equals(strShort + ".dll", StrUtil.CaseIgnoreCmp) ||
strExt.StartsWith(strShort + ".dll,", StrUtil.CaseIgnoreCmp))
return true;
}
if(!strShort.EndsWith(".exe", StrUtil.CaseIgnoreCmp))
if (!strShort.EndsWith(".exe", StrUtil.CaseIgnoreCmp))
{
if(strExt.Equals(strShort + ".exe", StrUtil.CaseIgnoreCmp) ||
if (strExt.Equals(strShort + ".exe", StrUtil.CaseIgnoreCmp) ||
strExt.StartsWith(strShort + ".exe,", StrUtil.CaseIgnoreCmp))
return true;
}
@@ -619,7 +690,7 @@ namespace KeePassLib.Utility
public static string GetTempPath()
{
string strDir;
if(NativeLib.IsUnix())
if (NativeLib.IsUnix())
strDir = NativeMethods.GetUserRuntimeDir();
#if KeePassUAP
else strDir = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
@@ -629,9 +700,9 @@ namespace KeePassLib.Utility
try
{
if(!Directory.Exists(strDir)) Directory.CreateDirectory(strDir);
if (!Directory.Exists(strDir)) Directory.CreateDirectory(strDir);
}
catch(Exception) { Debug.Assert(false); }
catch (Exception) { Debug.Assert(false); }
return strDir;
}
@@ -642,31 +713,29 @@ namespace KeePassLib.Utility
SearchOption opt)
{
List<string> l = new List<string>();
if(strDir == null) { Debug.Assert(false); return l; }
if(strPattern == null) { Debug.Assert(false); return l; }
if (strDir == null) { Debug.Assert(false); return l; }
if (strPattern == null) { Debug.Assert(false); return l; }
string[] v = Directory.GetFiles(strDir, strPattern, opt);
if(v == null) { Debug.Assert(false); return l; }
if (v == null) { Debug.Assert(false); return l; }
// Only accept files with the correct extension; GetFiles may
// return additional files, see GetFiles documentation
string strExt = GetExtension(strPattern);
if(!string.IsNullOrEmpty(strExt) && (strExt.IndexOf('*') < 0) &&
if (!string.IsNullOrEmpty(strExt) && (strExt.IndexOf('*') < 0) &&
(strExt.IndexOf('?') < 0))
{
strExt = "." + strExt;
foreach(string strPathRaw in v)
foreach (string strPathRaw in v)
{
if(strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(m_vPathTrimCharsWs);
if(strPath.Length == 0) { Debug.Assert(false); continue; }
if (strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(g_vPathTrimCharsWs);
if (strPath.Length == 0) { Debug.Assert(false); continue; }
Debug.Assert(strPath == strPathRaw);
if(!strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
continue;
l.Add(strPathRaw);
if (strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
l.Add(strPathRaw);
}
}
else l.AddRange(v);
@@ -679,33 +748,31 @@ namespace KeePassLib.Utility
SearchOption opt)
{
List<FileInfo> l = new List<FileInfo>();
if(di == null) { Debug.Assert(false); return l; }
if(strPattern == null) { Debug.Assert(false); return l; }
if (di == null) { Debug.Assert(false); return l; }
if (strPattern == null) { Debug.Assert(false); return l; }
FileInfo[] v = di.GetFiles(strPattern, opt);
if(v == null) { Debug.Assert(false); return l; }
if (v == null) { Debug.Assert(false); return l; }
// Only accept files with the correct extension; GetFiles may
// return additional files, see GetFiles documentation
string strExt = GetExtension(strPattern);
if(!string.IsNullOrEmpty(strExt) && (strExt.IndexOf('*') < 0) &&
if (!string.IsNullOrEmpty(strExt) && (strExt.IndexOf('*') < 0) &&
(strExt.IndexOf('?') < 0))
{
strExt = "." + strExt;
foreach(FileInfo fi in v)
foreach (FileInfo fi in v)
{
if(fi == null) { Debug.Assert(false); continue; }
if (fi == null) { Debug.Assert(false); continue; }
string strPathRaw = fi.FullName;
if(strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(m_vPathTrimCharsWs);
if(strPath.Length == 0) { Debug.Assert(false); continue; }
if (strPathRaw == null) { Debug.Assert(false); continue; }
string strPath = strPathRaw.Trim(g_vPathTrimCharsWs);
if (strPath.Length == 0) { Debug.Assert(false); continue; }
Debug.Assert(strPath == strPathRaw);
if(!strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
continue;
l.Add(fi);
if (strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
l.Add(fi);
}
}
else l.AddRange(v);
@@ -713,5 +780,82 @@ namespace KeePassLib.Utility
return l;
}
#endif
public static char GetDriveLetter(string strPath)
{
if (strPath == null) throw new ArgumentNullException("strPath");
Debug.Assert(default(char) == '\0');
if (strPath.Length < 3) return '\0';
if ((strPath[1] != ':') || (strPath[2] != '\\')) return '\0';
char ch = char.ToUpperInvariant(strPath[0]);
return (((ch >= 'A') && (ch <= 'Z')) ? ch : '\0');
}
internal static string GetSafeFileName(string strName)
{
Debug.Assert(!string.IsNullOrEmpty(strName));
string str = FilterFileName(GetFileName(strName ?? string.Empty));
if (string.IsNullOrEmpty(str))
{
Debug.Assert(false);
return "File.dat";
}
return str;
}
internal static string GetCanonicalUri(string strUri)
{
if (string.IsNullOrEmpty(strUri)) { Debug.Assert(false); return strUri; }
try
{
Uri uri = new Uri(strUri);
if (uri.IsAbsoluteUri) return uri.AbsoluteUri;
else { Debug.Assert(false); }
}
catch (Exception) { Debug.Assert(false); }
return strUri;
}
/* internal static Dictionary<string, string> ParseQuery(string strQuery)
{
Dictionary<string, string> d = new Dictionary<string, string>();
if(string.IsNullOrEmpty(strQuery)) return d;
string[] vKvp = strQuery.Split(new char[] { '?', '&' });
if(vKvp == null) { Debug.Assert(false); return d; }
foreach(string strKvp in vKvp)
{
if(string.IsNullOrEmpty(strKvp)) continue;
string strKey, strValue;
int iSep = strKvp.IndexOf('=');
if(iSep < 0)
{
strKey = strKvp;
strValue = string.Empty;
}
else
{
strKey = strKvp.Substring(0, iSep);
strValue = strKvp.Substring(iSep + 1);
}
strKey = Uri.UnescapeDataString(strKey);
strValue = Uri.UnescapeDataString(strValue);
d[strKey] = strValue;
}
return d;
} */
}
}

View File

@@ -249,28 +249,11 @@ namespace keepass2android.Io
reason.Result = UiStringKey.ReadOnlyReason_PreKitKat;
return true;
}
//KitKat or later...
var uri = Android.Net.Uri.Parse(ioc.Path);
cursor = _ctx.ContentResolver.Query(uri, null, null, null, null, null);
if (cursor != null && cursor.MoveToFirst())
{
int column = cursor.GetColumnIndex(DocumentsContract.Document.ColumnFlags);
if (column < 0)
return false; //seems like this is not supported. See below for reasoning to return false.
int flags = cursor.GetInt(column);
Kp2aLog.Log("File flags: " + flags);
if ((flags & (long) DocumentContractFlags.SupportsWrite) == 0)
{
if (reason != null)
reason.Result = UiStringKey.ReadOnlyReason_ReadOnlyFlag;
return true;
}
else return false;
}
else return false;
//in previous implementations, we were checking for FLAG_SUPPORTS_WRITE in the document flags,
//but it seems like this is very poorly supported, e.g. Dropbox and OneDrive return !FLAG_SUPPORTS_WRITE
//even though writing work.
return false;
}
catch (Exception e)
{

View File

@@ -322,20 +322,37 @@ namespace keepass2android.Io
private async Task<IGraphServiceClient> TryGetMsGraphClient(String path, bool tryConnect)
{
String userId = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(path).User.Id;
logDebug("TryGetMsGraphClient for " + userId);
if (mClientByUser.ContainsKey(userId))
{
logDebug("TryGetMsGraphClient found user " + userId);
GraphServiceClientWithState clientWithState = mClientByUser[userId];
if (!(clientWithState.RequiresUserInteraction || (clientWithState.TokenExpiryDate < DateTime.Now) || (clientWithState.Client == null)))
if (!(clientWithState.RequiresUserInteraction || (clientWithState.TokenExpiryDate < DateTime.Now) ||
(clientWithState.Client == null)))
{
logDebug("TryGetMsGraphClient returning client");
return clientWithState.Client;
}
else
{
logDebug("not returning client because " + clientWithState.RequiresUserInteraction + " " +
(clientWithState.TokenExpiryDate < DateTime.Now) + " " + (clientWithState.Client == null));
}
}
if (tryConnect)
{
logDebug("trying to connect...");
if (await TryLoginSilent(path) != null)
{
logDebug("trying to connect ok");
return mClientByUser[userId].Client;
}
logDebug("trying to connect failed");
}
logDebug("TryGetMsGraphClient for " + userId + " returns null");
return null;
}
@@ -367,7 +384,7 @@ namespace keepass2android.Io
if (authenticationResult.Account == null)
throw new Exception("authenticationResult.Account == null!");
mClientByUser[authenticationResult.Account.HomeAccountId.Identifier] = clientWithState;
logDebug("buildClient ok.");
return clientWithState.Client;
}
@@ -375,7 +392,9 @@ namespace keepass2android.Io
private void logDebug(string str)
{
Log.Debug("KP2A", str);
#if DEBUG
Log.Debug("KP2A", "OneDrive2: " + str);
#endif
}
@@ -530,13 +549,50 @@ namespace keepass2android.Io
{
Task.Run(async () =>
{
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(path);
PathItemBuilder pathItemBuilder = await GetPathItemBuilder(path);
//for small files <2MB use the direct upload:
if (stream.Length < 2* 1024 * 1024)
{
return await
pathItemBuilder
.getPathItem()
.Content
.Request()
.PutAsync<DriveItem>(stream);
}
//for larger files use an upload session. This is required for 4MB and beyond, but as the docs are not very clear about this
//limit, let's use it a bit more often to be safe.
var uploadProps = new DriveItemUploadableProperties
{
ODataType = null,
AdditionalData = new Dictionary<string, object>
{
{ "@microsoft.graph.conflictBehavior", "replace" }
}
};
var uploadSession = await pathItemBuilder
.getPathItem()
.CreateUploadSession(uploadProps)
.Request()
.PostAsync();
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask = new LargeFileUploadTask<DriveItem>(uploadSession, stream, maxSliceSize);
var uploadResult = await fileUploadTask.UploadAsync();
if (!uploadResult.UploadSucceeded)
{
throw new Exception("Failed to upload data!");
}
return uploadResult.ItemResponse;
}).Wait();
@@ -821,14 +877,17 @@ namespace keepass2android.Io
public async void OnStart(IFileStorageSetupActivity activity)
{
logDebug("OneDrive2.OnStart");
if (activity.ProcessName.Equals(FileStorageSetupDefs.ProcessNameFileUsageSetup))
activity.State.PutString(FileStorageSetupDefs.ExtraPath, activity.Ioc.Path);
string rootPathForUser = await TryLoginSilent(activity.Ioc.Path);
if (rootPathForUser != null)
{
logDebug("rootPathForUser not null");
FinishActivityWithSuccess(activity, rootPathForUser);
return;
}
logDebug("rootPathForUser null");
try
{
@@ -856,13 +915,14 @@ namespace keepass2android.Io
private async Task<string> TryLoginSilent(string iocPath)
{
logDebug("Login Silent for " + iocPath);
IAccount account = null;
try
{
if (IsConnected(iocPath))
{
logDebug("Login Silent ok, connected");
return iocPath;
}
String userId = OneDrive2ItemLocation<OneDrive2PrefixContainerType>.FromString(iocPath).User?.Id;
@@ -891,7 +951,9 @@ namespace keepass2android.Io
/*User me = await graphClient.Me.Request().WithForceRefresh(true).GetAsync();
logDebug("received name " + me.DisplayName);*/
return BuildRootPathForUser(authResult);
var rootFolder = BuildRootPathForUser(authResult);
logDebug("Found RootPath for user");
return rootFolder;
}
catch (MsalUiRequiredException ex)

View File

@@ -1,11 +1,10 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
// This code was generated by a tool.
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
@@ -15,7 +14,7 @@ namespace keepass2android
{
[System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
public partial class Resource
{

View File

@@ -1,18 +1,20 @@
cd ..\java\JavaFileStorageTest-AS
./gradlew clean assemble
cd ..\..\build-scripts
cd ..\KP2ASoftkeyboard_AS
cd ..\java\KP2ASoftkeyboard_AS
./gradlew clean assemble
cd ..\..\build-scripts
cd ..\Keepass2AndroidPluginSDK2
cd ..\java\Keepass2AndroidPluginSDK2
./gradlew clean assemble
cd ..\..\build-scripts
cd ..\KP2AKdbLibrary
cd ..\java\KP2AKdbLibrary
./gradlew clean assemble
cd ..\..\build-scripts
cd ..\PluginQR
cd ..\java\PluginQR
./gradlew clean assemble
cd ..\..\build-scripts

View File

@@ -26,7 +26,7 @@ NOTE: If you change dependencies here, don't forget to update the jar files in J
*/
dependencies {
compile 'com.android.support:appcompat-v7:28.0.0'
compile 'com.squareup.okhttp3:okhttp:4.10.0-RC1'
compile 'com.burgstaller:okhttp-digest:2.5'
compile 'com.google.android.gms:play-services:4.0.30'
@@ -35,7 +35,7 @@ dependencies {
exclude group: 'com.google.android.google-play-services'
}
compile 'com.google.apis:google-api-services-drive:v2-rev102-1.16.0-rc'
compile 'com.dropbox.core:dropbox-core-sdk:3.1.1'
compile 'com.dropbox.core:dropbox-core-sdk:4.0.0'
//onedrive:
compile('com.onedrive.sdk:onedrive-sdk-android:1.2.0') {
transitive = false

View File

@@ -7,6 +7,8 @@ import com.dropbox.core.DbxOAuth1Upgrader;
import com.dropbox.core.DbxRequestConfig;
import com.dropbox.core.InvalidAccessTokenException;
import com.dropbox.core.android.Auth;
import com.dropbox.core.json.JsonReadException;
import com.dropbox.core.oauth.DbxCredential;
import com.dropbox.core.v2.DbxClientV2;
import com.dropbox.core.http.OkHttp3Requestor;
import com.dropbox.core.v2.files.DeleteErrorException;
@@ -25,6 +27,7 @@ import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -39,17 +42,27 @@ import android.util.Log;
import android.widget.Toast;
class DbxRequestConfigFactory {
private static DbxRequestConfig sDbxRequestConfig;
public static DbxRequestConfig getRequestConfig() {
if (sDbxRequestConfig == null) {
sDbxRequestConfig = DbxRequestConfig.newBuilder("Keepass2Android")
.withHttpRequestor(new OkHttp3Requestor(OkHttp3Requestor.defaultOkHttpClient()))
.build();
}
return sDbxRequestConfig;
}
}
/**
* Created by Philipp on 18.11.2016.
*/
public class DropboxV2Storage extends JavaFileStorageBase
{
private List<String> scope = new ArrayList<String>(Arrays.asList("account_info.read", "files.metadata.write","files.content.write","files.content.read"));
private DbxAppInfo appInfo;
public void bla()
{
}
DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder("kp2a")
.withHttpRequestor(new OkHttp3Requestor(OkHttp3Requestor.defaultOkHttpClient()))
.build();
@@ -60,6 +73,8 @@ public class DropboxV2Storage extends JavaFileStorageBase
final static private String ACCESS_KEY_V1_NAME = "ACCESS_KEY";
final static private String ACCESS_SECRET_V1_NAME = "ACCESS_SECRET";
final static private String ACCESS_TOKEN_NAME = "ACCESS_TOKEN_V2";
//key for serialized dropbox credentials (used to store access + refresh tokens as long-living access tokens (v2) have been deprecated
final static private String SERIALIZED_CREDENTIALS = "SERIALIZED_CREDENTIALS";
private boolean mLoggedIn = false;
private Context mContext;
@@ -101,7 +116,9 @@ public class DropboxV2Storage extends JavaFileStorageBase
}
private void initialize(Context ctx, String _appKey, String _appSecret,
boolean clearKeysOnStart, AccessType accessType) {
boolean clearKeysOnStart, AccessType accessType)
{
Log.d("KP2A","Initializing Dropbox storage. Update for use with short-lived access tokens.");
appInfo = new DbxAppInfo(_appKey,_appSecret);
mContext = ctx;
@@ -117,7 +134,7 @@ public class DropboxV2Storage extends JavaFileStorageBase
public boolean tryConnect(Activity activity)
{
if (!mLoggedIn)
Auth.startOAuth2Authentication(activity, appInfo.getKey());
Auth.startOAuth2PKCE(activity, appInfo.getKey(), DbxRequestConfigFactory.getRequestConfig(), scope);
return mLoggedIn;
}
@@ -252,6 +269,22 @@ public class DropboxV2Storage extends JavaFileStorageBase
}
private DbxCredential getStoredCredential(){
SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
String serialized = prefs.getString(SERIALIZED_CREDENTIALS, null);
if (serialized == null)
return null;
try {
return DbxCredential.Reader.readFully(serialized);
} catch (JsonReadException e) {
return null;
}
}
//stores a long-living access token from API v2
//New tokens of this kind will no longer be issued, but we have a v1-updater which converts v1 to v2 tokens so we should still be able to store them.
private void storeKey(String v2token) {
Log.d(TAG, "Storing Dropbox accessToken");
// Save the access key for later
@@ -270,6 +303,18 @@ public class DropboxV2Storage extends JavaFileStorageBase
private void buildSession() {
DbxCredential credential = getStoredCredential();
if (credential != null)
{
credential = new DbxCredential(credential.getAccessToken(), -1L, credential.getRefreshToken(), credential.getAppKey());
dbxClient = new DbxClientV2(DbxRequestConfigFactory.getRequestConfig(), credential);
setLoggedIn(true);
return;
}
String v2Token = getKeyV2();
if (v2Token != null)
@@ -548,13 +593,13 @@ public class DropboxV2Storage extends JavaFileStorageBase
if (storageSetupAct.getState().containsKey("hasStartedAuth")) {
Log.d("KP2AJ", "auth started");
String v2Token = Auth.getOAuth2Token();
DbxCredential dbxCredential = Auth.getDbxCredential();
if (v2Token != null) {
if (dbxCredential != null) {
Log.d("KP2AJ", "auth successful");
try {
storeKey(v2Token);
storeCredentials(dbxCredential);
buildSession();
finishActivityWithSuccess(activity);
return;
@@ -574,12 +619,25 @@ public class DropboxV2Storage extends JavaFileStorageBase
((Activity) activity).finish();
} else {
Log.d("KP2AJ", "Starting auth");
Auth.startOAuth2Authentication((Activity) activity, appInfo.getKey());
Auth.startOAuth2PKCE((Activity) activity, appInfo.getKey(), DbxRequestConfigFactory.getRequestConfig(), scope);
Log.d("KP2AJ", "Started auth");
storageSetupAct.getState().putBoolean("hasStartedAuth", true);
Log.d("KP2AJ", "add state flag");
}
}
private void storeCredentials(DbxCredential dbxCredential)
{
Log.d(TAG, "Storing Dropbox credentials");
// Save the access key for later
SharedPreferences prefs = mContext.getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
Editor edit = prefs.edit();
edit.putString(SERIALIZED_CREDENTIALS, dbxCredential.toString());
edit.commit();
}
@Override
public void onStart(FileStorageSetupActivity activity) {

View File

@@ -247,15 +247,21 @@ public class PCloudFileStorage extends JavaFileStorageBase
}
private void handleAuthResult(FileStorageSetupActivity activity, AuthorizationData authorizationData) {
if (authorizationData.result == AuthorizationResult.ACCESS_GRANTED) {
String authToken = authorizationData.token;
String apiHost = authorizationData.apiHost;
setAuthToken(authToken, apiHost);
finishActivityWithSuccess(activity);
} else {
android.util.Log.d("KP2A", "Auth failed with " + authorizationData.result.toString() + ", code=" + authorizationData.authCode + ", error=" + authorizationData.errorMessage);
Activity castedActivity = (Activity)activity;
Intent resultData = new Intent();
resultData.putExtra(EXTRA_ERROR_MESSAGE, "Authentication failed.");
resultData.putExtra(EXTRA_ERROR_MESSAGE, "Authentication failed!");
//reset any stored token in case we have an invalid one
clearAuthToken();
castedActivity.setResult(Activity.RESULT_CANCELED, resultData);
castedActivity.finish();
}

View File

@@ -145,6 +145,7 @@ import java.util.ArrayList;
import java.util.List;
//import keepass2android.javafilestorage.DropboxCloudRailStorage;
import keepass2android.javafilestorage.DropboxV2Storage;
import keepass2android.javafilestorage.JavaFileStorage;
import keepass2android.javafilestorage.JavaFileStorage.FileEntry;
import keepass2android.javafilestorage.PCloudFileStorage;
@@ -539,7 +540,7 @@ 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, "yCeH59Ffgtm");
//storageToTest = new SkyDriveFileStorage("000000004010C234", appContext);
@@ -556,7 +557,7 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
}
});*/
//storageToTest = new DropboxV2Storage(ctx,"4ybka4p4a1027n6", "1z5lv528un9nre8", !simulateRestart);
storageToTest = new DropboxV2Storage(ctx,"4ybka4p4a1027n6", "3s86datjhkihwyc", !simulateRestart);
//storageToTest = new DropboxFileStorage(ctx,"4ybka4p4a1027n6", "1z5lv528un9nre8", !simulateRestart);
//storageToTest = new DropboxAppFolderFileStorage(ctx,"ax0268uydp1ya57", "3s86datjhkihwyc", true);

View File

@@ -1,11 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<compositeConfiguration>
<compositeBuild compositeDefinitionSource="SCRIPT" />
</compositeConfiguration>
<option name="delegatedBuild" value="false" />
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="C:\Program Files\Android\Android Studio1\gradle\gradle-2.10" />
@@ -17,7 +20,7 @@
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
<option name="testRunner" value="PLATFORM" />
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings>
</option>
</component>

View File

@@ -627,6 +627,10 @@ public class KP2AKeyboard extends InputMethodService
updateShowKp2aMode();
Log.d("KP2AK", "updateKeyboardMode -> setKM");
Log.d("KP2AK", "variation = " + variation);
Log.d("KP2AK", "input type = " + attribute.inputType);
if ((mShowKp2aKeyboard) && (mKp2aEnableSimpleKeyboard))
{
mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_KP2A, attribute.imeOptions);

View File

@@ -83,6 +83,11 @@
<!-- Option to always hide the settings key -->
<string name="settings_key_mode_always_hide_name">إخفاء دومًا</string>
<!-- Array of the settings key modes -->
<string-array name="settings_key_modes">
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_always_show_name</item>
<item>@string/settings_key_mode_always_hide_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">اقتراحات ثنائية</string>
<!-- Description for auto completion -->
@@ -133,9 +138,11 @@
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="label_symbol_key">\?123</string>
<!-- Label for "switch to numeric" key. Must be short to fit on key! -->
<string name="label_phone_key">123</string>
<!-- Label for "switch to alphabetic" key. Must be short to fit on key! -->
<string name="label_alpha_key">ب ت ث</string>
<!-- Label for ALT modifier key. Must be short to fit on key! -->
<string name="label_alt_key">ALT</string>
<!-- Voice related labels -->
<!-- Title of the warning dialog that shows when a user initiates voice input for
the first time. -->
@@ -247,4 +254,5 @@
<string name="subtype_mode_keyboard">لوحة مفاتيح</string>
<string name="subtype_mode_voice">صوت</string>
<!-- Title for Latin keyboard debug settings activity / dialog -->
<string name="kp2a_nextfields"><![CDATA[>]]></string>
</resources>

View File

@@ -19,122 +19,230 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Title for Latin keyboard -->
<string name="english_ime_name">Keepass2Android klaviaturası</string>
<!-- Title for Latin keyboard settings activity / dialog -->
<string name="english_ime_settings">Android klaviatura tənzimləmələri</string>
<!-- Title for Latin keyboard input options dialog -->
<string name="english_ime_input_options">Giriş seçimləri</string>
<!-- Option to provide vibrate/haptic feedback on keypress -->
<string name="vibrate_on_keypress">Basanda titrəmə</string>
<!-- Option to play back sound on keypress in soft keyboard -->
<string name="sound_on_keypress">Basanda səs vermə</string>
<!-- Option to pop up the character with a larger font above soft keyboard -->
<string name="popup_on_keypress">Basanda pəncərə açılması</string>
<!-- Option to enable using nearby keys when correcting/predicting -->
<string name="hit_correction">Yazılış xətalarını düzəlt</string>
<!-- Description for hit_correction -->
<string name="hit_correction_summary">Giriş xətasının düzəldilməsini fəallaşdır</string>
<!-- Option to enable using nearby keys when correcting/predicting in landscape-->
<string name="hit_correction_land">Üfüqi giriş xətaları</string>
<!-- Description for hit_correction in landscape -->
<string name="hit_correction_land_summary">Giriş xətasının düzəldilməsini fəallaşdır</string>
<!-- Option to automatically correct word on hitting space -->
<string name="auto_correction">Söz təklifləri</string>
<!-- Description for auto_correction -->
<string name="auto_correction_summary">Əvvəlki sözün avto-düzəlişi</string>
<!-- Option to enable text prediction -->
<string name="prediction">Söz təklifləri</string>
<!-- Category title for text prediction -->
<string name="prediction_category">Söz təklifi tənzimləmələri</string>
<!-- Description for text prediction -->
<string name="prediction_summary">Yazarkən avto-tamamlamanı fəallaşdır</string>
<!-- Dialog title for auto complete choices -->
<string name="auto_complete_dialog_title">Avtomatik tamamlama</string>
<!-- Option to enable text prediction in landscape -->
<string name="prediction_landscape">Mətn sahə ölçüsünü artır</string>
<!-- Description for text prediction -->
<string name="prediction_landscape_summary">Üfüqi rejimdə mətn təkliflərini gizlət</string>
<!-- Option to enable auto capitalization of sentences -->
<string name="auto_cap">Avto-böyük hərf</string>
<!-- Description for auto cap -->
<string name="auto_cap_summary">Cümləni böyük hərflə başlat</string>
<!-- Option to enable auto punctuate -->
<string name="auto_punctuate">Avto-nöqtələmə</string>
<!-- Description for auto punctuate -->
<!-- Option to enable quick fixes -->
<string name="quick_fixes">Cəld bərpa</string>
<!-- Description for quick fixes -->
<string name="quick_fixes_summary">Ən çox edilən yazılış səhvlərini düzəldir</string>
<!-- Option to enable showing suggestions -->
<string name="show_suggestions">Təklifləri göstər</string>
<!-- Description for show suggestions -->
<string name="show_suggestions_summary">Yazarkən təklif edilən sözləri görüntülə</string>
<!-- Option to enable auto completion -->
<string name="auto_complete">Avtomatik tamamla</string>
<!-- Description for auto completion -->
<string name="auto_complete_summary">Boşluq və durğu işarələrini vurğulanan sözə avtomatik əlavə edər</string>
<!-- Option to show/hide the settings key -->
<string name="prefs_settings_key">Tənzimləmələr açarını göstər</string>
<!-- Array of the settings key mode values -->
<!-- Option to automatically decide to show/hide the settings key -->
<string name="settings_key_mode_auto_name">Avtomatik</string>
<!-- Option to always show the settings key -->
<string name="settings_key_mode_always_show_name">Həmişə göstər</string>
<!-- Option to always hide the settings key -->
<string name="settings_key_mode_always_hide_name">Həmişə gizlət</string>
<!-- Array of the settings key modes -->
<string-array name="settings_key_modes">
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_always_show_name</item>
<item>@string/settings_key_mode_always_hide_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Bigram Təklifləri</string>
<!-- Description for auto completion -->
<string name="bigram_suggestion_summary">Təklifi təkmilləşdirmək üçün əvvəlki sözü istifadə et</string>
<!-- Array of prediction modes -->
<string-array name="prediction_modes">
<item>Heç biri</item>
<item>Təməl</item>
<item>Qabaqcıl</item>
</string-array>
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Indicates that a word has been added to the dictionary -->
<string name="added_word"><xliff:g id="word">%s</xliff:g> : Saxlanıldı</string>
<!-- Tip to long press on keys -->
<string name="tip_long_press">Vurğuları görmək üçün bir düyməyə basılı saxlayın (ø, ö və s.)</string>
<!-- Tip to dismiss keyboard -->
<string name="tip_dismiss">Klaviaturanı hər hansısa bir nöqtədə bağlamaq üçün \u21B6 geri düyməsinə bas</string>
<!-- Tip to press ?123 to access numbers and symbols -->
<string name="tip_access_symbols">Nömrələrə və simvollara müraciət</string>
<!-- Tip to long press on typed word to add to dictionary -->
<string name="tip_add_to_dictionary">Lüğətə əlavə etmək üçün ən soldakı sözə basıb saxlayın
</string>
<!-- Instruction to touch the bubble to continue -->
<string name="touch_to_continue">Davam etmək üçün bu məsləhət toxunun »</string>
<!-- Instruction to touch the bubble to start typing -->
<string name="touch_to_finish">Məsləhəti bağlayıb yazmağa başlamaq üçün bura toxun!</string>
<!-- Tutorial tip 1 - The keyboard opens any time you touch a text field -->
<string name="tip_to_open_keyboard"><b>Bir mətn sahəsinə toxunanda klaviatura açılır</b></string>
<!-- Tutorial tip 2 - Touch and hold a key to view accents (examples) -->
<!-- Tutorial tip 3 - How to switch to number/symbol keyboard -->
<!-- Tutorial tip 4 - How to switch back to alphabet keyboard -->
<!-- Tutorial tip 5 - How to launch keyboard settings -->
<!-- Tutorial tip 6 - Done with the tutorial -->
<!-- Label for soft enter key when it performs GO action. Must be short to fit on key! -->
<string name="label_go_key">Get</string>
<!-- Label for soft enter key when it performs NEXT action. Must be short to fit on key! -->
<string name="label_next_key">Növbəti</string>
<!-- Label for soft enter key when it performs DONE action. Must be short to fit on key! -->
<string name="label_done_key">Hazırdır</string>
<!-- Label for soft enter key when it performs SEND action. Must be short to fit on key! -->
<string name="label_send_key">Göndər</string>
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="label_symbol_key">\?123</string>
<!-- Label for "switch to numeric" key. Must be short to fit on key! -->
<string name="label_phone_key">123</string>
<!-- Label for "switch to alphabetic" key. Must be short to fit on key! -->
<string name="label_alpha_key">ABC</string>
<!-- Label for ALT modifier key. Must be short to fit on key! -->
<string name="label_alt_key">ALT</string>
<!-- Voice related labels -->
<!-- Title of the warning dialog that shows when a user initiates voice input for
the first time. -->
<string name="voice_warning_title">Səs girişi</string>
<!-- Message that gets put at the top of the warning dialog if the user is attempting to use
voice input in a currently unsupported locale. Voice input will work for such a user,
but it will only recognize them in English. -->
<string name="voice_warning_locale_not_supported">Səs girişi, hal-hazırda diliniz üçün dəstəklənmir, ancaq İngiliscədə işləyir.</string>
<!-- Message of the warning dialog that shows when a user initiates voice input for
the first time, or turns it on in settings. -->
<string name="voice_warning_may_not_understand">Səs girişi, Google-ın şəbəkə bağlantılı danışıq səsini tanıma özəlliyini istifadə edən təcrübi bir özəllikdir.</string>
<!-- An additional part of the warning dialog for voice input that only shows when the user
actually initiates voice input, rather than just turning it on in settings. -->
<string name="voice_warning_how_to_turn_off">Səs girişini söndürüb klaviatura tənzimləmələrinə gedin.</string>
<!-- Message to show when user clicks the swiping hint (which says
"Swipe across keyboard to speak"). Also shown when enabling settings. -->
<string name="voice_hint_dialog_message">Səs girişini istifadə etmək üçün, mikrofon düyməsinə bas və ya klaviatura üzərində barmağını sürüşdür.</string>
<!-- Short message to tell the user the system is ready for them to speak. -->
<string name="voice_listening">İndi danış</string>
<!-- Short message shown after the user finishes speaking. -->
<string name="voice_working">İşləyir</string>
<!-- Short message shown before the user should speak. -->
<!-- Short message shown when a generic error occurs. -->
<string name="voice_error">Xəta. Yenidən sınayın.</string>
<!-- Short message shown for a network error. -->
<string name="voice_network_error">Bağlantı qurula bilmədi</string>
<!-- Short message shown for a network error where the utterance was really long,
in which case we should suggest that the user speak less. -->
<string name="voice_too_much_speech">Xəta, çox uzun danışıq.</string>
<!-- Short message shown for an audio error. -->
<string name="voice_audio_error">Səs problemi</string>
<!-- Short message shown for an error with the voice server. -->
<string name="voice_server_error">Server xətası</string>
<!-- Short message shown when no speech is heard. -->
<string name="voice_speech_timeout">Danışıq eşidilmədi</string>
<!-- Short message shown when the server couldn't parse any speech. -->
<string name="voice_no_match">Uyğunluq tapılmadı</string>
<!-- Short message shown when the user initiates voice and voice
search is not installed. -->
<string name="voice_not_installed">Səsli axtarış quraşdırılmayıb</string>
<!-- Short hint shown in candidate view to explain voice input. -->
<string name="voice_swipe_hint"><b>Məsləhət:</b> Danışmaq üçün klaviaturada sürüşdürün.</string>
<!-- Short hint shown in candidate view to explain that user can speak punctuation. -->
<!-- Label on button to stop recognition. Must be short to fit on button. -->
<!-- Label on button when an error occurs -->
<string name="ok">Oldu</string>
<!-- Preferences item for enabling speech input -->
<string name="voice_input">Səs girişi</string>
<!-- Array of Voice Input modes -->
<string-array name="voice_input_modes">
<item>Əsas klaviaturada</item>
<item>Simvol klaviaturasında</item>
<item>Bağlı</item>
</string-array>
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Array of Voice Input modes summary -->
<string-array name="voice_input_modes_summary">
<item>Əsas klaviaturada mikrofon</item>
<item>Simvol klaviaturasında mikrofon</item>
<item>Səs girişi sıradan çıxarıldı</item>
</string-array>
<!-- Press the "enter" key after the user speaks. Option on settings.-->
<string name="auto_submit">Səsdən sonra avtomatik göndər</string>
<!-- Press the "enter" key after the user speaks. Summary of option in settings.-->
<string name="auto_submit_summary">Axtarış və ya növbəti sahəyə gediş zamanı enter düyməsinə avtomatik basın.</string>
<!-- IME Tutorial screen (ROMAN) -->
<!-- appears above image showing the user to click on a TextView to show the IME -->
<string name="open_the_keyboard"><font size="17"><b>Klaviaturanı aç\n</b></font><font size="3">\n</font>Hər hansısa mətn sahəsinə toxunun.</string>
<!-- appears above the image showing the back button used to close the keyboard -->
<string name="close_the_keyboard"><font size="17"><b>Klaviaturanı bağla\n</b></font><font size="3">\n</font>Geri düyməsinə basın.</string>
<!-- appears above image showing how to use touch and hold -->
<!-- appears above image showing how to access keyboard settings -->
<!-- popular web domains for the locale - most popular, displayed on the keyboard -->
<string name="popular_domain_0">".com"</string>
<!-- popular web domains for the locale - item 1, displayed in the popup -->
<string name="popular_domain_1">".net"</string>
<!-- popular web domains for the locale - item 2, displayed in the popup -->
<string name="popular_domain_2">".org"</string>
<!-- popular web domains for the locale - item 3, displayed in the popup -->
<string name="popular_domain_3">".org"</string>
<!-- popular web domains for the locale - item 4, displayed in the popup -->
<string name="popular_domain_4">".edu"</string>
<!-- Menu item for launching Input method picker -->
<string name="selectInputMethod">Giriş metodunu seç</string>
<!-- Title for input language selection screen -->
<string name="language_selection_title">Giriş dilləri</string>
<!-- Title summary for input language selection screen -->
<!-- Add to dictionary hint -->
<string name="hint_add_to_dictionary">\u2190 Saxlamaq üçün təkrar toxun</string>
<!-- Inform the user that a particular language has an available dictionary -->
<string name="has_dictionary">Lüğət əlçatandır</string>
<!-- Preferences item for enabling to send user statistics to Google -->
<string name="prefs_enable_log">İstifadəçi əks əlaqəsini fəallaşdır</string>
<!-- Description for enabling to send user statistics to Google -->
<!-- Preferences item for enabling to correct suggestions by touching words you have typed -->
<string name="prefs_enable_recorrection">Sözləri düzəltmək üçün toxun</string>
<!-- The summary for the preferences item for enabling to correct suggestions by touching words you have typed -->
<string name="prefs_enable_recorrection_summary">Düzəltmək üçün daxil edilən sözlərə toxun</string>
<!-- Description for keyboard theme switcher -->
<string name="keyboard_layout">Klaviatura teması</string>
<string name="subtype_mode_keyboard">klaviatura</string>
<string name="subtype_mode_voice">səs</string>
<!-- Title for Latin keyboard debug settings activity / dialog -->
<string name="kp2a_nextfields"><![CDATA[>]]></string>
</resources>

View File

@@ -0,0 +1,259 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Title for Latin keyboard -->
<string name="english_ime_name">Клавіятура Keepass2Android</string>
<!-- Title for Latin keyboard settings activity / dialog -->
<string name="english_ime_settings">Налады клавіятуры Android</string>
<!-- Title for Latin keyboard input options dialog -->
<string name="english_ime_input_options">Параметры ўводу</string>
<!-- Option to provide vibrate/haptic feedback on keypress -->
<string name="vibrate_on_keypress">Вібрацыя пры націсканні клавіш</string>
<!-- Option to play back sound on keypress in soft keyboard -->
<string name="sound_on_keypress">Гук пры націсканні клавіш</string>
<!-- Option to pop up the character with a larger font above soft keyboard -->
<string name="popup_on_keypress">Павелічэнне пры націсканні кнопак</string>
<!-- Option to enable using nearby keys when correcting/predicting -->
<string name="hit_correction">Выпраўленне памылак уводу</string>
<!-- Description for hit_correction -->
<string name="hit_correction_summary">Уключыць выпраўленне памылак падчас уводу</string>
<!-- Option to enable using nearby keys when correcting/predicting in landscape-->
<string name="hit_correction_land">Памылка ўводу ў альбомнай арыентацыі</string>
<!-- Description for hit_correction in landscape -->
<string name="hit_correction_land_summary">Уключыць выпраўленне памылак падчас уводу</string>
<!-- Option to automatically correct word on hitting space -->
<string name="auto_correction">Прапанаванне слоў</string>
<!-- Description for auto_correction -->
<string name="auto_correction_summary">Аўтаматычна выпраўляць папярэдняе слова</string>
<!-- Option to enable text prediction -->
<string name="prediction">Прапанаванне слоў</string>
<!-- Category title for text prediction -->
<string name="prediction_category">Налады прапанавання слоў</string>
<!-- Description for text prediction -->
<string name="prediction_summary">Уключыць аўтаматычнае завяршэнне слоў падчас уводу</string>
<!-- Dialog title for auto complete choices -->
<string name="auto_complete_dialog_title">Аўтазавяршэнне</string>
<!-- Option to enable text prediction in landscape -->
<string name="prediction_landscape">Павялічыць памер тэкставага поля</string>
<!-- Description for text prediction -->
<string name="prediction_landscape_summary">Хаваць прапанаванне слоў у альбомнай арыентацыі</string>
<!-- Option to enable auto capitalization of sentences -->
<string name="auto_cap">Аўтаматычныя вялікія літары</string>
<!-- Description for auto cap -->
<string name="auto_cap_summary">Рабіць вялікай першую літару сказа</string>
<!-- Option to enable auto punctuate -->
<string name="auto_punctuate">Аўтаматычная пунктуацыя</string>
<!-- Description for auto punctuate -->
<!-- Option to enable quick fixes -->
<string name="quick_fixes">Хуткае выпраўленне</string>
<!-- Description for quick fixes -->
<string name="quick_fixes_summary">Выпраўленне распаўсюджаных памылак</string>
<!-- Option to enable showing suggestions -->
<string name="show_suggestions">Паказ прапаноў</string>
<!-- Description for show suggestions -->
<string name="show_suggestions_summary">Прапаноўваць словы падчас уводу</string>
<!-- Option to enable auto completion -->
<string name="auto_complete">Аўтазавяршэнне</string>
<!-- Description for auto completion -->
<string name="auto_complete_summary">Пры націсканні прагала ўстаўляць прапанаванае слова</string>
<!-- Option to show/hide the settings key -->
<string name="prefs_settings_key">Кнопка налад</string>
<!-- Array of the settings key mode values -->
<!-- Option to automatically decide to show/hide the settings key -->
<string name="settings_key_mode_auto_name">Аўтаматычна</string>
<!-- Option to always show the settings key -->
<string name="settings_key_mode_always_show_name">Заўсёды паказваць</string>
<!-- Option to always hide the settings key -->
<string name="settings_key_mode_always_hide_name">Заўсёды хаваць</string>
<!-- Array of the settings key modes -->
<string-array name="settings_key_modes">
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_always_show_name</item>
<item>@string/settings_key_mode_always_hide_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Біграмныя прапановы</string>
<!-- Description for auto completion -->
<string name="bigram_suggestion_summary">Выкарыстоўвайце папярэдняе слова, каб выправіць прапанову</string>
<!-- Array of prediction modes -->
<string-array name="prediction_modes">
<item>Няма</item>
<item>Базавы</item>
<item>Пашыраны</item>
</string-array>
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Indicates that a word has been added to the dictionary -->
<string name="added_word"><xliff:g id="word">%s</xliff:g> : захавана</string>
<!-- Tip to long press on keys -->
<string name="tip_long_press">Утрымлівайце клавішу, каб пабачыць дыякрытычныя знакі (ø, ö і г.д.)</string>
<!-- Tip to dismiss keyboard -->
<string name="tip_dismiss">Націсніце клавішу \"Назад\", каб закрыць клавіятуру ў любы момант</string>
<!-- Tip to press ?123 to access numbers and symbols -->
<string name="tip_access_symbols">Адкрыць лічбы і сімвалы</string>
<!-- Tip to long press on typed word to add to dictionary -->
<string name="tip_add_to_dictionary">Націсніце і ўтрымлівайце слова злева, каб дадаць яго ў слоўнік</string>
<!-- Instruction to touch the bubble to continue -->
<string name="touch_to_continue">Націсніце на падказку, каб працягнуць »</string>
<!-- Instruction to touch the bubble to start typing -->
<string name="touch_to_finish">Націсніце сюды, каб закрыць прапанову і пачаць увод!</string>
<!-- Tutorial tip 1 - The keyboard opens any time you touch a text field -->
<string name="tip_to_open_keyboard"><b>Клавіятура з\'яўляецца аўтаматычна пры дакрананні да тэкставага поля</b></string>
<!-- Tutorial tip 2 - Touch and hold a key to view accents (examples) -->
<string name="tip_to_view_accents"><b>Націсніце і ўтрымлівайце кнопку, каб убачыць варыянты з дыякрытычнымі знакамі\n(ø, ö, ô, ó і г.д.)</b>
</string>
<!-- Tutorial tip 3 - How to switch to number/symbol keyboard -->
<string name="tip_to_open_symbols"><b>Для пераключэння паміж лічбамі і сімваламі выкарыстоўвайце гэтую кнопку</b></string>
<!-- Tutorial tip 4 - How to switch back to alphabet keyboard -->
<string name="tip_to_close_symbols"><b>Каб вярнуцца назад да літар, зноў націсніце гэтую кнопку</b></string>
<!-- Tutorial tip 5 - How to launch keyboard settings -->
<string name="tip_to_launch_settings"><b>Націсніце і ўтрымлівайце гэтую кнопку, каб змяніць налады клавіятуры кшталту аўтазапаўнення</b></string>
<!-- Tutorial tip 6 - Done with the tutorial -->
<string name="tip_to_start_typing"><b>Паспрабуйце!</b></string>
<!-- Label for soft enter key when it performs GO action. Must be short to fit on key! -->
<string name="label_go_key">Пераход</string>
<!-- Label for soft enter key when it performs NEXT action. Must be short to fit on key! -->
<string name="label_next_key">Далей</string>
<!-- Label for soft enter key when it performs DONE action. Must be short to fit on key! -->
<string name="label_done_key">Завершана</string>
<!-- Label for soft enter key when it performs SEND action. Must be short to fit on key! -->
<string name="label_send_key">Адправіць</string>
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="label_symbol_key">\?123</string>
<!-- Label for "switch to numeric" key. Must be short to fit on key! -->
<string name="label_phone_key">123</string>
<!-- Label for "switch to alphabetic" key. Must be short to fit on key! -->
<string name="label_alpha_key">АБВ</string>
<!-- Label for ALT modifier key. Must be short to fit on key! -->
<string name="label_alt_key">ALT</string>
<!-- Voice related labels -->
<!-- Title of the warning dialog that shows when a user initiates voice input for
the first time. -->
<string name="voice_warning_title">Галасавы ўвод</string>
<!-- Message that gets put at the top of the warning dialog if the user is attempting to use
voice input in a currently unsupported locale. Voice input will work for such a user,
but it will only recognize them in English. -->
<string name="voice_warning_locale_not_supported">На дадзены момант галасавы ўвод не падтрымліваецца для вашай мовы, але вы можаце карыстацца ім на англійскай.</string>
<!-- Message of the warning dialog that shows when a user initiates voice input for
the first time, or turns it on in settings. -->
<string name="voice_warning_may_not_understand">Галасавы ўвод - эксперыментальная функцыя, якая выкарыстоўвае сістэму распазнавання маўлення ад Google.</string>
<!-- An additional part of the warning dialog for voice input that only shows when the user
actually initiates voice input, rather than just turning it on in settings. -->
<string name="voice_warning_how_to_turn_off">Функцыя галасавога ўводу адключаецца ў наладах клавіятуры.</string>
<!-- Message to show when user clicks the swiping hint (which says
"Swipe across keyboard to speak"). Also shown when enabling settings. -->
<string name="voice_hint_dialog_message">Каб выкарыстоўваць галасавы ўвод, націсніце кнопку мікрафона або правядзіце пальцам па экраннай клавіятуры.</string>
<!-- Short message to tell the user the system is ready for them to speak. -->
<string name="voice_listening">Гаварыце</string>
<!-- Short message shown after the user finishes speaking. -->
<string name="voice_working">Апрацоўка</string>
<!-- Short message shown before the user should speak. -->
<!-- Short message shown when a generic error occurs. -->
<string name="voice_error">Памылка. Паўтарыце спробу.</string>
<!-- Short message shown for a network error. -->
<string name="voice_network_error">Не атрымалася злучыцца</string>
<!-- Short message shown for a network error where the utterance was really long,
in which case we should suggest that the user speak less. -->
<string name="voice_too_much_speech">Памылка, занадта доўгая фраза.</string>
<!-- Short message shown for an audio error. -->
<string name="voice_audio_error">Праблема з гукам</string>
<!-- Short message shown for an error with the voice server. -->
<string name="voice_server_error">Памылка сервера</string>
<!-- Short message shown when no speech is heard. -->
<string name="voice_speech_timeout">Голас не чутны</string>
<!-- Short message shown when the server couldn't parse any speech. -->
<string name="voice_no_match">Супадзенняў не знойдзена</string>
<!-- Short message shown when the user initiates voice and voice
search is not installed. -->
<string name="voice_not_installed">Галасавы пошук не ўсталяваны</string>
<!-- Short hint shown in candidate view to explain voice input. -->
<string name="voice_swipe_hint"><b>Падказка:</b> Для пачатку галасавога ўводу правядзіце па клавіятуры</string>
<!-- Short hint shown in candidate view to explain that user can speak punctuation. -->
<string name="voice_punctuation_hint"><b>Падказа:</b> Наступным разам называйце знакі прыпынку: \"коска\", \"кропка\", \"пытальнік\".</string>
<!-- Label on button to stop recognition. Must be short to fit on button. -->
<string name="cancel">Скасаваць</string>
<!-- Label on button when an error occurs -->
<string name="ok">Добра</string>
<!-- Preferences item for enabling speech input -->
<string name="voice_input">Галасавы ўвод</string>
<!-- Array of Voice Input modes -->
<string-array name="voice_input_modes">
<item>На асноўнай клавіятуры</item>
<item>На клавіятуры сімвалаў</item>
<item>Адключыць</item>
</string-array>
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Array of Voice Input modes summary -->
<string-array name="voice_input_modes_summary">
<item>Значок на асноўнай клавіятуры</item>
<item>Значок на клавіятуры сімвалаў</item>
<item>Галасавы ўвод адключаны</item>
</string-array>
<!-- Press the "enter" key after the user speaks. Option on settings.-->
<string name="auto_submit">Аўтаматычнае адпраўленне пасля галасавога ўводу</string>
<!-- Press the "enter" key after the user speaks. Summary of option in settings.-->
<string name="auto_submit_summary">Аўтаматычна націскаць \"Увод\" падчас пошуку альбо пераходу да новага поля.</string>
<!-- IME Tutorial screen (ROMAN) -->
<!-- appears above image showing the user to click on a TextView to show the IME -->
<string name="open_the_keyboard"><font size="17"><b>Адкрыццё клавіятуры\n</b></font><font size="3">\n</font>Націсніце на любое тэкставае поле.</string>
<!-- appears above the image showing the back button used to close the keyboard -->
<string name="close_the_keyboard"><font size="17"><b>Закрыццё клавіятуры\n</b></font><font size="3">\n</font>Націсніце кнопку \"Назад\".</string>
<!-- appears above image showing how to use touch and hold -->
<string name="touch_and_hold"><font size="17"><b>Націсніце \u0026 і ўтрымлівайце клавішу для паказу параметраў\n</b></font><font size="3">\n</font>Доступ да пунктуацыйных і дыякрытычных знакаў.</string>
<!-- appears above image showing how to access keyboard settings -->
<string name="keyboard_settings"><font size="17"><b>Налады клавіятуры\n</b></font><font size="3">\n</font>Націсніце \u0026 і ўтрымлівайце <b>\?123\</b>.</string>
<!-- popular web domains for the locale - most popular, displayed on the keyboard -->
<string name="popular_domain_0">".com"</string>
<!-- popular web domains for the locale - item 1, displayed in the popup -->
<string name="popular_domain_1">".net"</string>
<!-- popular web domains for the locale - item 2, displayed in the popup -->
<string name="popular_domain_2">".org"</string>
<!-- popular web domains for the locale - item 3, displayed in the popup -->
<string name="popular_domain_3">".gov"</string>
<!-- popular web domains for the locale - item 4, displayed in the popup -->
<string name="popular_domain_4">".edu"</string>
<!-- Menu item for launching Input method picker -->
<string name="selectInputMethod">Абраць метад уводу</string>
<!-- Title for input language selection screen -->
<string name="language_selection_title">Мовы ўводу</string>
<!-- Title summary for input language selection screen -->
<string name="language_selection_summary">Каб змяніць мову, правядзіце пальцам па кнопцы прагалу</string>
<!-- Add to dictionary hint -->
<string name="hint_add_to_dictionary">\u2190 Націсніце зноў, каб захаваць</string>
<!-- Inform the user that a particular language has an available dictionary -->
<string name="has_dictionary">Даступны слоўнік</string>
<!-- Preferences item for enabling to send user statistics to Google -->
<string name="prefs_enable_log">Уключыць адпраўленне водгукаў</string>
<!-- Description for enabling to send user statistics to Google -->
<string name="prefs_description_log">Дапамажыце палепшыць рэдактар метаду ўводу, дазволіўшы адпраўленне статыстыкі і справаздач пра хібы Google.</string>
<!-- Preferences item for enabling to correct suggestions by touching words you have typed -->
<string name="prefs_enable_recorrection">Выпраўленне націсканнем</string>
<!-- The summary for the preferences item for enabling to correct suggestions by touching words you have typed -->
<string name="prefs_enable_recorrection_summary">Націсніце на слова, каб выправіць яго</string>
<!-- Description for keyboard theme switcher -->
<string name="keyboard_layout">Тэма клавіятуры</string>
<string name="subtype_mode_keyboard">клавіятура</string>
<string name="subtype_mode_voice">галасавы</string>
<!-- Title for Latin keyboard debug settings activity / dialog -->
<string name="kp2a_nextfields"><![CDATA[>]]></string>
</resources>

View File

@@ -129,11 +129,15 @@
<!-- Label for soft enter key when it performs DONE action. Must be short to fit on key! -->
<string name="label_done_key">Udført</string>
<!-- Label for soft enter key when it performs SEND action. Must be short to fit on key! -->
<string name="label_send_key">Send</string>
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="label_symbol_key">\?123</string>
<!-- Label for "switch to numeric" key. Must be short to fit on key! -->
<string name="label_phone_key">123</string>
<!-- Label for "switch to alphabetic" key. Must be short to fit on key! -->
<string name="label_alpha_key">ABC</string>
<!-- Label for ALT modifier key. Must be short to fit on key! -->
<string name="label_alt_key">ALT</string>
<!-- Voice related labels -->
<!-- Title of the warning dialog that shows when a user initiates voice input for
the first time. -->
@@ -181,6 +185,7 @@
<!-- Label on button to stop recognition. Must be short to fit on button. -->
<string name="cancel">Annullér</string>
<!-- Label on button when an error occurs -->
<string name="ok">OK</string>
<!-- Preferences item for enabling speech input -->
<string name="voice_input">Stemmeinput</string>
<!-- Array of Voice Input modes -->
@@ -212,10 +217,15 @@
<!-- appears above image showing how to access keyboard settings -->
<string name="keyboard_settings"><font size="17"><b>\"Tastaturindstillinger\"\n</b></font><font size="3">\n</font>\"Tryk på tasten \"<b>\"?123\"</b>\", og hold den nede.\"</string>
<!-- popular web domains for the locale - most popular, displayed on the keyboard -->
<string name="popular_domain_0">".com"</string>
<!-- popular web domains for the locale - item 1, displayed in the popup -->
<string name="popular_domain_1">".net"</string>
<!-- popular web domains for the locale - item 2, displayed in the popup -->
<string name="popular_domain_2">".org"</string>
<!-- popular web domains for the locale - item 3, displayed in the popup -->
<string name="popular_domain_3">".gov"</string>
<!-- popular web domains for the locale - item 4, displayed in the popup -->
<string name="popular_domain_4">".edu"</string>
<!-- Menu item for launching Input method picker -->
<string name="selectInputMethod">Vælg inputmetode</string>
<!-- Title for input language selection screen -->
@@ -239,4 +249,5 @@
<string name="subtype_mode_keyboard">tastatur</string>
<string name="subtype_mode_voice">stemme</string>
<!-- Title for Latin keyboard debug settings activity / dialog -->
<string name="kp2a_nextfields"><![CDATA[>]]></string>
</resources>

View File

@@ -83,6 +83,11 @@
<!-- Option to always hide the settings key -->
<string name="settings_key_mode_always_hide_name">Πάντα απόκρυψη</string>
<!-- Array of the settings key modes -->
<string-array name="settings_key_modes">
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_always_show_name</item>
<item>@string/settings_key_mode_always_hide_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Προτάσεις bigram</string>
<!-- Description for auto completion -->

View File

@@ -84,9 +84,9 @@
<string name="settings_key_mode_always_hide_name">Ocultar siempre</string>
<!-- Array of the settings key modes -->
<string-array name="settings_key_modes">
<item>@string/modo-de-configuracion-de-clave-nombramiento-automatico</item>
<item>@string/modo-de-configuracion-de-clave-siempre-mostrar-nombre</item>
<item>@string/modo-de-configuracion-de-clave-siempre-ocultar-nombre</item>
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_always_show_name</item>
<item>@string/settings_key_mode_always_hide_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Sugerencias de bigramas</string>

View File

@@ -20,6 +20,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Title for Latin keyboard -->
<!-- Title for Latin keyboard settings activity / dialog -->
<string name="english_ime_settings">ആൻഡ്രോയിഡ് കീബോർഡ് ക്രമീകരണങ്ങൾ</string>
<!-- Title for Latin keyboard input options dialog -->
<!-- Option to provide vibrate/haptic feedback on keypress -->
<!-- Option to play back sound on keypress in soft keyboard -->

View File

@@ -111,7 +111,7 @@
<!-- Instruction to touch the bubble to start typing -->
<string name="touch_to_finish">Trykk her for å lukke dette hintet og begynne å skrive!</string>
<!-- Tutorial tip 1 - The keyboard opens any time you touch a text field -->
<string name="tip_to_open_keyboard"><b>\"Tastaturet åpnes når du tar på et tekstfelt\"</b></string>
<string name="tip_to_open_keyboard"><b>\"Tastaturet åpnes når du trykker på et tekstfelt\"</b></string>
<!-- Tutorial tip 2 - Touch and hold a key to view accents (examples) -->
<string name="tip_to_view_accents"><b>\"Trykk på og hold nede en tast for å se aksenter\"\n\"(ø, ö, ô, ó, osv.)\"</b></string>
<!-- Tutorial tip 3 - How to switch to number/symbol keyboard -->

View File

@@ -84,9 +84,9 @@
<string name="settings_key_mode_always_hide_name">Altijd verbergen</string>
<!-- Array of the settings key modes -->
<string-array name="settings_key_modes">
<item>\@ rij- instellingen-sleutel-wijze-automatisch-naam</item>
<item>\@ rij- instellingen- sleutel-wijze-altijd-tonen-naam</item>
<item>\@rij-instellingen-sleutel-wijze-altijd-verberg-naam</item>
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_always_show_name</item>
<item>@string/settings_key_mode_always_hide_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Digram-suggesties</string>

View File

@@ -83,6 +83,11 @@
<!-- Option to always hide the settings key -->
<string name="settings_key_mode_always_hide_name">Zawsze ukrywaj</string>
<!-- Array of the settings key modes -->
<string-array name="settings_key_modes">
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_auto_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Sugestie dla bigramów</string>
<!-- Description for auto completion -->
@@ -133,8 +138,11 @@
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="label_symbol_key">\?123</string>
<!-- Label for "switch to numeric" key. Must be short to fit on key! -->
<string name="label_phone_key">123</string>
<!-- Label for "switch to alphabetic" key. Must be short to fit on key! -->
<string name="label_alpha_key">ABC</string>
<!-- Label for ALT modifier key. Must be short to fit on key! -->
<string name="label_alt_key">ALT</string>
<!-- Voice related labels -->
<!-- Title of the warning dialog that shows when a user initiates voice input for
the first time. -->
@@ -182,6 +190,7 @@
<!-- Label on button to stop recognition. Must be short to fit on button. -->
<string name="cancel">Anuluj</string>
<!-- Label on button when an error occurs -->
<string name="ok">OK</string>
<!-- Preferences item for enabling speech input -->
<string name="voice_input">Wprowadzanie głosowe</string>
<!-- Array of Voice Input modes -->
@@ -213,10 +222,15 @@
<!-- appears above image showing how to access keyboard settings -->
<string name="keyboard_settings"><font size="17"><b>\"Ustawienia klawiatury\"\n</b></font><font size="3">\n</font>\"Dotknij klawisza \"<b>\"?123\"</b>\" i przytrzymaj go.\"</string>
<!-- popular web domains for the locale - most popular, displayed on the keyboard -->
<string name="popular_domain_0">".com"</string>
<!-- popular web domains for the locale - item 1, displayed in the popup -->
<string name="popular_domain_1">".net"</string>
<!-- popular web domains for the locale - item 2, displayed in the popup -->
<string name="popular_domain_2">".org"</string>
<!-- popular web domains for the locale - item 3, displayed in the popup -->
<string name="popular_domain_3">".gov"</string>
<!-- popular web domains for the locale - item 4, displayed in the popup -->
<string name="popular_domain_4">".edu"</string>
<!-- Menu item for launching Input method picker -->
<string name="selectInputMethod">Wybierz sposób wprowadzania tekstu</string>
<!-- Title for input language selection screen -->
@@ -240,4 +254,5 @@
<string name="subtype_mode_keyboard">klawiatura</string>
<string name="subtype_mode_voice">głosowe</string>
<!-- Title for Latin keyboard debug settings activity / dialog -->
<string name="kp2a_nextfields"><![CDATA[>]]></string>
</resources>

View File

@@ -19,129 +19,242 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Title for Latin keyboard -->
<string name="english_ime_name">Teclado Keepass2Android</string>
<!-- Title for Latin keyboard settings activity / dialog -->
<string name="english_ime_settings">Configurações do teclado Android</string>
<!-- Title for Latin keyboard input options dialog -->
<string name="english_ime_input_options">Opções de entrada</string>
<!-- Option to provide vibrate/haptic feedback on keypress -->
<string name="vibrate_on_keypress">Vibrar ao pressionar tecla</string>
<!-- Option to play back sound on keypress in soft keyboard -->
<string name="sound_on_keypress">Som ao pressionar tecla</string>
<!-- Option to pop up the character with a larger font above soft keyboard -->
<string name="popup_on_keypress">Popup ao pressionar tecla</string>
<!-- Option to enable using nearby keys when correcting/predicting -->
<string name="hit_correction">Corrigir erros de digitação</string>
<!-- Description for hit_correction -->
<string name="hit_correction_summary">Ativar correção de erro de digitação</string>
<!-- Option to enable using nearby keys when correcting/predicting in landscape-->
<string name="hit_correction_land">Erros de entrada em modo paisagem</string>
<!-- Description for hit_correction in landscape -->
<string name="hit_correction_land_summary">Ativar correção de erro de digitação</string>
<!-- Option to automatically correct word on hitting space -->
<string name="auto_correction">Sugestões de palavra</string>
<!-- Description for auto_correction -->
<string name="auto_correction_summary">Corrigir automaticamente a palavra anterior</string>
<!-- Option to enable text prediction -->
<string name="prediction">Sugestões de palavra</string>
<!-- Category title for text prediction -->
<string name="prediction_category">Configurações de sugestão de palavra</string>
<!-- Description for text prediction -->
<string name="prediction_summary">Ativar autocompletar durante digitação</string>
<!-- Dialog title for auto complete choices -->
<string name="auto_complete_dialog_title">Autocompletar</string>
<!-- Option to enable text prediction in landscape -->
<string name="prediction_landscape">Aumentar tamanho do campo de texto</string>
<!-- Description for text prediction -->
<string name="prediction_landscape_summary">Ocultar sugestões de palavras em modo paisagem</string>
<!-- Option to enable auto capitalization of sentences -->
<string name="auto_cap">Capitalização automática</string>
<!-- Description for auto cap -->
<string name="auto_cap_summary">Iniciar uma sentença com letra maiúscula</string>
<!-- Option to enable auto punctuate -->
<string name="auto_punctuate">Pontuação automática</string>
<!-- Description for auto punctuate -->
<!-- Option to enable quick fixes -->
<string name="quick_fixes">Correções rápidas</string>
<!-- Description for quick fixes -->
<string name="quick_fixes_summary">Corrigir erros digitados frequentemente</string>
<!-- Option to enable showing suggestions -->
<string name="show_suggestions">Mostrar sugestões</string>
<!-- Description for show suggestions -->
<string name="show_suggestions_summary">Exibir palavras sugeridas durante a digitação</string>
<!-- Option to enable auto completion -->
<string name="auto_complete">Autocompletar</string>
<!-- Description for auto completion -->
<string name="auto_complete_summary">Barra de espaço e pontuação inserem automaticamente a palavra destacada</string>
<!-- Option to show/hide the settings key -->
<string name="prefs_settings_key">Mostrar tecla de configurações</string>
<!-- Array of the settings key mode values -->
<!-- Option to automatically decide to show/hide the settings key -->
<string name="settings_key_mode_auto_name">Automático</string>
<!-- Option to always show the settings key -->
<string name="settings_key_mode_always_show_name">Exibir sempre</string>
<!-- Option to always hide the settings key -->
<string name="settings_key_mode_always_hide_name">Ocultar sempre</string>
<!-- Array of the settings key modes -->
<string-array name="settings_key_modes">
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_always_show_name</item>
<item>@string/settings_key_mode_always_hide_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Sugestões do Bigram</string>
<!-- Description for auto completion -->
<string name="bigram_suggestion_summary">Usar palavra anterior para melhorar a sugestão</string>
<!-- Array of prediction modes -->
<string-array name="prediction_modes">
<item>Nada</item>
<item>Basic</item>
<item>Advanced</item>
<item>Básico</item>
<item>Avançado</item>
</string-array>
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Indicates that a word has been added to the dictionary -->
<string name="added_word"><xliff:g id="word">%s</xliff:g> : Salvo</string>
<!-- Tip to long press on keys -->
<string name="tip_long_press">Segure a tecla para ver acentos (ø, ö, etc.)</string>
<!-- Tip to dismiss keyboard -->
<string name="tip_dismiss">Pressione a tecla voltar \u21B6 para fechar o teclado a qualquer momento</string>
<!-- Tip to press ?123 to access numbers and symbols -->
<string name="tip_access_symbols">Acessar números e símbolos</string>
<!-- Tip to long press on typed word to add to dictionary -->
<string name="tip_add_to_dictionary">Pressione e segure a palavra mais à esquerda para adicioná-la ao dicionário
</string>
<!-- Instruction to touch the bubble to continue -->
<string name="touch_to_continue">Toque nesta dica para continuar »</string>
<!-- Instruction to touch the bubble to start typing -->
<string name="touch_to_finish">Toque aqui para fechar esta dica e começar a digitar!</string>
<!-- Tutorial tip 1 - The keyboard opens any time you touch a text field -->
<string name="tip_to_open_keyboard"><b>O teclado abre sempre que você tocar um campo de texto</b></string>
<!-- Tutorial tip 2 - Touch and hold a key to view accents (examples) -->
<string name="tip_to_view_accents"><b>Toque em &amp; segure uma tecla para ver os acentos\n(ø, o├, o├, o├, etc.)</b>
</string>
<!-- Tutorial tip 3 - How to switch to number/symbol keyboard -->
<string name="tip_to_open_symbols"><b>Alterne para números e símbolos tocando esta tecla</b></string>
<!-- Tutorial tip 4 - How to switch back to alphabet keyboard -->
<string name="tip_to_close_symbols"><b>Volte para as letras tocando nessa tecla novamente</b></string>
<!-- Tutorial tip 5 - How to launch keyboard settings -->
<string name="tip_to_launch_settings"><b>Toque em &amp; segure esta tecla para alterar as configurações do teclado, como auto completar</b></string>
<!-- Tutorial tip 6 - Done with the tutorial -->
<string name="tip_to_start_typing"><b>Experimente!</b></string>
<!-- Label for soft enter key when it performs GO action. Must be short to fit on key! -->
<string name="label_go_key">Ir</string>
<!-- Label for soft enter key when it performs NEXT action. Must be short to fit on key! -->
<string name="label_next_key">Próximo</string>
<!-- Label for soft enter key when it performs DONE action. Must be short to fit on key! -->
<string name="label_done_key">Feito</string>
<!-- Label for soft enter key when it performs SEND action. Must be short to fit on key! -->
<string name="label_send_key">Enviar</string>
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="label_symbol_key">\?123</string>
<!-- Label for "switch to numeric" key. Must be short to fit on key! -->
<string name="label_phone_key">123</string>
<!-- Label for "switch to alphabetic" key. Must be short to fit on key! -->
<string name="label_alpha_key">ABC</string>
<!-- Label for ALT modifier key. Must be short to fit on key! -->
<string name="label_alt_key">ALT</string>
<!-- Voice related labels -->
<!-- Title of the warning dialog that shows when a user initiates voice input for
the first time. -->
<string name="voice_warning_title">Entrada por voz</string>
<!-- Message that gets put at the top of the warning dialog if the user is attempting to use
voice input in a currently unsupported locale. Voice input will work for such a user,
but it will only recognize them in English. -->
<string name="voice_warning_locale_not_supported">A entrada por voz não é atualmente suportada para o seu idioma, mas funciona em inglês.</string>
<!-- Message of the warning dialog that shows when a user initiates voice input for
the first time, or turns it on in settings. -->
<string name="voice_warning_may_not_understand">Entrada de voz é um recurso experimental usando o reconhecimento de fala em rede do Google.</string>
<!-- An additional part of the warning dialog for voice input that only shows when the user
actually initiates voice input, rather than just turning it on in settings. -->
<string name="voice_warning_how_to_turn_off">Para desativar a entrada por voz, vá para as configurações do teclado.</string>
<!-- Message to show when user clicks the swiping hint (which says
"Swipe across keyboard to speak"). Also shown when enabling settings. -->
<string name="voice_hint_dialog_message">Para usar entrada de voz, pressione o botão do microfone ou deslize seu dedo através do teclado na tela.</string>
<!-- Short message to tell the user the system is ready for them to speak. -->
<string name="voice_listening">Fale agora</string>
<!-- Short message shown after the user finishes speaking. -->
<string name="voice_working">Processando</string>
<!-- Short message shown before the user should speak. -->
<!-- Short message shown when a generic error occurs. -->
<string name="voice_error">Erro. Por favor tente novamente.</string>
<!-- Short message shown for a network error. -->
<string name="voice_network_error">Não foi possível se conectar</string>
<!-- Short message shown for a network error where the utterance was really long,
in which case we should suggest that the user speak less. -->
<string name="voice_too_much_speech">Erro, muita fala de uma vez.</string>
<!-- Short message shown for an audio error. -->
<string name="voice_audio_error">Problema de áudio</string>
<!-- Short message shown for an error with the voice server. -->
<string name="voice_server_error">Erro no servidor</string>
<!-- Short message shown when no speech is heard. -->
<string name="voice_speech_timeout">Nenhuma fala ouvida</string>
<!-- Short message shown when the server couldn't parse any speech. -->
<string name="voice_no_match">Nenhum resultado encontrado</string>
<!-- Short message shown when the user initiates voice and voice
search is not installed. -->
<string name="voice_not_installed">Pesquisa de voz não instalada</string>
<!-- Short hint shown in candidate view to explain voice input. -->
<string name="voice_swipe_hint"><b>Dica:</b> Deslize pelo teclado para falar</string>
<!-- Short hint shown in candidate view to explain that user can speak punctuation. -->
<string name="voice_punctuation_hint"><b>Dica:</b> Da próxima vez, tente falar pontuações como \"ponto\", \"vírgula\" ou \"ponto de interrogação\".</string>
<!-- Label on button to stop recognition. Must be short to fit on button. -->
<string name="cancel">Cancelar</string>
<!-- Label on button when an error occurs -->
<string name="ok">OK</string>
<!-- Preferences item for enabling speech input -->
<string name="voice_input">Entrada por voz</string>
<!-- Array of Voice Input modes -->
<string-array name="voice_input_modes">
<item>No teclado principal</item>
<item>No teclado de símbolos</item>
<item>Desativado</item>
</string-array>
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Don't translate -->
<!-- Array of Voice Input modes summary -->
<string-array name="voice_input_modes_summary">
<item>Microfone no teclado principal</item>
<item>Microfone no teclado de símbolos</item>
<item>Entrada por voz desativada</item>
</string-array>
<!-- Press the "enter" key after the user speaks. Option on settings.-->
<string name="auto_submit">Submeter automaticamente após fala</string>
<!-- Press the "enter" key after the user speaks. Summary of option in settings.-->
<string name="auto_submit_summary">Pressionar enter automaticamente quando estiver pesquisando ou indo para o próximo campo.</string>
<!-- IME Tutorial screen (ROMAN) -->
<!-- appears above image showing the user to click on a TextView to show the IME -->
<string name="open_the_keyboard"><font size="17"><b>Abra o teclado\n</b></font><font size="3">\n</font>Toque em qualquer campo de texto.</string>
<!-- appears above the image showing the back button used to close the keyboard -->
<string name="close_the_keyboard"><font size="17"><b>Feche o teclado\n</b></font><font size="3">\n</font>Pressione o botão de voltar.</string>
<!-- appears above image showing how to use touch and hold -->
<string name="touch_and_hold"><font size="17"><b>Toque em \u0026 segure uma tecla para opções\n</b></font><font size="3">\n</font>Acesse pontuações e acentos.</string>
<!-- appears above image showing how to access keyboard settings -->
<string name="keyboard_settings"><font size="17"><b>Configurações do teclado\n</b></font><font size="3">\n</font>Toque em \u0026 e segure a tecla <b>\?123\</b>.</string>
<!-- popular web domains for the locale - most popular, displayed on the keyboard -->
<string name="popular_domain_0">".com"</string>
<!-- popular web domains for the locale - item 1, displayed in the popup -->
<string name="popular_domain_1">".net"</string>
<!-- popular web domains for the locale - item 2, displayed in the popup -->
<string name="popular_domain_2">".org"</string>
<!-- popular web domains for the locale - item 3, displayed in the popup -->
<string name="popular_domain_3">".gov"</string>
<!-- popular web domains for the locale - item 4, displayed in the popup -->
<string name="popular_domain_4">".edu"</string>
<!-- Menu item for launching Input method picker -->
<string name="selectInputMethod">Selecionar método de entrada</string>
<!-- Title for input language selection screen -->
<string name="language_selection_title">Idiomas de entrada</string>
<!-- Title summary for input language selection screen -->
<string name="language_selection_summary">Deslize o dedo na barra de espaço para mudar o idioma</string>
<!-- Add to dictionary hint -->
<string name="hint_add_to_dictionary">\u2190 Toque novamente para salvar</string>
<!-- Inform the user that a particular language has an available dictionary -->
<string name="has_dictionary">Dicionário disponível</string>
<!-- Preferences item for enabling to send user statistics to Google -->
<string name="prefs_enable_log">Ativar feedback do usuário</string>
<!-- Description for enabling to send user statistics to Google -->
<string name="prefs_description_log">Ajude a melhorar este editor de métodos de entrada enviando automaticamente estatísticas de uso e relatórios de erro para o Google.</string>
<!-- Preferences item for enabling to correct suggestions by touching words you have typed -->
<string name="prefs_enable_recorrection">Toque para corrigir as palavras</string>
<!-- The summary for the preferences item for enabling to correct suggestions by touching words you have typed -->
<string name="prefs_enable_recorrection_summary">Toque nas palavras inseridas para corrigi-las</string>
<!-- Description for keyboard theme switcher -->
<string name="keyboard_layout">Tema do Teclado</string>
<string name="subtype_mode_keyboard">teclado</string>
<string name="subtype_mode_voice">voz</string>
<!-- Title for Latin keyboard debug settings activity / dialog -->
<string name="kp2a_nextfields"><![CDATA[>]]></string>
</resources>

View File

@@ -51,7 +51,7 @@
<!-- Dialog title for auto complete choices -->
<string name="auto_complete_dialog_title">Conclusão automática</string>
<!-- Option to enable text prediction in landscape -->
<string name="prediction_landscape">Aumentar o tamanho do arquivo de texto</string>
<string name="prediction_landscape">Aumentar o tamanho do campo de texto</string>
<!-- Description for text prediction -->
<string name="prediction_landscape_summary">Ocultar sugestões de palavra na visualização da paisagem</string>
<!-- Option to enable auto capitalization of sentences -->
@@ -62,7 +62,7 @@
<string name="auto_punctuate">Pontuação automática</string>
<!-- Description for auto punctuate -->
<!-- Option to enable quick fixes -->
<string name="quick_fixes">Reparos rápidos</string>
<string name="quick_fixes">Correções rápidas</string>
<!-- Description for quick fixes -->
<string name="quick_fixes_summary">Corrige erros comuns de digitação</string>
<!-- Option to enable showing suggestions -->
@@ -83,6 +83,11 @@
<!-- Option to always hide the settings key -->
<string name="settings_key_mode_always_hide_name">Sempre ocultar</string>
<!-- Array of the settings key modes -->
<string-array name="settings_key_modes">
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_always_show_name</item>
<item>@string/settings_key_mode_always_hide_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Sugestões de bigrama</string>
<!-- Description for auto completion -->
@@ -133,8 +138,11 @@
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="label_symbol_key">\?123</string>
<!-- Label for "switch to numeric" key. Must be short to fit on key! -->
<string name="label_phone_key">123</string>
<!-- Label for "switch to alphabetic" key. Must be short to fit on key! -->
<string name="label_alpha_key">ABC</string>
<!-- Label for ALT modifier key. Must be short to fit on key! -->
<string name="label_alt_key">ALT</string>
<!-- Voice related labels -->
<!-- Title of the warning dialog that shows when a user initiates voice input for
the first time. -->
@@ -182,6 +190,7 @@
<!-- Label on button to stop recognition. Must be short to fit on button. -->
<string name="cancel">Cancelar</string>
<!-- Label on button when an error occurs -->
<string name="ok">OK</string>
<!-- Preferences item for enabling speech input -->
<string name="voice_input">Entrada de voz</string>
<!-- Array of Voice Input modes -->
@@ -213,10 +222,15 @@
<!-- appears above image showing how to access keyboard settings -->
<string name="keyboard_settings"><font size="17"><b>\"Configurações de teclado\"\n</b></font><font size="3">\n</font>\"Toque e mantenha pressionada a tecla \"<b>\"?123\"</b>\".\"</string>
<!-- popular web domains for the locale - most popular, displayed on the keyboard -->
<string name="popular_domain_0">".com"</string>
<!-- popular web domains for the locale - item 1, displayed in the popup -->
<string name="popular_domain_1">".net"</string>
<!-- popular web domains for the locale - item 2, displayed in the popup -->
<string name="popular_domain_2">".org"</string>
<!-- popular web domains for the locale - item 3, displayed in the popup -->
<string name="popular_domain_3">".gov"</string>
<!-- popular web domains for the locale - item 4, displayed in the popup -->
<string name="popular_domain_4">".edu"</string>
<!-- Menu item for launching Input method picker -->
<string name="selectInputMethod">Selecionar método de entrada</string>
<!-- Title for input language selection screen -->
@@ -240,4 +254,5 @@
<string name="subtype_mode_keyboard">teclado</string>
<string name="subtype_mode_voice">voz</string>
<!-- Title for Latin keyboard debug settings activity / dialog -->
<string name="kp2a_nextfields"><![CDATA[>]]></string>
</resources>

View File

@@ -83,6 +83,11 @@
<!-- Option to always hide the settings key -->
<string name="settings_key_mode_always_hide_name">Vždy skrývať</string>
<!-- Array of the settings key modes -->
<string-array name="settings_key_modes">
<item>@string/settings_key_mode_auto_name</item>
<item>@string/settings_key_mode_always_show_name</item>
<item>@string/settings_key_mode_always_hide_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Návrh Bigram</string>
<!-- Description for auto completion -->

View File

@@ -21,23 +21,23 @@
<!-- Title for Latin keyboard -->
<string name="english_ime_name">Клавіатура Keepass2Android</string>
<!-- Title for Latin keyboard settings activity / dialog -->
<string name="english_ime_settings">Налашт-ня клавіат. Keepass2Android</string>
<string name="english_ime_settings">Налаштування клавіатури Android</string>
<!-- Title for Latin keyboard input options dialog -->
<string name="english_ime_input_options">Парам. введення</string>
<!-- Option to provide vibrate/haptic feedback on keypress -->
<string name="vibrate_on_keypress">Вібр при натиску клав.</string>
<string name="vibrate_on_keypress">Вібрація клавіатури</string>
<!-- Option to play back sound on keypress in soft keyboard -->
<string name="sound_on_keypress">Звук при натиску клав.</string>
<string name="sound_on_keypress">Звук клавіатури</string>
<!-- Option to pop up the character with a larger font above soft keyboard -->
<string name="popup_on_keypress">Сплив. при нат.клав.</string>
<!-- Option to enable using nearby keys when correcting/predicting -->
<string name="hit_correction">Виправ. помилки вводу</string>
<string name="hit_correction">Виправляти помилки</string>
<!-- Description for hit_correction -->
<string name="hit_correction_summary">Увімкн. виправл. помилок вводу</string>
<string name="hit_correction_summary">Виправлення помилок введення</string>
<!-- Option to enable using nearby keys when correcting/predicting in landscape-->
<string name="hit_correction_land">Помилки альбомного вводу</string>
<string name="hit_correction_land">Помилки при введенні в альбомному режимі</string>
<!-- Description for hit_correction in landscape -->
<string name="hit_correction_land_summary">Увімкн. виправл. помилок вводу</string>
<string name="hit_correction_land_summary">Виправлення помилок введення</string>
<!-- Option to automatically correct word on hitting space -->
<string name="auto_correction">Пропозиції слів</string>
<!-- Description for auto_correction -->
@@ -47,39 +47,39 @@
<!-- Category title for text prediction -->
<string name="prediction_category">Налашт-ня пропозицій слів</string>
<!-- Description for text prediction -->
<string name="prediction_summary">Увімкн. автозаповнення при вводі</string>
<string name="prediction_summary">Автозавершення при введенні</string>
<!-- Dialog title for auto complete choices -->
<string name="auto_complete_dialog_title">Автозаповнення</string>
<string name="auto_complete_dialog_title">Автозавершення</string>
<!-- Option to enable text prediction in landscape -->
<string name="prediction_landscape">Збільш. розмір текст. поля</string>
<string name="prediction_landscape">Збільшити розмір текстового поля</string>
<!-- Description for text prediction -->
<string name="prediction_landscape_summary">Сховати пропозиції слів в альбом. режимі</string>
<string name="prediction_landscape_summary">Приховати пропозиції слів в альбомному режимі</string>
<!-- Option to enable auto capitalization of sentences -->
<string name="auto_cap">Авто викор. вел. літер</string>
<string name="auto_cap">Великі літери автоматично</string>
<!-- Description for auto cap -->
<string name="auto_cap_summary">Поч. писати речення з великої літери</string>
<string name="auto_cap_summary">Велика літера початку речення</string>
<!-- Option to enable auto punctuate -->
<string name="auto_punctuate">Авто пунктуація</string>
<!-- Description for auto punctuate -->
<!-- Option to enable quick fixes -->
<string name="quick_fixes">Шв. виправлення</string>
<string name="quick_fixes">Швидке виправлення</string>
<!-- Description for quick fixes -->
<string name="quick_fixes_summary">Виправляє поширені помилки</string>
<!-- Option to enable showing suggestions -->
<string name="show_suggestions">Показати пропозиції</string>
<string name="show_suggestions">Показувати пропозиції</string>
<!-- Description for show suggestions -->
<string name="show_suggestions_summary">Відображати при вводі пропоновані слова</string>
<string name="show_suggestions_summary">Показувати пропозиції слів при введенні</string>
<!-- Option to enable auto completion -->
<string name="auto_complete">Автозаповнення</string>
<string name="auto_complete">Автозавершення</string>
<!-- Description for auto completion -->
<string name="auto_complete_summary">Пробіл і пунктуація автоматично вставляє виділене слово</string>
<string name="auto_complete_summary">Пробіл і пунктуація автоматично вставляє позначене слово</string>
<!-- Option to show/hide the settings key -->
<string name="prefs_settings_key">Показ. клав. налашт.</string>
<string name="prefs_settings_key">Клавіша налаштувань</string>
<!-- Array of the settings key mode values -->
<!-- Option to automatically decide to show/hide the settings key -->
<string name="settings_key_mode_auto_name">Автоматично</string>
<!-- Option to always show the settings key -->
<string name="settings_key_mode_always_show_name">Завжди показ.</string>
<string name="settings_key_mode_always_show_name">Завжди показувати</string>
<!-- Option to always hide the settings key -->
<string name="settings_key_mode_always_hide_name">Завжди ховати</string>
<!-- Array of the settings key modes -->
@@ -89,14 +89,14 @@
<item>@string/settings_key_mode_always_hide_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Двобуквені пропозиції</string>
<string name="bigram_suggestion">Біграмні пропозиції</string>
<!-- Description for auto completion -->
<string name="bigram_suggestion_summary">Викор. попер. слово для покращ. пропозиції</string>
<string name="bigram_suggestion_summary">Використовуйте попереднє слово для поліпшення пропозиції</string>
<!-- Array of prediction modes -->
<string-array name="prediction_modes">
<item>Нічого</item>
<item>Базовий</item>
<item>Розшир.</item>
<item>Основний</item>
<item>Розширений</item>
</string-array>
<!-- Don't translate -->
<!-- Don't translate -->
@@ -134,7 +134,7 @@
<!-- Label for soft enter key when it performs DONE action. Must be short to fit on key! -->
<string name="label_done_key">Виконано</string>
<!-- Label for soft enter key when it performs SEND action. Must be short to fit on key! -->
<string name="label_send_key">Надісл.</string>
<string name="label_send_key">Надіслати</string>
<!-- Label for "switch to symbols" key. Must be short to fit on key! -->
<string name="label_symbol_key">\?123</string>
<!-- Label for "switch to numeric" key. Must be short to fit on key! -->
@@ -146,7 +146,7 @@
<!-- Voice related labels -->
<!-- Title of the warning dialog that shows when a user initiates voice input for
the first time. -->
<string name="voice_warning_title">Голос. ввід</string>
<string name="voice_warning_title">Голосове введення</string>
<!-- Message that gets put at the top of the warning dialog if the user is attempting to use
voice input in a currently unsupported locale. Voice input will work for such a user,
but it will only recognize them in English. -->
@@ -163,7 +163,7 @@
<!-- Short message to tell the user the system is ready for them to speak. -->
<string name="voice_listening">Диктуйте</string>
<!-- Short message shown after the user finishes speaking. -->
<string name="voice_working">Працює</string>
<string name="voice_working">Обробка</string>
<!-- Short message shown before the user should speak. -->
<!-- Short message shown when a generic error occurs. -->
<string name="voice_error">Помилка. Спробуйте ще раз.</string>
@@ -192,7 +192,7 @@
<!-- Label on button when an error occurs -->
<string name="ok">Гаразд</string>
<!-- Preferences item for enabling speech input -->
<string name="voice_input">Голос. ввід</string>
<string name="voice_input">Голосове введення</string>
<!-- Array of Voice Input modes -->
<string-array name="voice_input_modes">
<item>На осн. клавіатурі</item>

View File

@@ -23,7 +23,7 @@
<!-- Title for Latin keyboard settings activity / dialog -->
<string name="english_ime_settings">USB 键盘设置</string>
<!-- Title for Latin keyboard input options dialog -->
<string name="english_ime_input_options">Input options</string>
<string name="english_ime_input_options">输入选项</string>
<!-- Option to provide vibrate/haptic feedback on keypress -->
<string name="vibrate_on_keypress">按键振动</string>
<!-- Option to play back sound on keypress in soft keyboard -->
@@ -39,11 +39,11 @@
<!-- Description for hit_correction in landscape -->
<string name="hit_correction_land_summary">启用输入错误校正</string>
<!-- Option to automatically correct word on hitting space -->
<string name="auto_correction">联想</string>
<string name="auto_correction">词联想</string>
<!-- Description for auto_correction -->
<string name="auto_correction_summary">自动更正前一个单词</string>
<!-- Option to enable text prediction -->
<string name="prediction">联想</string>
<string name="prediction">词联想</string>
<!-- Category title for text prediction -->
<string name="prediction_category"> 词语建议设置</string>
<!-- Description for text prediction -->
@@ -89,7 +89,7 @@
<item>@string/settings_key_mode_auto_name</item>
</string-array>
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">Bigram分词建议</string>
<string name="bigram_suggestion">二元语法分词建议</string>
<!-- Description for auto completion -->
<string name="bigram_suggestion_summary">使用曾经使用过的词语改进建议</string>
<!-- Array of prediction modes -->
@@ -152,7 +152,7 @@
<!-- Message that gets put at the top of the warning dialog if the user is attempting to use
voice input in a currently unsupported locale. Voice input will work for such a user,
but it will only recognize them in English. -->
<string name="voice_warning_locale_not_supported">语音输入目前不支持您的语言,但确实可以使用英语。</string>
<string name="voice_warning_locale_not_supported">语音输入目前不支持您的语言,但仍然可以使用英语。</string>
<!-- Message of the warning dialog that shows when a user initiates voice input for
the first time, or turns it on in settings. -->
<string name="voice_warning_may_not_understand">语音输入是一项使用Google的网络语音识别功能的实验性功能。</string>
@@ -188,7 +188,7 @@
<!-- Short hint shown in candidate view to explain voice input. -->
<string name="voice_swipe_hint"><b>提示:</b> 滑动键盘说话。</string>
<!-- Short hint shown in candidate view to explain that user can speak punctuation. -->
<string name="voice_punctuation_hint"><b>提示:</b> 下一次,试着说一些符号\"句号\",\"逗号\"或者\"问号\"</string>
<string name="voice_punctuation_hint"><b>提示:</b> 下一次,试着说“句号”“逗号”“问号”等标点符号</string>
<!-- Label on button to stop recognition. Must be short to fit on button. -->
<string name="cancel">取消</string>
<!-- Label on button when an error occurs -->
@@ -224,7 +224,7 @@
<!-- appears above image showing how to access keyboard settings -->
<string name="keyboard_settings"><font size="17"><b>键盘设置\n</b></font><font size="3">\n</font>触摸 \u0026 按住 <b>\?123\</b> 键。</string>
<!-- popular web domains for the locale - most popular, displayed on the keyboard -->
<string name="popular_domain_0">"com"</string>
<string name="popular_domain_0">".com"</string>
<!-- popular web domains for the locale - item 1, displayed in the popup -->
<string name="popular_domain_1">".net"</string>
<!-- popular web domains for the locale - item 2, displayed in the popup -->

View File

@@ -50,7 +50,7 @@
<string name="afc_title_date">التاريخ</string>
<string name="afc_title_error">خطأ</string>
<string name="afc_title_info">معلومة</string>
<string name="afc_title_name">الإسم</string>
<string name="afc_title_name">الاسم</string>
<string name="afc_title_save_as">حفظ كـ…</string>
<string name="afc_title_size">الحجم</string>
<string name="afc_title_sort_by">فرز حسب…</string>

View File

@@ -6,5 +6,65 @@
permission.
-->
<resources>
<string name="afc_cmd_advanced_selection_all">Hamısı</string>
<string name="afc_cmd_advanced_selection_invert">Seçimə tərsinə çevir</string>
<string name="afc_cmd_advanced_selection_none">Heç biri</string>
<string name="afc_cmd_grid_view">Tor görünüşü</string>
<string name="afc_cmd_home">Əsas səhifə</string>
<string name="afc_cmd_list_view">Siyahı görünüşü</string>
<string name="afc_cmd_new_folder">Yeni qovluq…</string>
<string name="afc_cmd_select_all_files">Bütün faylları seç</string>
<string name="afc_cmd_select_all_folders">Bütün qovluqları seç</string>
<string name="afc_cmd_sort">Sırala…</string>
<string name="afc_file">fayl</string>
<string name="afc_folder">qovluq</string>
<string name="afc_hint_clear">təmizlə</string>
<string name="afc_hint_folder_name">qovluq adı</string>
<string name="afc_hint_save_as_filename">fayl adı</string>
<string name="afc_hint_search">axtar</string>
<string name="afc_msg_app_doesnot_have_permission_to_create_files">Bu tətbiqin fayl/qovluq yaratma icazəsi yoxdur</string>
<string name="afc_msg_app_doesnot_have_permission_to_delete_files">Bu tətbiqin fayl/qovluq silmə icazəsi yoxdur</string>
<string name="afc_msg_cancelled">İmtina edildi</string>
<string name="afc_msg_cannot_connect_to_file_provider_service">Fayl təchizatçı xidməti ilə bağlantı qurula bilmir</string>
<string name="afc_msg_cannot_create_new_folder_here">Burada yeni qovluq yaradıla bilmir</string>
<string name="afc_msg_cannot_save_a_file_here">Burada fayl saxlanıla bilmir</string>
<string name="afc_msg_done">Hazırdır</string>
<string name="afc_msg_empty">Boş</string>
<string name="afc_msg_failed_please_try_again">Uğursuz. Yenidən sına.</string>
<string name="afc_msg_loading">Yüklənir…</string>
<string name="afc_phone">Telefon</string>
<string name="afc_pmsg_cannot_access_dir">\"%1$s\" müraciət edilə bilmir</string>
<string name="afc_pmsg_cannot_create_folder">\"%1$s\" qovluğu yaradıla bilmir</string>
<string name="afc_pmsg_cannot_delete_file">%1$s \"%2$s\" silinə bilmir</string>
<string name="afc_pmsg_confirm_delete_file">Bu %1$s \"%2$s\" silmək istəyirsən?</string>
<string name="afc_pmsg_confirm_replace_file">\"%1$s\" faylı artıq var.\n\nƏvəzləmək istəyirsən?</string>
<string name="afc_pmsg_deleting_file">%1$s \"%2$s\" silinir…</string>
<string name="afc_pmsg_file_has_been_deleted">%1$s \"%2$s\" silindi</string>
<string name="afc_pmsg_filename_is_directory">\"%1$s\" bir qovluqdur</string>
<string name="afc_pmsg_filename_is_invalid">\"%1$s\" fayl adı etibarsızdır</string>
<string name="afc_pmsg_max_file_count_allowed">…daha çox faylı var, maksimal icazə: %1$,d</string>
<string name="afc_pmsg_unknown_error">Bilinməyən xəta: %1$s</string>
<string name="afc_root">Kök (root)</string>
<string name="afc_title_advanced_selection">Seç…</string>
<string name="afc_title_confirmation">Təsdiq</string>
<string name="afc_title_date">Tarix</string>
<string name="afc_title_error">Xəta</string>
<string name="afc_title_info">Məlumat</string>
<string name="afc_title_name">Ad</string>
<string name="afc_title_save_as">Saxla…</string>
<string name="afc_title_size">Ölçü</string>
<string name="afc_title_sort_by">Sırala…</string>
<string name="afc_yesterday">Dünən</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">Qovluq seç…</item>
<item quantity="other">Qovluqları seç…</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="one">Fayl seç…</item>
<item quantity="other">Faylları seç…</item>
</plurals>
<plurals name="afc_title_choose_files_directories">
<item quantity="one">Fayl/qovluq seç…</item>
<item quantity="other">Faylları/qovluqları seç…</item>
</plurals>
</resources>

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2012 Hai Bison
See the file LICENSE at the root directory of this project for copying
permission.
-->
<resources>
<string name="afc_cmd_advanced_selection_all">Усе</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>
<string name="afc_cmd_list_view">У выглядзе спіса</string>
<string name="afc_cmd_new_folder">Новы каталог…</string>
<string name="afc_cmd_select_all_files">Абраць усе файлы</string>
<string name="afc_cmd_select_all_folders">Абраць усе каталогі</string>
<string name="afc_cmd_sort">Сартаванне…</string>
<string name="afc_file">файл</string>
<string name="afc_folder">каталог</string>
<string name="afc_hint_clear">ачысціць</string>
<string name="afc_hint_folder_name">назва каталога</string>
<string name="afc_hint_save_as_filename">назва файла</string>
<string name="afc_hint_search">пошук</string>
<string name="afc_msg_app_doesnot_have_permission_to_create_files">У гэтай праграмы няма дазволу на стварэнне файлаў і каталогаў</string>
<string name="afc_msg_app_doesnot_have_permission_to_delete_files">У гэтай праграмы няма дазволу на выдаленне файлаў і каталогаў</string>
<string name="afc_msg_cancelled">Скасавана</string>
<string name="afc_msg_cannot_connect_to_file_provider_service">Не атрымалася злучыцца са службай захоўвання файлаў</string>
<string name="afc_msg_cannot_create_new_folder_here">Тут немагчыма стварыць новы каталог</string>
<string name="afc_msg_cannot_save_a_file_here">Тут немагчыма захаваць файл</string>
<string name="afc_msg_done">Завершана</string>
<string name="afc_msg_empty">Пуста</string>
<string name="afc_msg_failed_please_try_again">Не атрымалася. Калі ласка, паспрабуйце зноў.</string>
<string name="afc_msg_loading">Загрузка…</string>
<string name="afc_phone">Тэлефон</string>
<string name="afc_pmsg_cannot_access_dir">Не ўдалося атрымаць доступ да \"%1$s\"</string>
<string name="afc_pmsg_cannot_create_folder">Не атрымалася стварыць каталог \"%1$s\"</string>
<string name="afc_pmsg_cannot_delete_file">Не атрымалася выдаліць %1$s \"%2$s\"</string>
<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_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_title_advanced_selection">Абраць…</string>
<string name="afc_title_confirmation">Пацвярджэнне</string>
<string name="afc_title_date">Дата</string>
<string name="afc_title_error">Памылка</string>
<string name="afc_title_info">Звесткі</string>
<string name="afc_title_name">Назва</string>
<string name="afc_title_save_as">Захаваць як…</string>
<string name="afc_title_size">Памер</string>
<string name="afc_title_sort_by">Сартаваць па…</string>
<string name="afc_yesterday">Учора</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">Абраць каталог…</item>
<item quantity="few">Абраць каталогі…</item>
<item quantity="many">Абраць каталогі…</item>
<item quantity="other">Абраць каталогі…</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="one">Абраць файл…</item>
<item quantity="few">Абраць файлы…</item>
<item quantity="many">Абраць файлы…</item>
<item quantity="other">Абраць файлы…</item>
</plurals>
<plurals name="afc_title_choose_files_directories">
<item quantity="one">Абраць файл / каталог…</item>
<item quantity="few">Абраць файлы / каталогі…</item>
<item quantity="many">Абраць файлы / каталогі…</item>
<item quantity="other">Абраць файлы / каталогі…</item>
</plurals>
</resources>

View File

@@ -55,4 +55,12 @@
<string name="afc_title_size">Размер</string>
<string name="afc_title_sort_by">Сортирай според…</string>
<string name="afc_yesterday">Вчера</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">Избери файл/папка…</item>
<item quantity="other">Избери категория…</item>
</plurals>
<plurals name="afc_title_choose_files_directories">
<item quantity="one">Избери файл/папка…</item>
<item quantity="other">Избери файл/папка…key</item>
</plurals>
</resources>

View File

@@ -50,7 +50,7 @@
<string name="afc_title_date">Datum</string>
<string name="afc_title_error">Chyba</string>
<string name="afc_title_info">Informace</string>
<string name="afc_title_name">Název</string>
<string name="afc_title_name">Jméno</string>
<string name="afc_title_save_as">Uložit jako…</string>
<string name="afc_title_size">Velikost</string>
<string name="afc_title_sort_by">Řadit podle…</string>

View File

@@ -44,7 +44,7 @@
<string name="afc_pmsg_filename_is_invalid">Filnavnet \"%1$s\" er ugyldigt</string>
<string name="afc_pmsg_max_file_count_allowed">… har for mange filer; maks. tilladt: %1$,d</string>
<string name="afc_pmsg_unknown_error">Ukendt fejl: %1$s</string>
<string name="afc_root">Root</string>
<string name="afc_root">Rod</string>
<string name="afc_title_advanced_selection">Vælg…</string>
<string name="afc_title_confirmation">Bekræftelse</string>
<string name="afc_title_date">Dato</string>

View File

@@ -44,13 +44,13 @@
<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>
<string name="afc_root">Start</string>
<string name="afc_root">Root</string>
<string name="afc_title_advanced_selection">Wählen…</string>
<string name="afc_title_confirmation">Bestätigung</string>
<string name="afc_title_date">Datum</string>
<string name="afc_title_error">Fehler</string>
<string name="afc_title_info">Info</string>
<string name="afc_title_name">Titel</string>
<string name="afc_title_name">Name</string>
<string name="afc_title_save_as">Speichern unter…</string>
<string name="afc_title_size">Größe</string>
<string name="afc_title_sort_by">Sortieren nach…</string>

View File

@@ -56,4 +56,16 @@
<string name="afc_title_size">اندازه‌</string>
<string name="afc_title_sort_by">مرتب‌کردن بر اساس…</string>
<string name="afc_yesterday">دیروز</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">انتخاب پوشه…</item>
<item quantity="other">انتخاب پوشه‌ها…</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="one">انتخاب فایل...</item>
<item quantity="other">انتخاب فایل‌ها</item>
</plurals>
<plurals name="afc_title_choose_files_directories">
<item quantity="one">انتخاب فایل / پوشه…</item>
<item quantity="other">انتخاب فایل‌ها / پوشه‌ها…</item>
</plurals>
</resources>

View File

@@ -44,7 +44,7 @@
<string name="afc_pmsg_filename_is_invalid">Tiedostonimi \"%1$s\" ei kelpaa</string>
<string name="afc_pmsg_max_file_count_allowed">...liikaa tiedostoja, maksimimäärä on %1$,d</string>
<string name="afc_pmsg_unknown_error">Tuntematon virhe: %1$s</string>
<string name="afc_root">Juurihakemisto</string>
<string name="afc_root">Juuri</string>
<string name="afc_title_advanced_selection">Valitse</string>
<string name="afc_title_confirmation">Vahvista</string>
<string name="afc_title_date">Pvm.</string>

View File

@@ -44,7 +44,7 @@
<string name="afc_pmsg_filename_is_invalid">O nome de arquivo \"%1$s\" non é válido</string>
<string name="afc_pmsg_max_file_count_allowed">…ten máis arquivos, máximo permitido: %1$,d</string>
<string name="afc_pmsg_unknown_error">Erro descoñecido: %1$s</string>
<string name="afc_root">Raíz</string>
<string name="afc_root">Root</string>
<string name="afc_title_advanced_selection">Seleccionar…</string>
<string name="afc_title_confirmation">Confirmación</string>
<string name="afc_title_date">Data</string>

View File

@@ -44,7 +44,7 @@
<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_root">שורש</string>
<string name="afc_title_advanced_selection">בחר...</string>
<string name="afc_title_confirmation">אישור</string>
<string name="afc_title_date">תאריך</string>

View File

@@ -57,7 +57,7 @@
<string name="afc_yesterday">Tegnap</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">Könyvtár választása...</item>
<item quantity="other">Könyvtárak választása</item>
<item quantity="other">Könyvtárak választása...</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="one">Fájl kiválasztása...</item>

View File

@@ -44,7 +44,7 @@
<string name="afc_pmsg_filename_is_invalid">Il nome del file \"%1$s\" non è valido</string>
<string name="afc_pmsg_max_file_count_allowed">…ha più file, massimo consentito: %1$,d</string>
<string name="afc_pmsg_unknown_error">Errore sconosciuto: %1$s</string>
<string name="afc_root">Radice</string>
<string name="afc_root">Principale</string>
<string name="afc_title_advanced_selection">Seleziona…</string>
<string name="afc_title_confirmation">Conferma</string>
<string name="afc_title_date">Data</string>

View File

@@ -36,7 +36,7 @@
<string name="afc_pmsg_cannot_access_dir">\"%1$s\" にアクセスできません。</string>
<string name="afc_pmsg_cannot_create_folder">フォルダ \"%1$s\" を作成できません。</string>
<string name="afc_pmsg_cannot_delete_file">%1$s \"%2$s\" を削除できません。</string>
<string name="afc_pmsg_confirm_delete_file">%1$s \"%2$s\"? を削除してもよろしいですか?</string>
<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>
@@ -50,7 +50,7 @@
<string name="afc_title_date">日付</string>
<string name="afc_title_error">エラー</string>
<string name="afc_title_info">情報</string>
<string name="afc_title_name">タイトル</string>
<string name="afc_title_name">名前</string>
<string name="afc_title_save_as">名前を付けて保存</string>
<string name="afc_title_size">サイズ</string>
<string name="afc_title_sort_by">並べ替え</string>

View File

@@ -9,7 +9,7 @@
<string name="afc_root">റൂട്ട്</string>
<string name="afc_title_name">പേര്</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">ഫോൾഡർ ചേർക്കുക</item>
<item quantity="one">ഫോൾഡർ തിരഞ്ഞെടുക്കുക</item>
<item quantity="other">ഫോൾഡറുകൾ ചേർക്കുക</item>
</plurals>
<plurals name="afc_title_choose_files">

View File

@@ -44,7 +44,7 @@
<string name="afc_pmsg_filename_is_invalid">Filnavnet \"%1$s\" er ugyldig</string>
<string name="afc_pmsg_max_file_count_allowed">…har flere filer, maks tillatt: %1$,d</string>
<string name="afc_pmsg_unknown_error">Ukjent feil: %1$s</string>
<string name="afc_root">Rot</string>
<string name="afc_root">Toppnivå</string>
<string name="afc_title_advanced_selection">Velg…</string>
<string name="afc_title_confirmation">Bekreftelse</string>
<string name="afc_title_date">Dato</string>

View File

@@ -55,4 +55,22 @@
<string name="afc_title_size">Rozmiar</string>
<string name="afc_title_sort_by">Sortuj według…</string>
<string name="afc_yesterday">Wczoraj</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">Wybierz katalog...</item>
<item quantity="few">Wybierz katalog...</item>
<item quantity="many">Wybierz katalogi...</item>
<item quantity="other">Wybierz katalog...</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="one">Wybierz pliki…</item>
<item quantity="few">Wybierz pliki…</item>
<item quantity="many">Wybierz pliki…</item>
<item quantity="other">Wybierz pliki…</item>
</plurals>
<plurals name="afc_title_choose_files_directories">
<item quantity="one">Wybierz plik/folder…</item>
<item quantity="few">Wybierz pliki/foldery...</item>
<item quantity="many">Wybierz pliki/foldery…</item>
<item quantity="other">Wybierz plik/folder…</item>
</plurals>
</resources>

View File

@@ -56,17 +56,15 @@
<string name="afc_title_sort_by">Ordenar por…</string>
<string name="afc_yesterday">Ontem</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">Escolher pasta&#8230;
Escolher pastas&#8230;</item>
<item quantity="other">Escolher pasta&#8230;
Escolher pastas&#8230;</item>
<item quantity="one">Escolher pasta</item>
<item quantity="other">Escolher pastas</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="one">Escolher arquivo&#8230;</item>
<item quantity="other">Escolher arquivos&#8230;</item>
<item quantity="one">Escolher arquivo</item>
<item quantity="other">Escolher arquivos</item>
</plurals>
<plurals name="afc_title_choose_files_directories">
<item quantity="one">Escolher arquivo/ pasta&#8230;</item>
<item quantity="other">Escolher arquivos/ pastas&#8230;</item>
<item quantity="one">Escolher arquivo/pasta</item>
<item quantity="other">Escolher arquivos/pastas</item>
</plurals>
</resources>

View File

@@ -25,7 +25,7 @@
<string name="afc_msg_app_doesnot_have_permission_to_create_files">Esta aplicação não tem permissão para criar ficheiros/pastas</string>
<string name="afc_msg_app_doesnot_have_permission_to_delete_files">Esta aplicação não tem permissão para apagar ficheiros/pastas</string>
<string name="afc_msg_cancelled">Cancelada</string>
<string name="afc_msg_cannot_connect_to_file_provider_service">Não foi possível ligar ao serviço provedor de ficheiros</string>
<string name="afc_msg_cannot_connect_to_file_provider_service">Não foi possível ligar ao serviço fornecedor de ficheiros</string>
<string name="afc_msg_cannot_create_new_folder_here">Não pode criar uma nova pasta aqui</string>
<string name="afc_msg_cannot_save_a_file_here">Não pode guardar um ficheiro aqui</string>
<string name="afc_msg_done">Terminado</string>
@@ -35,8 +35,8 @@
<string name="afc_phone">Telefone</string>
<string name="afc_pmsg_cannot_access_dir">Não foi possível aceder a \"%1$s\"</string>
<string name="afc_pmsg_cannot_create_folder">Não foi possível criar a pasta \"%1$s\"</string>
<string name="afc_pmsg_cannot_delete_file">Não foi possível apagar %1$s \"%2$s\"</string>
<string name="afc_pmsg_confirm_delete_file">Tem certeza de que quer apagar %1$s \"%2$s\"?</string>
<string name="afc_pmsg_cannot_delete_file">Não foi possível eliminar %1$s \"%2$s\"</string>
<string name="afc_pmsg_confirm_delete_file">Tem a certeza de que quer eliminar %1$s \"%2$s\"?</string>
<string name="afc_pmsg_confirm_replace_file">O ficheiro \"%1$s\" já existe.\n\nPretende substituí-lo?</string>
<string name="afc_pmsg_deleting_file">A apagar %1$s \"%2$s\"…</string>
<string name="afc_pmsg_file_has_been_deleted">%1$s \"%2$s\" foi apagado</string>
@@ -44,7 +44,7 @@
<string name="afc_pmsg_filename_is_invalid">O nome de ficheiro \"%1$s\" é inválido</string>
<string name="afc_pmsg_max_file_count_allowed">…tem mais ficheiros, máximo permitido %1$,d</string>
<string name="afc_pmsg_unknown_error">Erro desconhecido: %1$s</string>
<string name="afc_root">Raiz</string>
<string name="afc_root">Root</string>
<string name="afc_title_advanced_selection">Selecionar…</string>
<string name="afc_title_confirmation">Confirmação</string>
<string name="afc_title_date">Data</string>
@@ -55,4 +55,16 @@
<string name="afc_title_size">Tamanho</string>
<string name="afc_title_sort_by">Ordenar por…</string>
<string name="afc_yesterday">Ontem</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">Escolher pasta…</item>
<item quantity="other">Escolher pastas…</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="one">Escolher ficheiro...</item>
<item quantity="other">Escolher ficheiros...</item>
</plurals>
<plurals name="afc_title_choose_files_directories">
<item quantity="one">Escolher ficheiro/pasta…</item>
<item quantity="other">Escolher ficheiros/pastas…</item>
</plurals>
</resources>

View File

@@ -50,13 +50,13 @@
<string name="afc_title_date">Дата</string>
<string name="afc_title_error">Ошибка</string>
<string name="afc_title_info">Информация</string>
<string name="afc_title_name">Название</string>
<string name="afc_title_name">Имя</string>
<string name="afc_title_save_as">Сохранить как…</string>
<string name="afc_title_size">Размер</string>
<string name="afc_title_sort_by">Сортировать по…</string>
<string name="afc_yesterday">Вчера</string>
<plurals name="afc_title_choose_directories">
<item quantity="one">Выберите папку…</item>
<item quantity="one">Выберите папку…</item>
<item quantity="few">Выберите папки…</item>
<item quantity="many">Выберите папок…</item>
<item quantity="other">Выберите папки…</item>

View File

@@ -44,7 +44,7 @@
<string name="afc_pmsg_filename_is_invalid">Ime datoteke \"%1$s\" je neveljavno</string>
<string name="afc_pmsg_max_file_count_allowed">… ima več datotek, največje dovoljeno št.: %1,d</string>
<string name="afc_pmsg_unknown_error">Neznana napaka: %1$s</string>
<string name="afc_root">Korenska mapa</string>
<string name="afc_root">Koren</string>
<string name="afc_title_advanced_selection">Izberite …</string>
<string name="afc_title_confirmation">Potrditev</string>
<string name="afc_title_date">Datum</string>

View File

@@ -50,7 +50,7 @@
<string name="afc_title_date">Дата</string>
<string name="afc_title_error">Помилка</string>
<string name="afc_title_info">Інформація</string>
<string name="afc_title_name">Назва</string>
<string name="afc_title_name">Ім’я</string>
<string name="afc_title_save_as">Зберегти як…</string>
<string name="afc_title_size">Розмір</string>
<string name="afc_title_sort_by">Сортувати за…</string>

View File

@@ -44,7 +44,7 @@
<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">根目錄</string>
<string name="afc_root">Root</string>
<string name="afc_title_advanced_selection">請選擇...</string>
<string name="afc_title_confirmation">確認</string>
<string name="afc_title_date">日期</string>

View File

@@ -56,12 +56,12 @@
<string name="afc_title_sort_by">排序方式</string>
<string name="afc_yesterday">昨天</string>
<plurals name="afc_title_choose_directories">
<item quantity="other">选择文件夹...</item>
<item quantity="other">选择文件夹</item>
</plurals>
<plurals name="afc_title_choose_files">
<item quantity="other">选择文件...</item>
<item quantity="other">选择文件</item>
</plurals>
<plurals name="afc_title_choose_files_directories">
<item quantity="other">选择文件/ 文件夹...</item>
<item quantity="other">选择文件 / 文件夹</item>
</plurals>
</resources>

View File

@@ -176,6 +176,7 @@ namespace keepass2android
public void StopListening()
{
Kp2aLog.Log("Fingerprint: StopListening " + (_biometricPrompt != null ? " having prompt " : " without prompt"));
_biometricAuthCallbackAdapter?.IgnoreNextError();
_biometricPrompt?.CancelAuthentication();
}
@@ -272,7 +273,24 @@ namespace keepass2android
try
{
_keystore.Load(null);
var aliases = _keystore.Aliases();
if (aliases == null)
{
Kp2aLog.Log("KS: no aliases");
}
else
{
while (aliases.HasMoreElements)
{
var o = aliases.NextElement();
Kp2aLog.Log("alias: " + o?.ToString());
}
Kp2aLog.Log("KS: end aliases");
}
var key = _keystore.GetKey(GetAlias(_keyId), null);
if (key == null)
throw new Exception("Failed to init cipher for fingerprint Init: key is null");
var ivParams = new IvParameterSpec(_iv);
_cipher.Init(CipherMode.DecryptMode, key, ivParams);
@@ -286,27 +304,27 @@ namespace keepass2android
}
catch (KeyStoreException e)
{
throw new RuntimeException(FailedToInitCipher, e);
throw new RuntimeException(FailedToInitCipher + " (keystore)", e);
}
catch (CertificateException e)
{
throw new RuntimeException(FailedToInitCipher, e);
throw new RuntimeException(FailedToInitCipher + " (CertificateException)", e);
}
catch (UnrecoverableKeyException e)
{
throw new RuntimeException(FailedToInitCipher, e);
throw new RuntimeException(FailedToInitCipher + " (UnrecoverableKeyException)", e);
}
catch (IOException e)
{
throw new RuntimeException(FailedToInitCipher, e);
throw new RuntimeException(FailedToInitCipher + " (IOException)", e);
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException(FailedToInitCipher, e);
throw new RuntimeException(FailedToInitCipher + " (NoSuchAlgorithmException)", e);
}
catch (InvalidKeyException e)
{
throw new RuntimeException(FailedToInitCipher, e);
throw new RuntimeException(FailedToInitCipher + " (InvalidKeyException)" + e.ToString(), e);
}
}
@@ -349,14 +367,19 @@ namespace keepass2android
try
{
_keystore.Load(null);
_keyGen.Init(new KeyGenParameterSpec.Builder(GetAlias(_keyId),
KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(GetAlias(_keyId),
KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
.SetBlockModes(KeyProperties.BlockModeCbc)
// Require the user to authenticate with biometry to authorize every use
// of the key
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingPkcs7)
.SetUserAuthenticationRequired(true)
.SetUserAuthenticationRequired(true);
if ((int)Build.VERSION.SdkInt >= 24)
builder.SetInvalidatedByBiometricEnrollment(true);
_keyGen.Init(
builder
.Build());
_keyGen.GenerateKey();
}

View File

@@ -27,7 +27,9 @@ namespace keepass2android
AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(ctx, Android.Resource.Style.ThemeHoloLightDialog));
builder.SetTitle(ctx.GetString(Resource.String.ChangeLog_title));
List<string> changeLog = new List<string>{
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_08d, "1.08d"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_09a, "1.09a"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_08d, "1.08d"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_08c, "1.08c"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_08b, "1.08b"),
BuildChangelogString(ctx, Resource.Array.ChangeLog_1_08, "1.08"),

View File

@@ -429,7 +429,9 @@ namespace keepass2android
// Update last access time.
Entry.Touch(false);
if (PwDefs.IsTanEntry(Entry) && prefs.GetBoolean(GetString(Resource.String.TanExpiresOnUse_key), Resources.GetBoolean(Resource.Boolean.TanExpiresOnUse_default)) && ((Entry.Expires == false) || Entry.ExpiryTime > DateTime.Now))
if (PwDefs.IsTanEntry(Entry)
&& prefs.GetBoolean(GetString(Resource.String.TanExpiresOnUse_key), Resources.GetBoolean(Resource.Boolean.TanExpiresOnUse_default))
&& ((Entry.Expires == false) || Entry.ExpiryTime > DateTime.Now))
{
PwEntry backupEntry = Entry.CloneDeep();
Entry.ExpiryTime = DateTime.Now;
@@ -604,11 +606,10 @@ namespace keepass2android
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
string binaryDirectory = prefs.GetString(GetString(Resource.String.BinaryDirectory_key),
GetString(Resource.String.BinaryDirectory_default));
if (writeToCacheDirectory)
{
binaryDirectory = CacheDir.Path + File.Separator + AttachmentContentProvider.AttachmentCacheSubDir;
string binaryDirectory = CacheDir.Path + File.Separator + AttachmentContentProvider.AttachmentCacheSubDir;
string filepart = key;
Java.Lang.String javaFilename = new Java.Lang.String(filepart);
@@ -886,9 +887,10 @@ namespace keepass2android
popupItems.Add(new CopyToClipboardPopupMenuIcon(this, _stringViews[fieldKey]));
if (isProtected)
popupItems.Add(new ToggleVisibilityPopupMenuItem(this));
if (_stringViews[fieldKey].Text.StartsWith(KeePass.AndroidAppScheme)
if (fieldKey != PwDefs.UrlField //url already has a go-to-url menu
&& (_stringViews[fieldKey].Text.StartsWith(KeePass.AndroidAppScheme)
|| _stringViews[fieldKey].Text.StartsWith("http://")
|| _stringViews[fieldKey].Text.StartsWith("https://"))
|| _stringViews[fieldKey].Text.StartsWith("https://")))
{
popupItems.Add(new GotoUrlMenuItem(this, fieldKey));
}
@@ -1177,7 +1179,13 @@ namespace keepass2android
{
case Resource.Id.menu_donate:
return Util.GotoDonateUrl(this);
case Resource.Id.menu_delete:
case Resource.Id.menu_move:
var navMove = new NavigateToFolderAndLaunchMoveElementTask(App.Kp2a.CurrentDb, Entry.ParentGroup, new List<PwUuid>() {Entry.Uuid}, false);
AppTask = navMove;
navMove.SetActivityResult(this, Result.Ok);
Finish();
return true;
case Resource.Id.menu_delete:
DeleteEntry task = new DeleteEntry(this, App.Kp2a, Entry,
new ActionOnFinish(this, (success, message, activity) => { if (success) { RequiresRefresh(); Finish();}}));
task.Start();

View File

@@ -33,12 +33,17 @@ using KeePassLib.Security;
using Android.Content.PM;
using System.IO;
using System.Globalization;
using System.Net;
using System.Text;
using Android.Content.Res;
using Android.Database;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Util;
using keepass2android.Io;
using KeePassLib.Serialization;
using KeeTrayTOTP.Libraries;
using PluginTOTP;
using Debug = System.Diagnostics.Debug;
using File = System.IO.File;
using Object = Java.Lang.Object;
@@ -287,7 +292,31 @@ namespace keepass2android
EditAdvancedString(ees.FindViewById(Resource.Id.edit_extra));
};
SetAddExtraStringEnabled();
FindViewById(Resource.Id.entry_extras_container).Visibility =
Button configureTotpButton = (Button)FindViewById(Resource.Id.configure_totp);
configureTotpButton.Visibility = CanConfigureOtpSettings() ? ViewStates.Gone : ViewStates.Visible;
configureTotpButton.Click += (sender, e) =>
{
bool added = false;
View ees = FindExtraEditSection("otp");
if (ees == null)
{
LinearLayout container = (LinearLayout) FindViewById(Resource.Id.advanced_container);
KeyValuePair<string, ProtectedString> pair =
new KeyValuePair<string, ProtectedString>("otp", new ProtectedString(true, ""));
ees = CreateExtraStringView(pair);
container.AddView(ees);
added = true;
}
EditTotpString(ees.FindViewById(Resource.Id.edit_extra));
};
FindViewById(Resource.Id.entry_extras_container).Visibility =
State.EditMode.ShowAddExtras || State.Entry.Strings.Any(s => !PwDefs.IsStandardField(s.Key)) ? ViewStates.Visible : ViewStates.Gone;
FindViewById(Resource.Id.entry_binaries_container).Visibility =
State.EditMode.ShowAddAttachments || State.Entry.Binaries.Any() ? ViewStates.Visible : ViewStates.Gone;
@@ -402,9 +431,17 @@ namespace keepass2android
private void SetAddExtraStringEnabled()
{
((Button)FindViewById(Resource.Id.add_advanced)).Visibility = (!App.Kp2a.CurrentDb.DatabaseFormat.CanHaveCustomFields || !State.EditMode.ShowAddExtras) ? ViewStates.Gone : ViewStates.Visible;
((Button)FindViewById(Resource.Id.configure_totp)).Visibility = CanConfigureOtpSettings() ? ViewStates.Gone : ViewStates.Visible;
}
private void MakePasswordVisibleOrHidden()
private bool CanConfigureOtpSettings()
{
return (!App.Kp2a.CurrentDb.DatabaseFormat.CanHaveCustomFields || !State.EditMode.ShowAddExtras)
&& (new Kp2aTotp().TryGetAdapter(new PwEntryOutput(State.Entry, App.Kp2a.CurrentDb)) == null || (State.Entry.Strings.GetKeys().Contains("otp"))) //only allow to edit KeeWeb/KeepassXC style otps
;
}
private void MakePasswordVisibleOrHidden()
{
EditText password = (EditText) FindViewById(Resource.Id.entry_password);
TextView confpassword = (TextView) FindViewById(Resource.Id.entry_confpassword);
@@ -942,7 +979,8 @@ namespace keepass2android
binariesGroup.Visibility = ViewStates.Visible;
FindViewById(Resource.Id.entry_binaries_container).Visibility = ViewStates.Visible;
((Button)FindViewById(Resource.Id.add_advanced)).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.entry_extras_container).Visibility = ViewStates.Visible;
((Button)FindViewById(Resource.Id.configure_totp)).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.entry_extras_container).Visibility = ViewStates.Visible;
return true;
case Android.Resource.Id.Home:
@@ -1020,6 +1058,7 @@ namespace keepass2android
if (type == "bool")
{
RelativeLayout ees = (RelativeLayout)LayoutInflater.Inflate(Resource.Layout.entry_edit_section_bool, null);
ees.Tag = pair.Key;
var keyView = ((TextView)ees.FindViewById(Resource.Id.extrakey));
var checkbox = ((CheckBox)ees.FindViewById(Resource.Id.checkbox));
var valueView = ((TextView)ees.FindViewById(Resource.Id.value));
@@ -1037,6 +1076,7 @@ namespace keepass2android
else if (type == "file")
{
RelativeLayout ees = (RelativeLayout)LayoutInflater.Inflate(Resource.Layout.entry_edit_section_file, null);
ees.Tag = pair.Key;
var keyView = ((TextView)ees.FindViewById(Resource.Id.extrakey));
var titleView = ((TextView)ees.FindViewById(Resource.Id.title));
keyView.Text = pair.Key;
@@ -1054,6 +1094,7 @@ namespace keepass2android
else
{
RelativeLayout ees = (RelativeLayout)LayoutInflater.Inflate(Resource.Layout.entry_edit_section, null);
ees.Tag = pair.Key;
var keyView = ((TextView)ees.FindViewById(Resource.Id.extrakey));
var titleView = ((TextView)ees.FindViewById(Resource.Id.title));
keyView.Text = pair.Key;
@@ -1090,8 +1131,184 @@ namespace keepass2android
}
}
private void EditTotpString(View sender)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
View dlgView = LayoutInflater.Inflate(Resource.Layout.
configure_totp_dialog, null);
private void EditAdvancedString(View sender)
builder.SetView(dlgView);
builder.SetNegativeButton(Android.Resource.String.Cancel, (o, args) => { });
builder.SetPositiveButton(Android.Resource.String.Ok, (o, args) =>
{
var targetField = ((TextView)((View)sender.Parent).FindViewById(Resource.Id.value));
if (targetField != null)
{
string entryTitle = Util.GetEditText(this, Resource.Id.entry_title);
string username = Util.GetEditText(this, Resource.Id.entry_user_name);
string secret = dlgView.FindViewById<TextView>(Resource.Id.totp_secret_key).Text;
string totpLength = dlgView.FindViewById<EditText>(Resource.Id.totp_length).Text;
string timeStep = dlgView.FindViewById<EditText>(Resource.Id.totp_time_step).Text;
var checkedTotpId = (int)dlgView.FindViewById<RadioGroup>(Resource.Id.totp_encoding).CheckedRadioButtonId;
TotpEncoding encoding = (checkedTotpId == Resource.Id.totp_encoding_steam)
? TotpEncoding.Steam : (checkedTotpId == Resource.Id.totp_encoding_rfc6238 ? TotpEncoding.Default : TotpEncoding.Custom);
var algorithm = (int)dlgView.FindViewById<Spinner>(Resource.Id.totp_algorithm).SelectedItemPosition;
targetField.Text = BuildOtpString(entryTitle, username, secret, totpLength, timeStep, encoding, algorithm);
}
else
{
Toast.MakeText(this, "did not find target field", ToastLength.Long).Show();
}
//not calling State.Entry.Strings.Set(...). We only do this when the user saves the changes.
State.EntryModified = true;
});
Dialog dialog = builder.Create();
dlgView.FindViewById<RadioButton>(Resource.Id.totp_encoding_custom).CheckedChange += (o, args) =>
{
dlgView.FindViewById(Resource.Id.totp_custom_settings_group).Visibility = args.IsChecked ? ViewStates.Visible : ViewStates.Gone;
};
//copy values from entry into dialog
View ees = (View)sender.Parent;
TotpData totpData = new Kp2aTotp().TryGetTotpData(new PwEntryOutput(State.Entry, App.Kp2a.CurrentDb));
if (totpData != null)
{
dlgView.FindViewById<TextView>(Resource.Id.totp_secret_key).Text = totpData.TotpSeed;
if (totpData.Encoder == TotpData.EncoderSteam)
{
dlgView.FindViewById<RadioButton>(Resource.Id.totp_encoding_steam).Checked = true;
}
else if ((totpData.Encoder == TotpData.EncoderRfc6238) && (totpData.IsDefaultRfc6238))
{
dlgView.FindViewById<RadioButton>(Resource.Id.totp_encoding_rfc6238).Checked = true;
}
else
{
dlgView.FindViewById<RadioButton>(Resource.Id.totp_encoding_custom).Checked = true;
}
dlgView.FindViewById<EditText>(Resource.Id.totp_length).Text = totpData.Length;
dlgView.FindViewById<EditText>(Resource.Id.totp_time_step).Text = totpData.Duration;
dlgView.FindViewById <Spinner>(Resource.Id.totp_algorithm).SetSelection(totpData.HashAlgorithm == TotpData.HashSha1 ? 0 : (
totpData.HashAlgorithm == TotpData.HashSha256 ? 1:
(totpData.HashAlgorithm == TotpData.HashSha256 ? 2 : 0)));
dlgView.FindViewById(Resource.Id.totp_custom_settings_group).Visibility = dlgView.FindViewById<RadioButton>(Resource.Id.totp_encoding_custom).Checked ? ViewStates.Visible : ViewStates.Gone;
}
_passwordFont.ApplyTo(dlgView.FindViewById<EditText>(Resource.Id.totp_secret_key));
Util.SetNoPersonalizedLearning(dlgView);
dialog.Show();
}
string SanitizeInput(string encodedData)
{
if (encodedData.Length <= 0)
{
return encodedData;
}
StringBuilder newEncodedDataBuilder = new StringBuilder(encodedData);
int i = 0;
foreach (var ch in encodedData)
{
switch (ch)
{
case '0':
newEncodedDataBuilder[i++] = 'O';
break;
case '1':
newEncodedDataBuilder[i++] = 'L';
break;
case '8':
newEncodedDataBuilder[i++] = 'B';
break;
default:
if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ('2' <= ch && ch <= '7'))
{
newEncodedDataBuilder[i++] = ch;
}
break;
}
}
string newEncodedData = newEncodedDataBuilder.ToString().Substring(0, i);
return AddPadding(newEncodedData);
}
string AddPadding(string encodedData)
{
if (encodedData.Length <= 0 || encodedData.Length % 8 == 0) {
return encodedData;
}
int rBytes = encodedData.Length % 8;
// rBytes must be a member of {2, 4, 5, 7}
if (1 == rBytes || 3 == rBytes || 6 == rBytes) {
return encodedData;
}
string newEncodedData = encodedData;
for (int nPads = 8 - rBytes; nPads > 0; --nPads)
{
newEncodedData += "=";
}
return newEncodedData;
}
enum TotpEncoding
{
Default, Steam, Custom
}
private string BuildOtpString(string entryTitle, string userName, string secret, string totpLength, string timeStep, TotpEncoding encoding, int algorithm)
{
string entryEncoded = string.IsNullOrWhiteSpace(entryTitle)
? "Keepass2Android"
: System.Uri.EscapeUriString(entryTitle);
return $"otpauth://totp/{entryEncoded}:{System.Uri.EscapeUriString(userName)}?" +
$"secret={SanitizeInput(secret)}" +
$"&issuer={ entryEncoded}"
+ (encoding != TotpEncoding.Custom? "" : $"&period={timeStep}&digits={totpLength}&algorithm={AlgorithmIndexToString(algorithm)}") +
(encoding == TotpEncoding.Steam ? "&encoder=steam" : "");
}
private string AlgorithmIndexToString(in int algorithm)
{
switch (algorithm)
{
case 0:
return "SHA1";
case 1:
return "SHA256";
case 2:
return "SHA512";
default:
return "";
}
}
private void EditAdvancedString(View sender)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
View dlgView = LayoutInflater.Inflate(Resource.Layout.

View File

@@ -35,7 +35,7 @@ namespace keepass2android
protected override bool FitSystemWindows(Rect insets)
{
if (Build.VERSION.SdkInt >= Build.VERSION_CODES.Kitkat)
if (Util.IsKitKatOrLater)
{
// Intentionally do not modify the bottom inset. For some reason,
// if the bottom inset is modified, window resizing stops working.

View File

@@ -16,22 +16,58 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Android.App;
using Android.App.Admin;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.OS;
using Android.Preferences;
using Android.Views;
using Android.Widget;
using Java.Util;
using KeePassLib.Cryptography;
using Newtonsoft.Json;
using OtpKeyProv;
namespace keepass2android
{
[Activity(Label = "@string/app_name", Theme = "@style/MyTheme_ActionBar", WindowSoftInputMode = SoftInput.StateHidden, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden)]
public class GeneratePasswordActivity : LockCloseActivity {
private readonly int[] _buttonIds = new[] {Resource.Id.btn_length6, Resource.Id.btn_length8, Resource.Id.btn_length12, Resource.Id.btn_length16};
public class GeneratePasswordActivity :
#if DEBUG
LifecycleAwareActivity
#else
LockCloseActivity
#endif
private ActivityDesign _design;
{
private readonly int[] _buttonLengthButtonIds = new[] {Resource.Id.btn_length6,
Resource.Id.btn_length8,
Resource.Id.btn_length12,
Resource.Id.btn_length16,
Resource.Id.btn_length24,
Resource.Id.btn_length32};
private readonly int[] _checkboxIds = new[] {Resource.Id.cb_uppercase,
Resource.Id.cb_lowercase,
Resource.Id.cb_digits,
Resource.Id.cb_minus,
Resource.Id.cb_underline,
Resource.Id.cb_space,
Resource.Id.cb_specials,
Resource.Id.cb_specials_extended,
Resource.Id.cb_brackets,
Resource.Id.cb_at_least_one_from_each_group,
Resource.Id.cb_exclude_lookalike
};
PasswordFont _passwordFont = new PasswordFont();
private ActivityDesign _design;
public GeneratePasswordActivity()
{
_design = new ActivityDesign(this);
@@ -47,11 +83,56 @@ namespace keepass2android
{
Intent i = new Intent(act, typeof(GeneratePasswordActivity));
#if DEBUG
#else
i.PutExtra(NoLockCheck, true);
#endif
act.StartActivityForResult(i, 0);
}
private PasswordProfiles _profiles;
private bool _updateDisabled = false;
class PasswordProfiles
{
public List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>> Profiles { get; set; }
public PasswordGenerator.CombinedKeyOptions LastUsedSettings { get; set; }
public int? TryFindLastUsedProfileIndex()
{
for (int i=0;i<Profiles.Count;i++)
{
var kvp = Profiles[i];
if (kvp.Value.Equals(LastUsedSettings))
return i;
}
return null;
}
public void Add(string key, PasswordGenerator.CombinedKeyOptions options)
{
for (var index = 0; index < Profiles.Count; index++)
{
var kvp = Profiles[index];
if (kvp.Key == key)
{
Profiles[index] = new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(key, options);
return;
}
}
Profiles.Add(new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(key, options));
}
public void Remove(in int profileIndex)
{
Profiles.RemoveAt(profileIndex);
}
}
protected override void OnCreate(Bundle savedInstanceState) {
_design.ApplyTheme();
@@ -61,17 +142,74 @@ namespace keepass2android
SetResult(KeePass.ExitNormal);
var prefs = GetPreferences(FileCreationMode.Private);
((CheckBox) FindViewById(Resource.Id.cb_uppercase)).Checked = prefs.GetBoolean("cb_uppercase", true);
((CheckBox)FindViewById(Resource.Id.cb_lowercase)).Checked = prefs.GetBoolean("cb_lowercase", true);
((CheckBox)FindViewById(Resource.Id.cb_digits)).Checked = prefs.GetBoolean("cb_digits", true);
((CheckBox)FindViewById(Resource.Id.cb_minus)).Checked = prefs.GetBoolean("cb_minus", false);
((CheckBox)FindViewById(Resource.Id.cb_underline)).Checked = prefs.GetBoolean("cb_underline", false);
((CheckBox)FindViewById(Resource.Id.cb_space)).Checked = prefs.GetBoolean("cb_space", false);
((CheckBox)FindViewById(Resource.Id.cb_specials)).Checked = prefs.GetBoolean("cb_specials", false);
((CheckBox)FindViewById(Resource.Id.cb_brackets)).Checked = prefs.GetBoolean("cb_brackets", false);
((EditText)FindViewById(Resource.Id.length)).Text = prefs.GetInt("length", 12).ToString(CultureInfo.InvariantCulture);
foreach (int id in _buttonIds) {
string jsonOptions = prefs.GetString("password_generator_profiles", null);
if (jsonOptions != null)
{
try
{
_profiles = JsonConvert.DeserializeObject<PasswordProfiles>(jsonOptions);
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
}
else
{
PasswordGenerator.CombinedKeyOptions options = new PasswordGenerator.CombinedKeyOptions()
{
PasswordGenerationOptions = new PasswordGenerator.PasswordGenerationOptions()
{
Length = prefs.GetInt("length", 12),
UpperCase = prefs.GetBoolean("cb_uppercase", true),
LowerCase = prefs.GetBoolean("cb_lowercase", true),
Digits = prefs.GetBoolean("cb_digits", true),
Minus = prefs.GetBoolean("cb_minus", false),
Underline = prefs.GetBoolean("cb_underline", false),
Space = prefs.GetBoolean("cb_space", false),
Specials = prefs.GetBoolean("cb_specials", false),
SpecialsExtended = false,
Brackets = prefs.GetBoolean("cb_brackets", false)
}
};
_profiles = new PasswordProfiles()
{
LastUsedSettings = options,
Profiles = GetDefaultProfiles()
};
}
_profiles ??= new PasswordProfiles();
_profiles.LastUsedSettings ??= new PasswordGenerator.CombinedKeyOptions()
{
PasswordGenerationOptions = new PasswordGenerator.PasswordGenerationOptions()
{Length = 7, UpperCase = true, LowerCase = true, Digits = true}
};
_profiles.Profiles ??= new List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>>();
_updateDisabled = true;
PopulateFieldsFromOptions(_profiles.LastUsedSettings);
_updateDisabled = false;
var profileSpinner = UpdateProfileSpinner();
profileSpinner.ItemSelected += (sender, args) =>
{
if (profileSpinner.SelectedItemPosition > 0)
{
_profiles.LastUsedSettings = _profiles.Profiles[profileSpinner.SelectedItemPosition - 1].Value;
_updateDisabled = true;
PopulateFieldsFromOptions(_profiles.LastUsedSettings);
_updateDisabled = false;
UpdatePassword();
}
};
foreach (int id in _buttonLengthButtonIds) {
Button button = (Button) FindViewById(id);
button.Click += (sender, e) =>
{
@@ -79,17 +217,36 @@ namespace keepass2android
EditText editText = (EditText) FindViewById(Resource.Id.length);
editText.Text = b.Text;
};
}
Button genPassButton = (Button) FindViewById(Resource.Id.generate_password_button);
genPassButton.Click += (sender, e) => {
String password = GeneratePassword();
EditText txtPassword = (EditText) FindViewById(Resource.Id.password_edit);
txtPassword.Text = password;
};
FindViewById<EditText>(Resource.Id.length).TextChanged += (sender, args) => UpdatePassword();
FindViewById<EditText>(Resource.Id.wordcount).TextChanged += (sender, args) => UpdatePassword();
FindViewById<EditText>(Resource.Id.wordseparator).TextChanged += (sender, args) => UpdatePassword();
foreach (int id in _checkboxIds)
{
FindViewById<CheckBox>(id).CheckedChange += (sender, args) => UpdatePassword();
}
var mode_spinner = FindViewById<Spinner>(Resource.Id.spinner_password_generator_mode);
mode_spinner.ItemSelected += (sender, args) =>
{
FindViewById(Resource.Id.passphraseOptions).Visibility =
mode_spinner.SelectedItemPosition == 0 ? ViewStates.Gone : ViewStates.Visible;
FindViewById(Resource.Id.passwordOptions).Visibility =
mode_spinner.SelectedItemPosition == 1 ? ViewStates.Gone : ViewStates.Visible;
UpdatePassword();
};
FindViewById<Spinner>(Resource.Id.spinner_password_generator_case_mode).ItemSelected += (sender, args) =>
{
UpdatePassword();
};
Button genPassButton = (Button) FindViewById(Resource.Id.generate_password_button);
genPassButton.Click += (sender, e) => { UpdatePassword(); };
@@ -114,62 +271,323 @@ namespace keepass2android
Finish();
};
EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit);
txtPasswordToSet.Text = GeneratePassword();
FindViewById(Resource.Id.btn_password_generator_profile_save)
.Click += (sender, args) =>
{
var editText = new EditText(this);
new AlertDialog.Builder(this)
.SetMessage(Resource.String.save_password_generation_profile_text)
.SetView(editText)
.SetPositiveButton(Android.Resource.String.Ok, (o, eventArgs) =>
{
_profiles.Add(editText.Text, GetOptions());
UpdateProfileSpinner();
})
.Show();
};
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
FindViewById(Resource.Id.btn_password_generator_profile_delete)
.Click += (sender, args) =>
{
if (profileSpinner.SelectedItemPosition > 0)
{
_profiles.Remove(profileSpinner.SelectedItemPosition-1);
UpdateProfileSpinner();
}
};
EditText txtPasswordToSet = (EditText) FindViewById(Resource.Id.password_edit);
_passwordFont.ApplyTo(txtPasswordToSet);
UpdatePassword();
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.SetHomeButtonEnabled(true);
}
public String GeneratePassword() {
private Spinner UpdateProfileSpinner()
{
string[] profileNames = new List<string> {GetString(Resource.String.custom_settings)}
.Concat(_profiles.Profiles.Select(p => p.Key))
.ToArray();
ArrayAdapter<String> profileArrayAdapter = new ArrayAdapter<String>(this,
Android.Resource.Layout.SimpleSpinnerDropDownItem,
profileNames);
var profileSpinner = FindViewById<Spinner>(Resource.Id.spinner_password_generator_profile);
profileSpinner.Adapter = profileArrayAdapter;
UpdateProfileSpinnerSelection();
return profileSpinner;
}
private static List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>> GetDefaultProfiles()
{
return new List<KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>>()
{
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
"Simple12", new PasswordGenerator.CombinedKeyOptions()
{
PasswordGenerationOptions
= new PasswordGenerator.PasswordGenerationOptions()
{
Length = 12, AtLeastOneFromEachGroup = true, ExcludeLookAlike = true,
Digits = true, LowerCase = true, UpperCase = true
}
}
),
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
"Special12", new PasswordGenerator.CombinedKeyOptions()
{
PasswordGenerationOptions
= new PasswordGenerator.PasswordGenerationOptions()
{
Length = 12, AtLeastOneFromEachGroup = true, ExcludeLookAlike = true,
Digits = true, LowerCase = true, UpperCase = true,Specials = true,Brackets = true
}
}
),
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
"Password64", new PasswordGenerator.CombinedKeyOptions()
{
PasswordGenerationOptions
= new PasswordGenerator.PasswordGenerationOptions()
{
Length = 64, AtLeastOneFromEachGroup = true,
Digits = true, LowerCase = true, UpperCase = true, ExcludeLookAlike = false,Specials = true,Brackets = true, Minus = true, Space = true, SpecialsExtended = true,Underline = true
}
}
),
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
"Passphrase7", new PasswordGenerator.CombinedKeyOptions()
{
PassphraseGenerationOptions
= new PasswordGenerator.PassphraseGenerationOptions()
{
WordCount = 7,
CaseMode = PasswordGenerator.PassphraseGenerationOptions.PassphraseCaseMode.Lowercase,
Separator = " "
}
}
),
new KeyValuePair<string, PasswordGenerator.CombinedKeyOptions>(
"Passphrase5Plus", new PasswordGenerator.CombinedKeyOptions()
{
PassphraseGenerationOptions
= new PasswordGenerator.PassphraseGenerationOptions()
{
WordCount = 5,
CaseMode = PasswordGenerator.PassphraseGenerationOptions.PassphraseCaseMode.PascalCase,
Separator = " "
},
PasswordGenerationOptions = new PasswordGenerator.PasswordGenerationOptions()
{
Length = 2,
AtLeastOneFromEachGroup = true,
ExcludeLookAlike = true,
Digits = true,
Specials = true
}
}
)
};
}
private void PopulateFieldsFromOptions(PasswordGenerator.CombinedKeyOptions combinedOptions)
{
PasswordGenerator.PasswordGenerationOptions passwordOptions = combinedOptions.PasswordGenerationOptions;
if (passwordOptions != null)
{
((CheckBox)FindViewById(Resource.Id.cb_uppercase)).Checked = passwordOptions.UpperCase;
((CheckBox)FindViewById(Resource.Id.cb_lowercase)).Checked = passwordOptions.LowerCase;
((CheckBox)FindViewById(Resource.Id.cb_digits)).Checked = passwordOptions.Digits;
((CheckBox)FindViewById(Resource.Id.cb_minus)).Checked = passwordOptions.Minus;
((CheckBox)FindViewById(Resource.Id.cb_underline)).Checked = passwordOptions.Underline;
((CheckBox)FindViewById(Resource.Id.cb_space)).Checked = passwordOptions.Space;
((CheckBox)FindViewById(Resource.Id.cb_specials)).Checked = passwordOptions.Specials;
((CheckBox)FindViewById(Resource.Id.cb_specials_extended)).Checked = passwordOptions.SpecialsExtended;
((CheckBox)FindViewById(Resource.Id.cb_brackets)).Checked = passwordOptions.Brackets;
((CheckBox)FindViewById(Resource.Id.cb_exclude_lookalike)).Checked = passwordOptions.ExcludeLookAlike;
((CheckBox)FindViewById(Resource.Id.cb_at_least_one_from_each_group)).Checked = passwordOptions.AtLeastOneFromEachGroup;
((EditText)FindViewById(Resource.Id.length)).Text = passwordOptions.Length.ToString(CultureInfo.InvariantCulture);
FindViewById(Resource.Id.passwordOptions).Visibility = ViewStates.Visible;
}
else
{
FindViewById(Resource.Id.passwordOptions).Visibility = ViewStates.Gone;
}
var passphraseOptions = combinedOptions.PassphraseGenerationOptions;
if (passphraseOptions != null)
{
FindViewById<EditText>(Resource.Id.wordcount).Text = passphraseOptions.WordCount.ToString(CultureInfo.InvariantCulture);
FindViewById<EditText>(Resource.Id.wordseparator).Text = passphraseOptions.Separator;
FindViewById<Spinner>(Resource.Id.spinner_password_generator_case_mode)
.SetSelection((int)passphraseOptions.CaseMode);
FindViewById(Resource.Id.passphraseOptions).Visibility = ViewStates.Visible;
}
else
{
FindViewById(Resource.Id.passphraseOptions).Visibility = ViewStates.Gone;
}
int mode;
if (combinedOptions.PasswordGenerationOptions != null &&
combinedOptions.PassphraseGenerationOptions != null)
mode = 2;
else if (combinedOptions.PasswordGenerationOptions == null &&
combinedOptions.PassphraseGenerationOptions != null)
mode = 1;
else mode = 0;
FindViewById<Spinner>(Resource.Id.spinner_password_generator_mode)
.SetSelection(mode);
}
private void UpdatePassword()
{
if (_updateDisabled)
return;
String password = GeneratePassword();
EditText txtPassword = (EditText) FindViewById(Resource.Id.password_edit);
txtPassword.Text = password;
var progressBar = FindViewById<ProgressBar>(Resource.Id.pb_password_strength);
var passwordBits = QualityEstimation.EstimatePasswordBits(password.ToCharArray());
progressBar.Progress = (int)passwordBits;
progressBar.Max = 128;
Color color = new Color(196, 63, 49);
if (passwordBits > 40)
{
color = new Color(219, 152, 55);
}
if (passwordBits > 64)
{
color = new Color(96, 138, 38);
}
if (passwordBits > 100)
{
color = new Color(31, 128, 31);
}
progressBar.ProgressDrawable.SetColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SrcIn));
FindViewById<TextView>(Resource.Id.tv_password_strength).Text = " " + passwordBits + " bits";
UpdateProfileSpinnerSelection();
}
private void UpdateProfileSpinnerSelection()
{
int? lastUsedIndex = _profiles.TryFindLastUsedProfileIndex();
FindViewById<Spinner>(Resource.Id.spinner_password_generator_profile)
.SetSelection(lastUsedIndex != null ? lastUsedIndex.Value + 1 : 0);
}
public String GeneratePassword() {
String password = "";
try {
try
{
PasswordGenerator generator = new PasswordGenerator(this);
int length;
if (!int.TryParse(((EditText) FindViewById(Resource.Id.length)).Text, out length))
{
Toast.MakeText(this, Resource.String.error_wrong_length, ToastLength.Long).Show();
return password;
}
var options = GetOptions();
try
{
password = generator.GeneratePassword(options);
}
catch (Exception e)
{
throw new ArgumentException(GetString(Resource.String.error_pass_gen_type));
}
PasswordGenerator generator = new PasswordGenerator(this);
password = generator.GeneratePassword(length,
((CheckBox) FindViewById(Resource.Id.cb_uppercase)).Checked,
((CheckBox) FindViewById(Resource.Id.cb_lowercase)).Checked,
((CheckBox) FindViewById(Resource.Id.cb_digits)).Checked,
((CheckBox) FindViewById(Resource.Id.cb_minus)).Checked,
((CheckBox) FindViewById(Resource.Id.cb_underline)).Checked,
((CheckBox) FindViewById(Resource.Id.cb_space)).Checked,
((CheckBox) FindViewById(Resource.Id.cb_specials)).Checked,
((CheckBox) FindViewById(Resource.Id.cb_brackets)).Checked);
_profiles.LastUsedSettings = options;
var prefs = GetPreferences(FileCreationMode.Private);
prefs.Edit()
.PutBoolean("cb_uppercase", ((CheckBox) FindViewById(Resource.Id.cb_uppercase)).Checked)
.PutBoolean("cb_lowercase", ((CheckBox) FindViewById(Resource.Id.cb_lowercase)).Checked)
.PutBoolean("cb_digits", ((CheckBox) FindViewById(Resource.Id.cb_digits)).Checked)
.PutBoolean("cb_minus", ((CheckBox) FindViewById(Resource.Id.cb_minus)).Checked)
.PutBoolean("cb_underline", ((CheckBox) FindViewById(Resource.Id.cb_underline)).Checked)
.PutBoolean("cb_space", ((CheckBox) FindViewById(Resource.Id.cb_space)).Checked)
.PutBoolean("cb_specials", ((CheckBox) FindViewById(Resource.Id.cb_specials)).Checked)
.PutBoolean("cb_brackets", ((CheckBox) FindViewById(Resource.Id.cb_brackets)).Checked)
.PutInt("length", length)
.Commit();
} catch (ArgumentException e) {
SaveProfiles();
}
catch (Exception e)
{
Toast.MakeText(this, e.Message, ToastLength.Long).Show();
}
return password;
}
private void SaveProfiles()
{
var prefs = GetPreferences(FileCreationMode.Private);
prefs.Edit()
.PutString("password_generator_profiles", JsonConvert.SerializeObject(_profiles))
.Commit();
}
private PasswordGenerator.CombinedKeyOptions GetOptions()
{
PasswordGenerator.CombinedKeyOptions options = new PasswordGenerator.CombinedKeyOptions();
if (FindViewById(Resource.Id.passphraseOptions).Visibility == ViewStates.Visible)
{
int wordCount;
if (!int.TryParse(((EditText)FindViewById(Resource.Id.wordcount)).Text, out wordCount))
{
throw new Exception(GetString(Resource.String.error_wrong_length));
}
options.PassphraseGenerationOptions =
new PasswordGenerator.PassphraseGenerationOptions()
{
WordCount = wordCount,
Separator = FindViewById<EditText>(Resource.Id.wordseparator).Text,
CaseMode = (PasswordGenerator.PassphraseGenerationOptions.PassphraseCaseMode)FindViewById<Spinner>(Resource.Id.spinner_password_generator_case_mode).SelectedItemPosition
};
}
if (FindViewById(Resource.Id.passwordOptions).Visibility == ViewStates.Visible)
{
int length;
if (!int.TryParse(((EditText) FindViewById(Resource.Id.length)).Text, out length))
{
throw new Exception(GetString(Resource.String.error_wrong_length));
}
options.PasswordGenerationOptions =
new PasswordGenerator.PasswordGenerationOptions()
{
Length = length,
UpperCase = ((CheckBox) FindViewById(Resource.Id.cb_uppercase)).Checked,
LowerCase = ((CheckBox) FindViewById(Resource.Id.cb_lowercase)).Checked,
Digits = ((CheckBox) FindViewById(Resource.Id.cb_digits)).Checked,
Minus = ((CheckBox) FindViewById(Resource.Id.cb_minus)).Checked,
Underline = ((CheckBox) FindViewById(Resource.Id.cb_underline)).Checked,
Space = ((CheckBox) FindViewById(Resource.Id.cb_space)).Checked,
Specials = ((CheckBox) FindViewById(Resource.Id.cb_specials)).Checked,
SpecialsExtended = ((CheckBox) FindViewById(Resource.Id.cb_specials_extended)).Checked,
Brackets = ((CheckBox) FindViewById(Resource.Id.cb_brackets)).Checked,
ExcludeLookAlike = ((CheckBox) FindViewById(Resource.Id.cb_exclude_lookalike)).Checked,
AtLeastOneFromEachGroup = ((CheckBox) FindViewById(Resource.Id.cb_at_least_one_from_each_group))
.Checked
};
}
return options;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
@@ -181,7 +599,7 @@ namespace keepass2android
}
return false;
}
}
}
}

View File

@@ -236,6 +236,7 @@ namespace keepass2android
{
IOConnectionInfo ioc = new IOConnectionInfo();
Util.SetIoConnectionFromIntent(ioc, data);
Kp2aLog.Log("Set keyfile after returning from RequestCodeSelectKeyfile");
_keyFile = IOConnectionInfo.SerializeToString(ioc);
UpdateKeyfileIocView();
}
@@ -692,7 +693,8 @@ namespace keepass2android
String action = i.Action;
if ((action != null) && (action.Equals(Intents.StartWithOtp)))
{
{
Kp2aLog.Log("Launching with OTP");
if (!GetIocFromOtpIntent(savedInstanceState, i)) return;
_keepPasswordInOnResume = true;
}
@@ -772,7 +774,8 @@ namespace keepass2android
private void GetIocFromLaunchIntent(Intent i)
{
_makeCurrent = i.GetBooleanExtra("MakeCurrent", true);
Kp2aLog.Log("GetIocFromLaunchIntent()");
_makeCurrent = i.GetBooleanExtra("MakeCurrent", true);
Util.SetIoConnectionFromIntent(_ioConnection, i);
var keyFileFromIntent = i.GetStringExtra(KeyKeyfile);
if (keyFileFromIntent != null)
@@ -785,7 +788,9 @@ namespace keepass2android
}
else
{
_keyFile = null;
Kp2aLog.Log("no keyprovider specified");
_keyFile = null;
KeyProviderTypes.Clear();
}
@@ -843,7 +848,8 @@ namespace keepass2android
KeyProviderTypes.Clear();
if (string.IsNullOrEmpty(keyProviderString))
{
_keyFile = null;
Kp2aLog.Log("Reset keyfile");
_keyFile = null;
return;
}
@@ -852,12 +858,15 @@ namespace keepass2android
keyProviderString = keyProviderString.Substring(Kp2aKeyProviderStringPrefix.Length);
foreach (string type in keyProviderString.Split(';'))
{
Kp2aLog.Log("PasswordActivity: key file type " + type);
if (!type.Trim().Any())
continue;
if (type.StartsWith(KeyProviders.KeyFile.ToString()))
{
_keyFile = WebUtility.UrlDecode(type.Substring(KeyProviders.KeyFile.ToString().Length));
KeyProviderTypes.Add(KeyProviders.KeyFile);
Kp2aLog.Log("Added key file of length " + _keyFile.Length);
KeyProviderTypes.Add(KeyProviders.KeyFile);
continue;
}
foreach (KeyProviders providerType in Enum.GetValues(typeof(KeyProviders)))
@@ -872,9 +881,9 @@ namespace keepass2android
}
else
{
//legacy mode
_keyFile = null;
Kp2aLog.Log("PasswordActivity: legacy key file mode");
//legacy mode
_keyFile = null;
if (keyProviderString == KeyProviderIdOtp)
KeyProviderTypes.Add(KeyProviders.Otp);
@@ -1195,13 +1204,16 @@ namespace keepass2android
{
KeyProviderTypes.Clear();
_keyFile = null;
Kp2aLog.Log("PasswordModeSpinner item selected: " + args.Position);
switch (args.Position)
{
case 0:
break;
case 1:
//don't set to "" to prevent losing the filename. (ItemSelected is also called during recreation!)
_keyFile = (FindViewById(Resource.Id.label_keyfilename).Tag ?? "").ToString();
//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.KeyFile);
break;
case 2:
@@ -1242,11 +1254,16 @@ namespace keepass2android
private void RestoreState(Bundle savedInstanceState)
{
if (savedInstanceState != null)
{
{
Kp2aLog.Log("PasswordActivity: Restoring state from savedInstanceState");
_showPassword = savedInstanceState.GetBoolean(ShowpasswordKey, false);
MakePasswordMaskedOrVisible();
SetKeyProviderFromString(savedInstanceState.GetString(KeyFileOrProviderKey));
if (!string.IsNullOrEmpty(savedInstanceState.GetString(KeyFileOrProviderKey)))
Kp2aLog.Log("No key provider found");
else
Kp2aLog.Log("Key provider found");
SetKeyProviderFromString(savedInstanceState.GetString(KeyFileOrProviderKey));
_password = FindViewById<EditText>(Resource.Id.password_edit).Text = savedInstanceState.GetString(PasswordKey);
_pendingOtps = new List<string>(savedInstanceState.GetStringArrayList(PendingOtpsKey));
@@ -1964,8 +1981,18 @@ namespace keepass2android
}
}
private void InitializeFilenameView() {
SetEditText(Resource.Id.filename, App.Kp2a.GetFileStorage(_ioConnection).GetDisplayName(_ioConnection));
private void InitializeFilenameView()
{
string filenameToShow = _ioConnection.Path;
try
{
filenameToShow = App.Kp2a.GetFileStorage(_ioConnection).GetDisplayName(_ioConnection);
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
SetEditText(Resource.Id.filename, filenameToShow);
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="166"
android:versionName="1.08d-r3"
android:versionCode="173"
android:versionName="1.09a-r3"
package="keepass2android.keepass2android"
android:installLocation="auto">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="29" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
</vector>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/totp_secret_key"
android:singleLine="true"
android:inputType="text"
android:hint="@string/totp_secret_key"
android:dropDownWidth="match_parent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<RadioGroup
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/totp_encoding">
<RadioButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/totp_encoding_rfc6238"
android:id="@+id/totp_encoding_rfc6238" />
<RadioButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/totp_encoding_steam"
android:id="@+id/totp_encoding_steam" />
<RadioButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/totp_encoding_custom"
android:id="@+id/totp_encoding_custom" />
</RadioGroup>
<LinearLayout
android:orientation="vertical"
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/totp_custom_settings_group">
<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/totp_algorithms"
android:prompt="@string/algorithm"
android:id="@+id/totp_algorithm" />
<EditText
android:inputType="numberDecimal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/totp_time_step"
android:id="@+id/totp_time_step" />
<EditText
android:inputType="numberDecimal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/totp_length"
android:id="@+id/totp_length" />
</LinearLayout>
</LinearLayout>

View File

@@ -177,6 +177,19 @@
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="@string/add_extra_string"/>
<Button
android:id="@+id/configure_totp"
android:background="?android:selectableItemBackground"
android:textColor="?android:attr/textColorPrimary"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAllCaps="true"
android:paddingLeft="8dp"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/baseline_schedule_white_24"
android:text="@string/configure_totp"/>
</LinearLayout>
</LinearLayout>

View File

@@ -73,6 +73,12 @@
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="@string/CloseDbAfterFailedAttempts" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="@string/WarnFingerprintInvalidated"
/>
</LinearLayout>
<RelativeLayout
android:id="@+id/fingerprint_auth_container"

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:background="?activityBackgroundColor"
android:layout_height="fill_parent">
android:layout_width="fill_parent"
android:background="?activityBackgroundColor"
android:layout_height="fill_parent">
<RelativeLayout
@@ -32,7 +32,7 @@ android:background="?activityBackgroundColor"
<ScrollView
android:id="@+id/ScrollView"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/bottom_bar"
android:layout_marginBottom="12dip"
@@ -40,81 +40,220 @@ android:background="?activityBackgroundColor"
android:layout_marginRight="12dip"
android:layout_marginTop="12dip"
android:layout_alignParentTop="false">
<RelativeLayout
android:id="@+id/RelativeLayout"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/password_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:singleLine="true"
android:typeface="monospace"
android:hint="@string/hint_generated_password" />
<Button
android:id="@+id/generate_password_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/generate_password" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/pb_password_strength"
android:layout_width="50dp"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:indeterminate="false"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_password_strength"
android:paddingLeft="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="4dp"
android:paddingBottom="1dp"
android:paddingTop="8dp"
android:text="@string/password_generation_profile"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<EditText
android:id="@+id/password_edit"
android:layout_width="fill_parent"
android:orientation="horizontal"
>
<Spinner
android:id="@+id/spinner_password_generator_profile"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
/>
<ImageButton
android:id="@+id/btn_password_generator_profile_save"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_menu_save"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" />
<ImageButton
android:id="@+id/btn_password_generator_profile_delete"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_menu_close"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" />
</LinearLayout>
<Spinner
android:id="@+id/spinner_password_generator_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:layout_marginBottom="64dip"
android:entries="@array/PasswordGeneratorModes"
/>
<LinearLayout
android:id="@+id/passphraseOptions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/wordcountlayout">
<EditText
android:id="@+id/wordcount"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:singleLine="true"
android:text="7"
android:hint="@string/hint_wordcount" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/wordseparatorlayout">
<EditText
android:id="@+id/wordseparator"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:singleLine="true"
android:text=" "
android:hint="@string/hint_wordseparator" />
</android.support.design.widget.TextInputLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="4dp"
android:paddingBottom="8dp"
android:text="@string/passphrase_capitalization"/>
<Spinner
android:id="@+id/spinner_password_generator_case_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/PasswordGeneratorCaseModes"
/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:id="@+id/passwordOptions"
android:layout_height="wrap_content"
android:layout_width="match_parent">
<LinearLayout
android:orientation="horizontal"
android:id="@+id/pwd_buttons"
android:layout_height="wrap_content"
android:layout_width="match_parent">
<android.support.design.widget.TextInputLayout
android:layout_height="wrap_content"
android:layout_width="50dp"
android:layout_weight="1"
android:id="@+id/lengthlayout">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:singleLine="true"
android:typeface="monospace"
android:hint="@string/hint_generated_password" />
<Button
android:id="@+id/generate_password_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/password_edit"
android:text="@string/generate_password" />
<TextView
android:id="@+id/length_label"
android:text="@string/length"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:layout_below="@id/generate_password_button" />
<Button
android:id="@+id/btn_length16"
android:text="16"
android:layout_alignParentRight="true"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:layout_below="@id/length_label" />
<Button
android:id="@+id/btn_length12"
android:text="12"
android:layout_toLeftOf="@id/btn_length16"
android:layout_height="wrap_content"
android:layout_width="60sp"
android:layout_alignTop="@id/btn_length16" />
<Button
android:id="@+id/btn_length8"
android:text="8"
android:layout_toLeftOf="@id/btn_length12"
android:layout_height="wrap_content"
android:layout_width="60sp"
android:layout_alignTop="@id/btn_length16" />
<Button
android:id="@+id/btn_length6"
android:text="6"
android:layout_toLeftOf="@id/btn_length8"
android:layout_height="wrap_content"
android:layout_width="60sp"
android:layout_alignTop="@id/btn_length16" />
<EditText
android:id="@+id/length"
android:layout_width="fill_parent"
android:layout_toLeftOf="@id/btn_length6"
android:layout_height="wrap_content"
android:layout_alignTop="@id/btn_length16"
android:singleLine="true"
android:text="12"
android:hint="@string/hint_length" />
<CheckBox
android:id="@+id/cb_uppercase"
android:layout_width="wrap_content"
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/btn_length6"
android:text="6"
android:layout_height="wrap_content"
android:text="@string/uppercase"
android:checked="true"
android:layout_below="@id/length" />
android:layout_width="50sp"
/>
<Button
android:id="@+id/btn_length8"
android:text="8"
android:layout_height="wrap_content"
android:layout_width="50sp"
/>
<Button
android:id="@+id/btn_length12"
android:text="12"
android:layout_height="wrap_content"
android:layout_width="50sp"
/>
<Button
android:id="@+id/btn_length16"
android:text="16"
android:layout_height="wrap_content"
android:layout_width="50sp"
/>
<Button
android:id="@+id/btn_length24"
android:text="24"
android:layout_height="wrap_content"
android:layout_width="50sp"
/>
<Button
android:id="@+id/btn_length32"
android:text="32"
android:layout_width="50sp"
android:layout_height="wrap_content"
/>
</LinearLayout>
<CheckBox
android:id="@+id/cb_uppercase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/uppercase"
android:checked="true" />
<CheckBox
android:id="@+id/cb_lowercase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lowercase"
android:checked="true"
android:layout_below="@id/cb_uppercase" />
android:checked="true" />
<CheckBox
android:id="@+id/cb_digits"
android:layout_width="wrap_content"
@@ -146,12 +285,35 @@ android:background="?activityBackgroundColor"
android:layout_height="wrap_content"
android:text="@string/special"
android:layout_below="@id/cb_space" />
<CheckBox
android:id="@+id/cb_specials_extended"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/special_extended"
android:layout_below="@id/cb_specials" />
<CheckBox
android:id="@+id/cb_brackets"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/brackets"
android:layout_below="@id/cb_specials" />
</RelativeLayout>
android:layout_below="@id/cb_specials_extended" />
<CheckBox
android:id="@+id/cb_exclude_lookalike"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/exclude_lookalike"
android:layout_below="@id/cb_brackets" />
<CheckBox
android:id="@+id/cb_at_least_one_from_each_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/at_least_one_from_each_group"
android:layout_below="@id/cb_exclude_lookalike" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</RelativeLayout>
</RelativeLayout>

View File

@@ -32,6 +32,11 @@
android:title="@string/menu_delete"
app:showAsAction="ifRoom"
/>
<item android:id="@+id/menu_move"
android:icon="@drawable/ic_menu_cut"
android:title="@string/menu_move"
app:showAsAction="ifRoom"
/>
<item android:id="@+id/menu_donate"
android:title="@string/menu_donate"
app:showAsAction="ifRoom"

View File

@@ -5,6 +5,7 @@
<string name="AboutText">Keepass2Android هو مدير كلمات سر يتيح القراءة والكتابة على قواعد بيانات KeePass 2.x على نظام الـAndroid.</string>
<string name="CreditsText">واجهة المستخدم مبنية على تطبيق KeepassDroid الذي طوّره Brian Pellin. الكود المستخدم لعمليات قاعدة البيانات مبني على برنامج KeePass الذي طوّرهDominik Reichl. تم تطوير أو تعديل برنامج Android باستخدام بعض من أعمال Google حسب شروط الاستخدام المذكورة في رخصة Creative Commons 3.0 Attribution License.</string>
<string name="CreditsTextSFTP">دعم SFTP مطور باستخدام مكتبة JSch تحت رخصة BSD، التي تم إنشاؤها بواسطة شركة JCraft.</string>
<string name="CreditsIcons">تم إنشاء أيقونة المطرقة بواسطة جون كاسيرتا من مشروع نون. يتم إنشاء أيقونة البطريق بواسطة أدريانو أميريك من مشروع نون. تم إنشاء أيقونة الريشة بواسطة جون تيستا من مشروع نون. تم إنشاء أيقونة أبل بواسطة آفا روويل من مشروع نون. رمز الصورة من https://icons8.com/icon/5570/Picture.</string>
<string name="accept">موافق</string>
<string name="deny">منع</string>
<string name="add_entry">إضافة قيد</string>
@@ -49,6 +50,7 @@
<string name="fingerprint_prefs">فتح القفل الحيوي</string>
<string name="import_db_prefs">استيراد قاعدة البيانات إلى مجلد داخلي</string>
<string name="import_keyfile_prefs">استيراد ملف المفتاح إلى المجلد الداخلية</string>
<string name="export_keyfile_prefs">تصدير ملف المفتاح من المجلد الداخلي</string>
<string name="keyboardswitch_prefs">تبديل لوحة المفاتيح</string>
<string name="OnlyAvailableForLocalFiles">متاح فقط للملفات المحلية.</string>
<string name="FileIsInInternalDirectory">تم تخزين الملف.</string>
@@ -87,6 +89,17 @@
<string name="fingerprint_os_error">\"فتح القفل بالبصمة\" يتطلب Android 6.0 أو أحدث.</string>
<string name="fingerprint_hardware_error">لا يوجد حساس البصمة.</string>
<string name="fingerprint_no_enrolled">لا يوجد بصمات مسجلة على الجهاز. الرجاء الذهاب لاعدادات الجهاز اولا.</string>
<string name="disable_fingerprint_unlock">تعطيل \"فتح القفل بالبصمة\"</string>
<string name="enable_fingerprint_unlock">تفعيل \"فتح القفل بالبصمة\" الكامل</string>
<string name="enable_fingerprint_quickunlock">تفعيل \"فتح القفل بالبصمة\" لـ \"فتح القفل السريع\"</string>
<string name="fingerprint_unlock_failed">فشل فتح القفل البيومتري. تم إبطال مفتاح فك التشفير بواسطة نظام التشغيل الأندرويد. يحدث هذا عادة إذا تم تغيير المصادقة الحيوية أو إعدادات الأمان. </string>
<string name="fingerprint_disabled_wrong_masterkey">فشل فتح قاعدة البيانات: مفتاح مركب غير صالح. تم تعطيل فتح القفل القياسي الحيوي لأنه يبدو أن كلمة المرور الرئيسية المخزنة لم تعد صالحة. </string>
<string name="fingerprint_reenable">الرجاء إعادة تفعيل \"فتح القفل بالبصمة\" لكلمة المرور الرئيسية الجديدة.</string>
<string name="fingerprint_reenable2">الرجاء إلغاء قفل كلمة المرور الخاصة بك ثم إعادة تمكين فتح القفل الحيوي في إعدادات قاعدة البيانات.</string>
<string name="FingerprintInitFailed">فشل في تهيئة مصادقة القياس الحيوي. </string>
<string name="FingerprintSetupFailed">فشل في تشفير البيانات. يمكن أن يحدث هذا إذا قمت بإضافة أو إزالة بصمات الأصابع في إعدادات النظام بينما يستمع Keepass2Android لبصمة إصبعك.</string>
<string name="enable_fingerprint_unlock_Info">سيؤدي هذا إلى تخزين كلمة المرور الرئيسية على هذا الجهاز، مشفرة مع متجر مفاتيح الأندرويد ومحمية باستخدام المصادقة الحيوية القياسية. يسمح بفتح قاعدة البيانات الخاصة بك باستخدام القياس الحيوي فقط.</string>
<string name="enable_fingerprint_quickunlock_Info">يسمح باستخدام المصادقة الحيوية بدلاً من رمز فتح القفل السريع. لا يخزن أي معلومات تتعلق بكلمة المرور الرئيسية الخاصة بك.</string>
<string name="enter_filename">أدخل اسم ملف قاعدة البيانات</string>
<string name="entry_accessed">تم الوصول</string>
<string name="entry_cancel">إلغاء</string>
@@ -102,11 +115,12 @@
<string name="entry_modified">معدل</string>
<string name="entry_password">كلمة السر</string>
<string name="entry_save">حفظ</string>
<string name="entry_title">الإسم</string>
<string name="entry_title">الاسم</string>
<string name="entry_url">الرابط</string>
<string name="entry_user_name">اسم المستخدم</string>
<string name="entry_extra_strings">حقول النص الإضافية</string>
<string name="entry_binaries">المرفقات</string>
<string name="error_can_not_handle_uri">Keepass2Android لا يمكنه التعامل مع هذا الرابط.</string>
<string name="error_could_not_create_group">خطأ في إنشاء المجموعة.</string>
<string name="error_could_not_create_parent">تعذر إنشاء المجلد الأصل.</string>
<string name="error_database_exists">هذا الملف موجود بالفعل.</string>
@@ -123,6 +137,7 @@
<string name="error_rounds_not_number">الجولات يجب أن تكون عدداً.</string>
<string name="error_param_not_number">يجب أن يكون المعطى عددا.</string>
<string name="error_title_required">العنوان مطلوب.</string>
<string name="error_wrong_length">أدخل عددًا صحيحًا موجبًا في حقل الطول</string>
<string name="FileNotFound">الملف غير موجود.</string>
<string name="file_browser">متصفح الملفات</string>
<string name="generate_password">توليد كلمة السر</string>
@@ -137,6 +152,7 @@
<string name="hint_login_pass">كلمة السر</string>
<string name="hint_title">الإسم</string>
<string name="hint_url">الرابط</string>
<string name="hint_override_url">تجاوز الرابط</string>
<string name="hint_tags">وسم1، وسم2</string>
<string name="hint_username">اسم المستخدم</string>
<string name="InvalidPassword">كلمة المرور أو ملف المفتاح غير صالح.</string>
@@ -157,6 +173,7 @@
<string name="menu_change_key">تغيير المفتاح الرئيسي</string>
<string name="menu_copy_pass">نسخ كلمة السر</string>
<string name="menu_copy_user">نسخ المستخدم</string>
<string name="menu_copy_totp">نسخ TOTP</string>
<string name="menu_create">إنشاء</string>
<string name="menu_app_settings">الإعدادات</string>
<string name="menu_db_settings">إعدادات قاعدة البيانات</string>
@@ -183,6 +200,7 @@
<string name="no">لا</string>
<string name="no_keys">لا توجد قيود في قاعدة البيانات أو المجموعة.</string>
<string name="no_results">لا توجد نتائج للبحث</string>
<string name="no_url_handler">لا يوجد معالج لهذا الرابط.</string>
<string name="open_recent">فتح قاعدة البيانات الأخيرة (انقر للفتح)</string>
<string name="omitbackup_title">عدم البحث عن القيود في النسخ الاحتياطي وسلة المحذوفات</string>
<string name="omitbackup_summary">تجاهل \"النسخ الاحتياطية\" و\"مجموعة سلة المحذوفات\" من نتائج البحث</string>
@@ -211,6 +229,7 @@
<string name="saving_database">جاري حفظ قاعدة البيانات…</string>
<string name="exporting_database">جاري تصدير قاعدة البيانات…</string>
<string name="export_database_successful">تم تصدير قاعدة البيانات بنجاح!</string>
<string name="export_keyfile_successful">تم تصدير ملف المفتاح بنجاح!</string>
<string name="space">فراغ</string>
<string name="search_label">بحث</string>
<string name="show_password">عرض كلمة السر</string>
@@ -220,6 +239,11 @@
<string name="sort_moddate">فرز حسب تاريخ التعديل</string>
<string name="sort_default">الحفاظ على الترتيب الاصلي</string>
<string name="special">خاص</string>
<string-array name="PasswordGeneratorModes">
<item>كلمة السر</item>
<item>Passphrase</item>
<item>Passphrase + Password</item>
</string-array>
<string name="search_hint">البحث عن ماذا</string>
<string name="search_results">نتائج البحث</string>
<string name="search_in">البحث في</string>
@@ -227,7 +251,9 @@
<string name="select_group_then_add">افتح المجموعة المطلوبة، ثم اضغط على \"%1$s\"!</string>
<string name="insert_element_here">أدخل هنا</string>
<string name="twofish">تشفير Twofish</string>
<string name="underline">تأكيد</string>
<string name="unsupported_db_version">إصدار قاعدة بيانات غير مدعوم.</string>
<string name="uppercase">أحرف كبيرة</string>
<string name="warning_read_only">بطاقة الذاكرة SD الخاصة بك هي للقراءة فقط حاليًا. قد لا تكون قادراً على حفظ التغييرات إلى قاعدة البيانات الخاصة بك.</string>
<string name="warning_unmounted">بطاقة الذاكرة SD الخاصة بك غير مهيئة حالياً على جهازك. لن تكون قادراً على إنشاء أو تحميل قاعدة البيانات الخاصة بك.</string>
<string name="version_label">الإصدار</string>
@@ -235,17 +261,22 @@
<string name="author">Keepass2Android تم تطويره من قبل Philipp Crocoll.</string>
<string name="further_authors">شكرًا لـ %1$s على مشاركته في البرمجة.</string>
<string name="designers">شكرًا لـ %1$s على مساهماتهم في تصميم الأيقونة وتخطيط الواجهة.</string>
<string name="supporters">بفضل الدعم المالي من قبل %1$s.</string>
<string name="credit_plugin1">تم تطوير إضافة \"Twofish Cipher\" لـ KeePass بواسطة \"Scott Greenberg\" وتم تضمينها في KP2A.</string>
<string name="credit_android_filechooser">تم تطوير \"Android filechooser\" بواسطة \"Hai Bison\"</string>
<string name="credit_keyboard">لوحة مفاتيح KP2A مبنية على لوحة المفاتيح من نظام Gingerbread بواسطة AOSP وتستخدم كود Plugin Manager من Hacker\'s Keyboard بواسطة Klaus Weidner.</string>
<string name="please_note">يرجى إضافة ملاحظة</string>
<string name="contributors">المساهمون</string>
<string name="regular_expression">التعبير الاعتيادي</string>
<string name="TanExpiresOnUse_title">تنتهي صلاحية TAN عند الاستخدام</string>
<string name="TanExpiresOnUse_summary">قم بتعيين \"أرقام العمليات\" كمنتهية الصلاحية عند استخدامها</string>
<string name="ShowUsernameInList_title">عرض اسم المستخدم في القائمة</string>
<string name="ShowUsernameInList_summary">عرض أسماء المستخدمين أسفل عناوين القيود.. مفيدة للحسابات أو \"أرقام العمليات\" المتعددة.</string>
<string name="RememberRecentFiles_title">تذكر قواعد البيانات</string>
<string name="RememberRecentFiles_summary">تذكر قواعد البيانات المفتوحة حديثا وأظهرها في شاشة \"فتح قاعدة البيانات\".</string>
<string name="NoDalVerification_title">لا يوجد تحقق DAL</string>
<string name="NoDalVerification_summary">تعطيل التحقق من تطابق النطاق وحزمة التطبيقات</string>
<string name="kp2a_findUrl">العثور على كلمة المرور</string>
<string name="excludeExpiredEntries">استبعاد المدخالات المنتهية الصلاحية</string>
<string name="search_options">خيارات البحث</string>
<string name="caseSensitive">حساسية حالة الأحرف</string>
@@ -257,6 +288,7 @@
<string name="enter_filename_details_create_import">الملف المراد استيراده سوف يتم تحديده في الخطوة التالية</string>
<string name="enable_quickunlock">تفعيل \"فتح القفل السريع\"</string>
<string name="QuickUnlock_label">أدخل الأحرف الـ%1$d الأخيرة من كلمة المرور الخاصة بك:</string>
<string name="QuickUnlock_label_secure">أدخل رمز فتح القفل السريع:</string>
<string name="QuickUnlock_button">فتح القفل السريع!</string>
<string name="QuickUnlock_lockButton">إغلاق قاعدة البيانات</string>
<string name="QuickUnlockDefaultEnabled_title">تفعيل \"فتح القفل السريع\" افتراضياً</string>
@@ -269,13 +301,14 @@
<string name="QuickUnlockIconHidden16_summary">ميزة \"فتح القفل السريع\" لا تعمل جيدُا إلا بوجود إشعار. حدد هذا الخيار لاستخدام إشعار بلا أيقونة.</string>
<string name="QuickUnlockLength_title">طول مفتاح \"فتح القفل السريع\"</string>
<string name="QuickUnlockLength_summary">الحد الأقصى لعدد الأحرف المستخدمة ككلمة السر لـ\"فتح القفل السريع\".</string>
<string name="QuickUnlockHideLength_title">إخفاء طول الفتح السريع</string>
<string name="QuickUnlockHideLength_summary">في حالة التمكين، لا يتم عرض طول رمز فتح القفل السريع على شاشة فتح القفل السريع.</string>
<string name="QuickUnlock_fail">فشل \"فتح القفل السريع\": كلمة السر خاطئة!</string>
<string name="BinaryDirectory_title">مجلد المرفقات</string>
<string name="BinaryDirectory_summary">المجلد حيث يتم حفظ المرفقات.</string>
<string name="SaveAttachmentDialog_title">حفظ المرفقات</string>
<string name="SaveAttachmentDialog_text">الرجاء تحديد مكان حفظ المرفقات.</string>
<string name="SaveAttachmentDialog_save">تصدير إلى ملف...</string>
<string name="SaveAttachmentDialog_open">حفظ في ذاكرة التخزين المؤقت، ثم فتح المرفق</string>
<string name="ShowAttachedImage">إظهار مع مشهد الصور الداخلي</string>
<string name="SaveAttachment_doneMessage">حفظ الملف في %1$s.</string>
<string name="SaveAttachment_Failed">لا يمكن حفظ المرفق في %1$s.</string>
<string name="AddUrlToEntryDialog_title">تذكر نص البحث؟</string>
@@ -302,15 +335,24 @@
<string name="LockWhenNavigateBack_summary">تأمين قاعدة البيانات عند ترك التطبيق عن طريق الضغط على زر العودة.</string>
<string name="UseKp2aKeyboardInKp2a_title">استخدم لوحة المفاتيح مضمنة داخل Keepass2Android</string>
<string name="UseKp2aKeyboardInKp2a_summary">إذا كنت لا تثق بموفر لوحة المفاتيح القياسية، اختر هذا الخيار لاستخدام لوحة المفاتيح المدمجة عند إدخال كلمة السر الرئيسية أو تحرير القيود.</string>
<string name="ActivateSearchViewOnStart_title">تفعيل حقل البحث عند البدء</string>
<string name="ActivateSearchViewOnStart_summary">تنشيط حقل البحث في عرض المجموعة بعد إلغاء القفل أو عند البحث عن إدخال</string>
<string name="NoDonateOption_title">إخفاء خيار التبرع</string>
<string name="NoDonateOption_summary">هذا الإعداد للمتبرعين. يظهر فقط بعد استخدام Keepass2Android لبعض الوقت.</string>
<string name="NoDonateOption_question">بدون تبرعات، لن يكون هذا التطبيق موجودا وسوف يتم تحسينه باستمرار! إذا لم تتبرع بعد، يرجى النظر في القيام بذلك الآن</string>
<string name="NoDonationReminder_title">لا تطلب التبرع مطلقاً</string>
<string name="NoDonationReminder_summary">لن أعطيك قرشاً أو أني تبرعت مسبقاُ. لا تطلب التبرع، ولا حتى في عيد ميلاد المطور.</string>
<string name="UseOfflineCache_title">التخزين المؤقت لقاعدة بيانات</string>
<string name="UseOfflineCache_summary">الاحتفاظ بنسخة من ملفات قاعدة البيانات في دليل التخزين المؤقت للتطبيق. هذا يسمح باستخدام قواعد البيانات حتى حين ملف قاعدة البيانات غير قابل للوصول.</string>
<string name="CreateBackups_title">النسخ الاحتياطي المحلي</string>
<string name="CreateBackups_summary">إنشاء نسخة احتياطية محلية بعد تحميل قاعدة البيانات بنجاح.</string>
<string name="UpdatingBackup">تحديث النسخة الاحتياطية المحلية...</string>
<string name="LocalBackupOf">النسخ الاحتياطي المحلي لـ %1$s</string>
<string name="show_local_backups">إظهار النسخ الاحتياطية المحلية</string>
<string name="AcceptAllServerCertificates_title">شهادات SSL</string>
<string name="AcceptAllServerCertificates_summary">حدد السلوك عند فشل التحقق من صحة الشهادة. ملاحظة: يمكنك تثبيت شهادات على الجهاز الخاص بك إذا فشل التحقق من صحة الشهادة!</string>
<string name="ClearOfflineCache_title">مسح ذاكرة التخزين المؤقت؟</string>
<string name="ClearOfflineCache_question">سيؤدي هذا إلى حذف جميع ملفات قاعدة البيانات المخزنة مؤقتاً. سيتم فقدان أي تغييرات قمت بها دون الوصول إلى قاعدة بيانات المصدر والتي لم يتم مزامنتها بعد! هل تريد المتابعة؟</string>
<string name="CheckForFileChangesOnSave_title">التحقق من التعديلات</string>
<string name="CheckForFileChangesOnSave_summary">التحقق مما إذا تم تعديل الملف خارجيًا قبل حفظ التغييرات.</string>
<string name="CheckForDuplicateUuids_title">التحقق من وجود معرفات (UUIDs) مكررة</string>
@@ -319,15 +361,26 @@
<string name="ShowCopyToClipboardNotification_summary">إتاحة الوصول إلى اسم المستخدم وكلمة المرور من خلال شريط الإشعارات وحافظة لوحة المفاتيح. احذر من برامج التقاط كلمات المرور!</string>
<string name="ShowSeparateNotifications_title">إشعارات منفصلة</string>
<string name="ShowSeparateNotifications_summary">عرض إشعارات منفصلة عند \"نسخ اسم المستخدم وكلمة المرور إلى حافظة لوحة المفاتيح\" و \"تفعيل لوحة المفاتيح\".</string>
<string name="AccServiceAutoFill_prefs">خدمة التعبئة التلقائية</string>
<string name="AutoFill_prefs">استخدام خدمة التعبئة التلقائية</string>
<string name="ShowKp2aKeyboardNotification_title">إشعار لوحة المفاتيح KP2A</string>
<string name="ShowKp2aKeyboardNotification_summary">جعل الإدخال الكامل متاحاً من خلال لوحة مفاتيح KP2A (موصى به).</string>
<string name="OpenKp2aKeyboardAutomatically_title">تبديل لوحة المفاتيح</string>
<string name="OpenKp2aKeyboardAutomatically_summary">قم بفتح مربع حوار لتحديد لوحة المفاتيح عندما يتوفر قيد من خلال لوحة مفاتيح KP2A بعد البحث من المتصفح.</string>
<string name="kp2a_switch_rooted">تبديل تلقائي للوحة المفاتيح</string>
<string name="kp2a_switch_rooted_summary">التبديل تلقائياً إلى لوحة مفاتيح KP2A عند فتح الإدخال. يتطلب لوحة المفاتيح المعدة بشكل صحيح أو جهاز متجذر وتطبيق إعدادات آمنة مع النظام+. </string>
<string name="get_keyboardswap">تثبيت لوحة المفاتيح إضافة</string>
<string name="get_keyboardswap_summary">هذا البرنامج المساعد يسمح بالتبديل إلى لوحة مفاتيح KP2A بدون جذر. يتطلب ADB. </string>
<string name="OpenKp2aKeyboardAutomaticallyOnlyAfterSearch_title">التبديل تلقائيًا فقط بعد البحث</string>
<string name="OpenKp2aKeyboardAutomaticallyOnlyAfterSearch_summary">التبديل تلقائياً إلى لوحة مفاتيح KP2A فقط بعد استخدام \"مشاركة الرابط\" (ولكن ليس عند فتح قيد ما بطريقة أخرى)</string>
<string name="AutoSwitchBackKeyboard_title">استرجاع لوحة المفاتيح الأصلية</string>
<string name="AutoSwitchBackKeyboard_summary">قم باسترجاع لوحة المفاتيح الأصلية عند فتح أي قبد.</string>
<string name="ShowUnlockedNotification_title">ايقونة الاشعار بينما القفل مفتوح</string>
<string name="ShowUnlockedNotification_summary">إظهار اشعار بينما قاعدة البيانات مفتوحة.</string>
<string name="IconVisibilityInfo_Android8_text">أندرويد 8 قدم سلوكا جديدا للإشعارات. إذا كنت ترغب في إخفاء الأيقونة لإشعارات Keepass2Android، يرجى تكوين هذا من خلال إعدادات النظام. تعيين أهمية فئة الإشعار إلى الحد الأدنى.</string>
<string name="IconVisibilityInfo_Android8_btnSettings">فتح الإعدادات</string>
<string name="DontCare">لا أوافق</string>
<string name="DocumentAccessRevoked">لم يعد من الممكن الوصول إلى الملف Keepass2Android. إما أنه تمت إزالته أو تم إلغاء أذونات الوصول. الرجاء استخدام إعادة فتح الملف، على سبيل المثال باستخدام تغيير قاعدة البيانات.</string>
<string name="PreloadDatabaseEnabled_title">تحميل ملف قاعدة البيانات مسبقًا</string>
<string name="PreloadDatabaseEnabled_summary">بدء التحميل في الخلفية أو تحميل ملف قاعدة البيانات أثناء إدخال كلمة المرور.</string>
<string name="AskOverwriteBinary">هل تريد الكتابة فوق الملف الحالي بنفس الاسم؟</string>
@@ -366,11 +419,26 @@
<string name="SynchronizingDatabase">جاري دمج التغييرات…</string>
<string name="YesSynchronize">نعم، ادمج التغييرات</string>
<string name="NoOverwrite">لا، اكتب فوق التغييرات</string>
<string name="UseOfflineMode">العمل على ذاكرة التخزين المؤقت الداخلية فقط</string>
<string name="UseOnlineMode">مزامنة نسخ ذاكرة التخزين المؤقت مع المصدر</string>
<string name="UseOfflineMode_Info">يتم تحميل قاعدة البيانات من ذاكرة التخزين المؤقت الداخلية. يتم تخزين التغييرات في ذاكرة التخزين المؤقت الداخلية فقط وسيتم مزامنتها فقط عند تحديد مزامنة نسخ ذاكرة التخزين المؤقت مع المصدر.</string>
<string name="InOfflineMode">العمل على ذاكرة التخزين المؤقت الداخلية فقط.</string>
<string name="SynchronizingCachedDatabase">جاري مزامنة قاعدة البيانات المخزنة مؤقتاً…</string>
<string name="DownloadingRemoteFile">تحميل ملف المصدر…</string>
<string name="UploadingFile">حفظ الملف…</string>
<string name="RestoringRemoteFile">استعادة ملف المصدر…</string>
<string name="FilesInSync">الملفات في تزامن.</string>
<string name="SynchronizedDatabaseSuccessfully">تمت مزامنة قاعدة البيانات بنجاح!</string>
<string name="CheckingDatabaseForChanges">جاري التحقق من التغييرات في قاعدة البيانات…</string>
<string name="CouldNotSaveToRemote">تعذر الحفظ في الملف المصدر: %1$s. احفظ مرة أخرى أو استخدم قائمة المزامنة عند الوصول إلى الملف مرة أخرى.</string>
<string name="CouldNotLoadFromRemote">تعذر الوصول إلى ملف المصدر: %1$s. الملف الذي تم تحميله من ذاكرة التخزين المؤقت الداخلية. لا يزال بإمكانك إجراء تغييرات في قاعدة البيانات ومزامنتها لاحقاً.</string>
<string name="UpdatedRemoteFileOnLoad">تم تحديث ملف المصدر.</string>
<string name="NotifyOpenFromLocalDueToConflict">فتح ملف مؤقت داخليا بسبب تعارض مع التغييرات في الملف المصدر. استخدم قائمة المزامنة للدمج.</string>
<string name="LoadedFromRemoteInSync">تم مزامنة الملف المصدر وذاكرة التخزين المؤقت.</string>
<string name="UpdatedCachedFileOnLoad">تم تحديث نسخة التخزين المؤقت الداخلية من %1$s.</string>
<string name="RemoteDatabaseUnchanged">لم يتم اكتشاف أية تغييرات.</string>
<string name="ResolvedCacheConflictByUsingRemoteOtpAux">ملف مساعد OTP تم تحديثه: عداد المصدر كان أعلى.</string>
<string name="ResolvedCacheConflictByUsingLocalOtpAux">تم تحديث ملف OTP المساعد: كانت العداد المحلي أعلى.</string>
<string name="SynchronizingOtpAuxFile">جاري مزامنة ملف \"كلمة السر أحادية الاستخدام\"…</string>
<string name="database_file">ملف قاعدة البيانات</string>
<string name="otp_aux_file">ملف \"كلمة السر أحادية الاستخدام\"</string>
@@ -411,8 +479,12 @@
<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">هذا النوع من التخزين سيطلب فقط الوصول إلى مجلد \"Applications/Keepass2Android\". إذا كنت ترغب في استخدام قاعدة بيانات موجودة من حساب PCloud الخاص بك، يرجى التأكد من وضع الملف في هذا المجلد.</string>
<string name="filestoragename_onedrive">OneDrive</string>
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_full">جميع الملفات والملفات المشتركة</string>
<string name="filestoragename_onedrive2_myfiles">ملفاتي</string>
<string name="filestoragename_onedrive2_appfolder">مجلد تطبيق Keepass2Android</string>
<string name="filestoragename_sftp">SFTP (نقل ملفات SSH)</string>
<string name="filestoragename_content">منتقي ملف النظام</string>
<string name="filestorage_setup_title">تهيئة الوصول إلى الملفات</string>
@@ -432,6 +504,7 @@
<string name="use_key_file">استخدم ملف المفتاح</string>
<string name="error_adding_keyfile">حدث خطأ أثناء إضافة ملف المفتاح!</string>
<string name="init_otp">تحميل الملف الخارجي الخاص بكلمة المرور لمرة واحدة OTP…</string>
<string name="otp_explanation">أدخل كلمات المرور التالية لمرة واحدة (OTPs). اسحب الجسم القريب من يوبيك على ظهر جهازك للدخول عبر NFC (يتطلب تطبيق يوبيكليب).</string>
<string name="otp_hint">كلمة مرور مؤقتة %1$d</string>
<string name="CouldntLoadOtpAuxFile">تعذر تحميل الملف الخارجي الخاص ب \"كلمة المرور لمرة واحدة\" OTP!</string>
<string name="CouldntLoadOtpAuxFile_Hint">الرجاء استخدام ألأضافة \"أوتبكييبروف\" OtpKeyProv في KeePass 2.x (نسخة PC) لتمكين قاعدة بياناتك مناستخدام \"كلمة المرور لمرة واحدة\" OTP !</string>
@@ -444,6 +517,9 @@
<string name="OtpKeyError">فشل إنشاء مفتاح كلمة المرور المؤقتة! تأكد من إدخالك كلمات المرور الصحيحة.</string>
<string name="ErrorUpdatingOtpAuxFile">خطأ أثناء تحديث الملف الخارجي الخاص بكلمة المرور أحادية الاستخدام!</string>
<string name="SavingOtpAuxFile">جاري حفظ الملف الخارجي الخاص بكلمة المرور أحادية الاستخدام…</string>
<string name="NoChallengeApp">تعذر العثور على تطبيق يمكنه التعامل مع التحدي.</string>
<string name="PleaseInstallApp">الرجاء تثبيت %1$s من Google Play.</string>
<string name="AppOutdated">%1$s لم يعد مدعوماً.</string>
<string name="bad_resp">إجابة السؤال غير صحيحة.</string>
<string name="CouldntLoadChalAuxFile">تعذر تحميل الملف الخارجي الخاص بالسؤال!</string>
<string name="CouldntLoadChalAuxFile_Hint">من فضلك استعمل ملحق KeeChallenge فالنّسخة 2.0 من KeePass للحاسوب، وذلك لإعداد قاعدة البيانات من أجل استعمال نظام تحدّي/استجابة (Challenge-response)!</string>
@@ -453,8 +529,10 @@
<string name="TrayTotp_SettingsField_title">اسم الحقل \"إعدادات TOTP\"</string>
<string name="TrayTotp_SettingsField_summary">أدخل اسمًا لحقل \"إعدادات TrayTotp\" هنا.</string>
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">سجل الملفات لتصحيح الأخطاء</string>
<string name="DebugLog_title">استخدم ملف المفتاح</string>
<string name="DebugLog_summary">كتابة إخراج التطبيق إلى ملف سجل محلي</string>
<string name="DebugLog_send">إرسال سجل التصحيح...</string>
<string name="loading">جاري التحميل…</string>
<string name="plugins">الإضافات</string>
<string name="plugin_packagename">اسم الحزمة:</string>
@@ -464,6 +542,7 @@
<string name="plugin_disabled">غير مفعل</string>
<string name="plugin_web">البحث عن الإضافات على الإنترنت</string>
<string name="plugin_scopes">النطاقات</string>
<string name="not_enabled">غير مفعل</string>
<string name="query_credentials_for_url">%1$s يتطلب بيانات الدخول لـ %2$s.</string>
<string name="query_credentials">%1$s يتطلب بيانات الدخول. الرجاء تحديد أحد القيود.</string>
<string name="plugin_enabled_checkbox">مفعل</string>
@@ -472,7 +551,9 @@
<string name="SCOPE_CURRENT_ENTRY_title">بيانات القيد الحالي</string>
<string name="SCOPE_CURRENT_ENTRY_explanation">ستتلقى الإضافة جميع البيانات حول قيد قاعدة البيانات الحالي وسيتم توفير عمليات على القيد وتعديل طريقة عرضه.</string>
<string name="SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE_title">استعلام عن بيانات الدخول الخاصة</string>
<string name="SCOPE_QUERY_CREDENTIALS_FOR_OWN_PACKAGE_explanation">سيتم السماح للملحق بالاستعلام عن بيانات الاعتماد المرتبطة بحزمة التطبيقات الخاصة به.</string>
<string name="SCOPE_QUERY_CREDENTIALS_title">الاستعلام عن بيانات الدخول</string>
<string name="SCOPE_QUERY_CREDENTIALS_explanation">سيتم السماح للإضافة بالاستعلام عن بيانات الاعتماد لمواقع الويب أو التطبيقات المتعمدة.</string>
<string name="get_regular_version">الحصول على المزيد من أنواع التخزين</string>
<string name="CertificateWarning">تحذير: فشل التحقق من صحة شهادة الخادم: %1$s. قم بتثبيت شهادة الجذر المناسبة على الجهاز الخاص بك أو راجع الإعدادات!</string>
<string name="CertificateFailure">تحذير: فشل التحقق من صحة شهادة الخادم! قم بتثبيت شهادة الجذر المناسبة على الجهاز الخاص بك أو راجع الإعدادات!</string>
@@ -483,6 +564,7 @@
<string name="FileIsReadOnlyOnKitkat">الملف المحدد للقراءة فقط لـ Keepass2Android بسبب القيود المفروضة على نظام Android 4.4+.</string>
<string name="CopyFileRequired">لاستخدامه، يجب أن تقوم بنسخه إلى موقع آخر.</string>
<string name="CopyFileRequiredForEditing">لتعديله، يجب أن تقوم بنسخه إلى موقع آخر.</string>
<string name="ClickOkToSelectLocation">انقر موافق لتحديد موقع يجب أن يتم فيه نسخ الملف.</string>
<string name="FileReadOnlyTitle">قاعدة البيانات للقراءة فقط</string>
<string name="FileReadOnlyMessagePre">Keepass2Android فتح قاعدة البيانات الحالية في وضع للقراءة فقط.</string>
<string name="ReadOnlyReason_PreKitKat">يبدو أنك قمت بفتح الملف من تطبيق خارجي. هذه الطريقة لا تدعم الكتابة. إذا كنت ترغب في إجراء تغييرات على قاعدة البيانات، الرجاء إغلاق قاعدة البيانات وحدد \"تعديل قاعدة البيانات\". ثم افتح الملف من أحد الخيارات المتاحة إذا كان ذلك ممكناً.</string>
@@ -495,7 +577,7 @@
<string name="DefaultTemplate">قيد إعتيادي</string>
<string name="TemplateGroupName">قوالب</string>
<string name="TemplateTitle_IdCard">بطاقة التعريف/الهوية</string>
<string name="TemplateField_IdCard_Name">الإسم</string>
<string name="TemplateField_IdCard_Name">الاسم</string>
<string name="TemplateField_IdCard_PlaceOfIssue">مكان الإصدار</string>
<string name="TemplateField_IdCard_IssueDate">تاريخ الصّدور</string>
<string name="TemplateTitle_EMail">البريد الإلكتروني</string>
@@ -513,6 +595,7 @@
<string name="AskAddTemplatesTitle">إضافة قوالب؟</string>
<string name="AskAddTemplatesMessage">Keepass2Android يحتوي على قوالب لسجلات الدخول للبريد الإلكتروني، كلمات السّر للشبكات المحلية اللاسلكية، تدوينات آمنة وأكثر. هل ترغب في إضافة هذه إلى قاعدة البيانات الخاصة بك؟ إن اخترت لا فيُمكنك إضافتها لاحقاً في إعدادات قاعدة البيانات.</string>
<string name="AddTemplates_pref">إضافة قوالب لقاعدة البيانات</string>
<string name="PreviewWarning">من فضلك لاحظ! هذا إصدار معاينة وقد يأتي مع بعض العيوب! إذا واجهت *أي شيء* غير متوقع، يرجى إعلامي (على مجموعة مختبري بيتا غوغل + أو عن طريق البريد الإلكتروني).</string>
<string name="Continue">استمر</string>
<string name="NoFilenameWarning">الـ URI المدرجة لا تبدو اسم ملف. هل أنت متأكد من أن هذا ملف صحيح؟</string>
<string name="FirstInvalidCompositeKeyError">تركيبة مفتاح المرور غير صالحة! يرجى المحاولة مجدداً.</string>
@@ -521,9 +604,147 @@
•تأكد من تحديدك لنوع كلمة المرور الصحيح. هذا عليه ان يطابق النوع الذي أستخدم عند تأليف القاعدة لأول مرة.\n
•تأكد انك اخترت ملف قاعدة البيانات الصحيح.
</string>
<string name="HintLocalBackupInvalidCompositeKey"> \n
&#8226; تلميح: إذا كنت تعتقد أن ملف قاعدة البيانات الخاص بك قد يكون فاسدا أو أنك لا تتذكر المفتاح الرئيسي بعد تعديله، يمكنك محاولة آخر إصدار ملف فتح بنجاح عن طريق النقر فوق \"%1$s\" واختيار النسخة الاحتياطية المحلية.</string>
<string name="HintLocalBackupOtherError"> \n
&#8226; تلميح: قام Keepass2Android بتخزين آخر إصدار فتح للملف بنجاح على وحدة التخزين الداخلية. يمكنك فتحه بالنقر على \"%1$s\" واختيار النسخة الاحتياطية المحلية.
</string>
<string name="CorruptDatabaseHelp">الملف معطوب.\n
هنا هي بعض التلميحات التي قد تساعد على تشخيص المسألة: \n إن قمت بنسخ الملف عبر USB (وضع الخطة المتوسطة الأجل)، الرجاء المحاولة مرة أخرى باستخدام أدوات مثل MyPhoneExplorer. الخطة المتوسطة الأجل تقتطع الملفات في بعض الحالات. \n • إذا تعذر عليك فتح الملف من نفس الموقع على جهاز الكمبيوتر الخاص بك، فمن المحتمل جداً أن الملف معطوب فعلا. الرجاء استخدام نسخة احتياطية من قاعدة البيانات. إذا كنت تفترض أن Keepass2Android قد أتلف الملف، الرجاء الاتصال بالمساعدة. \n • إذا تمكنت من فتح الملف على جهاز الكمبيوتر الخاص بك، الرجاء الاتصال بالمساعدة. يمكنك محاولة حفظه باستخدام إعدادات مختلفة (مثلا، غير مضغوط) على جهاز الكمبيوتر وإعادة محاولة فتحه في Keepass2Android. </string>
<string name="open_other_db">فتح قاعدة بيانات أخرى…</string>
<string name="select_database">حدد قاعدة البيانات</string>
<string name="configure_child_dbs">تكوين قواعد بيانات الأطفال…</string>
<string name="child_dbs_title">قواعد بيانات الأطفال</string>
<string name="unspecified">غير محدد</string>
<string name="child_db_explanation">قواعد بيانات الأطفال هي قواعد بيانات أخرى يمكن فتحها تلقائياً عند فتح قاعدة البيانات الأصلية. ولذلك فإن كلمة السر الرئيسية للطفل وموقع ملفه يخزن لدى الوالدين. هذه الميزة تسمح بمشاركة بعض كلمات المرور الخاصة بك مع شخص آخر. التنفيذ متوافق مع KeeAutoExec للحاسب الشخصي.</string>
<string name="child_db_enabled_on_this_device">تمكين على هذا الجهاز</string>
<string name="child_db_enable_on_this_device">تمكين على هذا الجهاز</string>
<string name="child_db_disable_on_this_device">تعطيل على هذا الجهاز</string>
<string name="child_db_enable_a_copy_for_this_device">نسخ لهذا الجهاز</string>
<string name="unconfigured_child_dbs">قاعدة البيانات الخاصة بك تحتوي على قواعد بيانات جديدة للأطفال في مجموعة \"تلقائي\". يرجى تحديد ما إذا كان يجب استخدام قواعد البيانات الخاصة بالأطفال هذه على هذا الجهاز.</string>
<string name="add_child_db">إضافة قاعدة بيانات فرعية...</string>
<string name="EnableCopyForThisDevice_Info">سيؤدي هذا إلى إنشاء وتمكين نسخة من إعدادات قاعدة البيانات الفرعية. يمكن بعد ذلك تعديل هذه الإعدادات المنسوخة خصيصا لهذا الجهاز.</string>
<string name="Visible_title">مرئي</string>
<string name="child_db_Enabled_title">فتح تلقائياً</string>
<string name="database_file_heading">ملف قاعدة البيانات</string>
<string name="if_device_text">تمكين ل %1$s</string>
<string name="DbUnlockedChannel_name">قاعدة البيانات مفتوحة</string>
<string name="DbUnlockedChannel_desc">إشعار حول فتح قاعدة البيانات</string>
<string name="DbQuicklockedChannel_name">QuickUnlock</string>
<string name="DbQuicklockedChannel_desc">إشعار حول قاعدة البيانات مقفلة مع QuickUnlock</string>
<string name="EntryChannel_name">إشعارات الإدخال</string>
<string name="EntryChannel_desc">إشعار لتبسيط الوصول إلى الإدخال المحدد حاليا.</string>
<string name="CloseDbAfterFailedAttempts">أغلق قاعدة البيانات بعد ثلاث محاولات فاشلة لفتح القفل بواسطة القياسات الحيوية.</string>
<string-array name="ChangeLog_1_08d">
<item>إضافة دعم لتنسيق ملف المفتاح الجديد الذي تم إدخاله في Keepass 2.47</item>
<item>إضافة دعم لـ Argon2id كوظيفة اشتقاق رئيسية</item>
<item>تحسين توافق التعبئة التلقائية مع Firefox و Chrome</item>
<item>تحسين الدعم لإدخالات TOTP من برامج سطح المكتب</item>
<item>تحديث pCloud SDK لإصلاح مشكلة المصادقة</item>
<item>تحديث Jsch إلى الإصدار 0.1.55</item>
<item>إضافة قائمة إلى شاشة اختيار قاعدة البيانات</item>
<item>السماح بتصدير ملفات المفاتيح المستوردة</item>
</string-array>
<string-array name="ChangeLog_1_08c">
<item>لم يعد يتم تخزين أسماء حزم تطبيقات الأندرويد في حقل URL</item>
<item>تحسين سلوك القفل - لم يعد عرض الدعوة الحيوية مباشرة بعد فتح القفل</item>
<item>تحديث OkHttp لدعم HTTP/2</item>
<item>إصلاح الترجمات الناقصة</item>
</string-array>
<string-array name="ChangeLog_1_08b">
<item>قوة HTTP/1.1 بسبب مشكلة في تنفيذ OkHttp HTTP/2</item>
<item>تحسين مربع حوار لوحة المفاتيح على أندرويد 9+</item>
<item>تغيير ارتباطات الملفات للتطبيق لتجنب بعض الارتباطات غير الضرورية</item>
<item>تأكد من أن نص كلمة المرور غير مخفي خلف أيقونة العين</item>
<item>تغيير سلوك التعبئة التلقائية للتحذير عند ملء بيانات الاعتماد لنطاق إلى تطبيق غير معترف به</item>
<item>تحديث إلى مكتبة FTP</item>
<item>إصلاحات إلى الأعطال المحتملة للتطبيق</item>
<item>إصلاحات ثانوية أخرى</item>
</string-array>
<string-array name="ChangeLog_1_08">
<item>إضافة زر إشعار لنسخ TOTP إلى الحافظة</item>
<item>التبديل إلى استخدام FluentFTP لتوفير الدعم لـ TLS 1.2</item>
<item>قم بالتبديل إلى API BometricPrompt لتحسين تجربة المستخدم مع فتح بصمة الإصبع والسماح باستخدام إلغاء قفل الوجه، على سبيل المثال على Pixel 4.</item>
<item>إصلاح الأخطاء</item>
</string-array>
<string name="ChangeLog_1_07b"> الإصدار 1. 7b\n
* تحسين أداء أرغون2 باستخدام التنفيذ المحلي (شكراً لشيه - هسوان!\n
* السماح بتعطيل بصمة الإصبع بالنقر على أيقونة بصمة الإصبع (تجنب مشكلة مع قراء بصمة الإصبع أسفل الشاشة، شكرا للماركودالاس!\n
* استعادة موضع المؤشر عند تبديل رؤية كلمة المرور (بفضل DDoSolitary!\n
* إدخال تحسينات على تطبيق pCloud (بفضل gilbsgilps مرة أخرى!\n
* إضافة دعم تلقائي للعديد من المتصفحات الأخرى \n
* تنفيذ جديد لـ OneDrive: يشمل دعم OneDrive للأعمال التجارية، الملفات المشتركة، نطاقات الوصول المحددة، العديد من الحسابات وإصلاح المشكلات مع الوصول دون اتصال\n
* إصلاحات الشوائب
</string>
<string name="ChangeLog_1_07"> الإصدار 1. 7\n
* إصلاحات لتعطل الأندرويد 9\nفي سامسونج
* السماح بفتح أكثر من قاعدة بيانات واحدة، متوافق مع KeeAutoExec\n
* SFTP: السماح بمصادقة المفتاح العمومي، تحقق مما إذا كان مفتاح المضيف قد تغير\n
* تقديم دعم pCloud - بفضل gilbsgilb!\n
* جعل دعم Nextcloud صريحا\n
* تحسين حفظ وتحديث مرفقات الدخول\n
* المزيد من الخيارات لتكييف السلوك مع التفضيلات الشخصية\n
* SSL: شهادات المستخدم الائتماني\n
* تحسين التعبئة التلقائية (يعمل الآن مع Firefox)، السماح بتقليل النوافذ المنبثقة)\n
* إصلاحات الشوائب\n
</string>
<string name="ChangeLog_1_06"> الإصدار 1. 6\n
* التبديل إلى ykDroid بدلاً من YubiChallenge كتطبيق لتحدي Yubikey - Response.\n
* تنفيذ الدعم للتحدي على نمط KeepassXC. ملاحظة: يجب أن يكون تنسيق قاعدة البيانات KDBX4!\n
* رفض تحميل الملفات المحذوفة من Google Drive\n
* تبديل تطبيق TLS لـ FTPS ، إضافة العمل إلى مشكلة JSch مع خوادم تدعم gssapi-مع mic\n
* إصلاحات الشوائب\n
</string>
<string name="ChangeLog_1_05"> الإصدار 1. 5\n
* استخدام قنوات الإشعار لنظام أندرويد 8، السماح بالتكوين من خلال إعدادات النظام\n
* إظهار رمز الإدخال في التنبيهات\n
* استخدام أيقونات التكيف للأندرويد 8، استخدام أيقونة مشغل الجولة للأندرويد 7\n
* السماح بتنشيط البحث عند فتح القفل (انظر الإعدادات)\n
* غيّر طريقة كتابة الملفات من خلال إطار الوصول إلى التخزين، إصلاح المشكلات مع تحديث الملفات على Google Drive التي تم فتحها من خلال منتقي ملفات النظام\n
* إضافة بعض نصوص المعلومات لتجنب بعض سوء الفهم الشائعة\n
* إنشاء نسخ احتياطية محلية لقواعد البيانات المفتوحة بنجاح للحد من خطر فقدان البيانات\n
* تم تحديث JSch لدعم المزيد من شفرات SSH الأحدث\n
* السماح بتعديل إعدادات الاتصال، e. . عند تغيير كلمة مرور WebDav\n
* تم إضافة دعم لوضع كلمة المرور الثابتة لـ Yubikey Neo\n
* السماح بتعطيل اقتراح التعبئة التلقائية\n
* تسرب البيانات الثابتة إلى logcat\n
* إصلاحات الشوائب\n
</string>
<string name="ChangeLog_1_04b"> الإصدار 1.04b\n
* تجنب الأعطال عندما يحاول المستخدم تمكين التعبئة التلقائية على أجهزة Huawei\n
</string>
<string name="ChangeLog_1_04"> الإصدار 1.04\n
* إضافة خدمة التعبئة التلقائية للأندرويد 8.0 وما بعد.\n
* تم ترقية المكتبات، بناء الأدوات والإصدار المستهدف SDK.\n
</string>
<string name="ChangeLog_1_03"> الإصدار 1.03\n
* إزالة خدمة الوصول إلى التعبئة التلقائية كما هو مطلوب من جوجل. الرجاء الاطلاع على إعدادات الوصول إلى كلمة المرور للعثور على إضافة تكرارية للوظيفة السابقة.\n
* أضيفت تطبيقات الطرف الثالث كخيار تخزين مرة أخرى\n
* مشاهدة الصور المتكاملة لعرض الصور المرفقة دون نقلها إلى تطبيقات أخرى\n
* تم ترقية OkHttp لإصلاح المشكلات مع بعض الاتصالات\n
* دعم إدخالات KeeTrayTOTP. يدعم الآن إدخالات Steam\n
</string>
<string name="ChangeLog_1_02"> الإصدار 1.02\n
* عدة تحسينات أمنية. شكرا جزيلا على التقرير الأمني الذي أعده jean-baptiste.cayrou@thalesgroup.com و vincent.fargues@thalesgroup. اوم و لتعاونهم!\n
* دعم تطبيق KeyboardSwapplugin (انظر خيارات الوصول إلى كلمة المرور): يسمح بتبديل طريقة الإدخال تلقائياً على الأجهزة غير المجذرة. شكراً لمشعل رحمان من مطوري XDA-developers لجعل هذا ممكناً.\n
* إصلاح خدمة إمكانية الوصول مع إصدارات كروم الأخيرة\n
* إصلاح لإزالة بيانات بصمات الأصابع بلا داع\n
* إصلاح الأعطال الثانوية\n
* تحديث Dropbox SDK لضمان التوافق في المستقبل\n
* إزالة الإبلاغ عن الأخطاء من خلال تصورات Xamarin\n
* تحديث أدوات البناء\n
</string>
<string name="ChangeLog_1_01g"> الإصدار 1. 1-g\n
* إصلاح الأعطال عند محاولة العمل دون اتصال\n
* إصلاح لترميز بيانات اعتماد FTP(S) بشكل غير صحيح\n
* إصلاح الأعطال عند استخدام OneDrive وعلى إصدارات الأندرويد الأقدم\n
* عرض الأوقات المحلية في شاشة الدخول\n
</string>
<string name="ChangeLog_1_01d"> الإصدار 1. 1-d\n
* إصلاح قائمة ملفات OneDrive\n
* السماح بتجاهل أخطاء الشهادة أيضًا عندما فشل التحقق من اسم المضيف (غير موصى به للاستخدام في الإنتاج)\n
* إصلاح لفتح القفل السريع في بعض الأحيان على الرغم من رمز الفتح الصحيح\n
</string>
<string name="ChangeLog_0_9_8c">الإصدار 0.9.8c
* إصلاح مشكلة ضعف طبقة المقابس الآمنة (SSL) في Microsoft Live SDK (تستخدم عند الوصول إلى الملفات عبر OneDrive)
* إصلاح بعض الأخطاء: الإصدار السابق يحتوي على طرقتين للإدخال (واحدة تحطمت) </string>
@@ -536,16 +757,135 @@
*إضافة دعم ل OwnCloud.
* طلب إذن للتخزين قبل فتح الملفات المحلية
</string>
<string name="ChangeLog_1_0_0e"> الإصدار 1.0. e\n
* إصلاح لفتح قفل البصمة على أجهزة سامسونج الأقدم مع أندرويد 6\n
* إضافة دعم أصلي لأجهزة x86\n
* السماح بإخفاء لوحة المفاتيح أثناء فحص بصمات الإصبع\n
* إنشاء تحديث النظام
</string>
<string name="ChangeLog_1_0_0"> الإصدار 1.0.0\n
* فتح بصمة الإصبع (يتطلب أندرويد 6. + أو جهاز سامسونج)\n
* تمت إضافة خدمة التعبئة التلقائية (يتطلب أندرويد 5. +)\n
* تم إضافة دعم لقوالب الإدخال\n
* تم إضافة وضع \"العمل دون اتصال\"\n
* السماح بنسخ المدخلات\n
* وضع الإكمال التلقائي للأسماء الحقول\n
* السماح بإزالة العناصر من قائمة الملفات الحديثة\n
* طلب أذونات في وقت التشغيل في Android 6.\n
* إصلاحات الشوائب (في لوحة المفاتيح المدمجة، عند تحديد الأيقونات\n
* يتضمن خيار إرسال تقارير الأخطاء\n
* تم إضافة رسائل مساعدة في عدة نقاط\n
</string>
<string name="ChangeLog_0_9_9">&lt;unk&gt; نسخة 0.9.9\n
&lt;unk&gt; * أكمل إعادة تصميم واجهة المستخدم. شكرا جزيلا لستيفانو بيغناتارو (http://www.spstudio). ) لدعمه!\n
* السماح بإضافة أيقونات مخصصة\n
* دعم وضع النافذة المتعددة على أجهزة سامسونج\n
* زيادة العدد الافتراضي لجولات التشفير لقواعد البيانات الجديدة\n
* التحقق من المفاتيح المكررة للحقول الإضافية لتجنب فقدان البيانات\n
</string>
<string name="ChangeLog_0_9_9c"> الإصدار 0.9. c\n
* موضوع الظلام عاد\n
* يمكنك تثبيت حزم أيقونات أخرى (أيقونات نمط ويندوز القديمة متوفرة على متجر Play)\n
* إضافة سؤال تأكيد عند حذف عناصر بدون سلة إعادة التدوير\n
* إصلاحات الشوائب (عرض خاطئ لترميز OTP السري، أيقونة التطبيق خاطئة في بعض الأماكن)\n
</string>
<string name="ChangeLog_0_9_8b">&lt;unk&gt; الإصدار 0.9. (ب)\n
&lt;unk&gt; * إصلاحات الشوائب (فشل الحفظ لبعض قواعد البيانات، التصدير إلى الجهاز المحلي لا يعمل، تحديد بعض خيارات التفضيلات تعطل التطبيق)\n</string>
<string name="ChangeLog_0_9_8">&lt;unk&gt; الإصدار 0.9.\n
&lt;unk&gt; * دعم لإطار الوصول إلى التخزين (يسمح بالكتابة إلى بطاقة SD و Google Drive في KP2A Offline)\n
&lt;unk&gt; * حاول الكشف عن إدخال المستخدم الخاطئ عند إدخال عناوين URL لـ WebDAV (الدليل بدلا من الملف)\n
&lt;unk&gt; * * تغيير خط كلمة المرور\n
&lt;unk&gt; * السماح بتغيير حساب Dropbox\n
&lt;unk&gt; * إصلاح خطأ: الآن تذكر كلمة مرور OTP</string>
<string name="ChangeLog_0_9_7b">&lt;unk&gt; الإصدار 0.9.7b\n
&lt;unk&gt; * الترجمات المحدثة\n
&lt;unk&gt; * إصلاحات الشوائب: خط كلمة المرور مفقود في 0. .7، الترتيب حسب الاسم لم يتم فرز المجموعات\n</string>
<string name="ChangeLog_0_9_7">&lt;unk&gt; الإصدار 0.9.\n
&lt;unk&gt; * دعم الكتابة لقاعدة بيانات Keepass 1 (kdb) (بيتا!\n
&lt;unk&gt; * التبديل الأفضل إلى لوحة المفاتيح السابقة (يعمل أيضًا على الأجهزة غير الجذرية)\n
&lt;unk&gt; &lt;unk&gt; * دعم تحدي KeeChallenge مع تحديات الطول المتغيرة\n
&lt;unk&gt; &lt;unk&gt; &lt;unk&gt; * منع التقاط لقطات الشاشة من QuickUnlock وشاشات كلمة المرور\n
&lt;unk&gt; * عكس ترتيب الترتيب للترتيب حسب تاريخ التعديل (التنازل)\n
&lt;unk&gt; * إصلاحات الشوائب: يتم تحديث الملاحظات الآن بشكل صحيح بعد التغييرات، عرض كلمة المرور الآن يخفي كلمة المرور بشكل صحيح على (آمل) جميع الأجهزة، وقم بإصلاح المشكلة التي سمحت بإضافة إدخال مرتين، إصلاح المشكلة مع عرض تحذير UUID المكرر حتى بعد إصلاح قاعدة البيانات\n</string>
<string name="ChangeLog_0_9_6">&lt;unk&gt; الإصدار 0.9.\n
&lt;unk&gt; * السماح باستيراد ملف المفتاح و/أو ملف قاعدة البيانات المحلية إلى الدليل الداخلي للتطبيق (انظر الإعدادات)\n
&lt;unk&gt; &lt;unk&gt; * السماح بخيارات فرز مختلفة\n
&lt;unk&gt; &lt;unk&gt; * تحسين تفضيلات التبديل التلقائي للوحة المفاتيح\n
&lt;unk&gt; &lt;unk&gt; * تحديث تصميم شعار التطبيق والإشعارات تصميم من ستيفانو بيغناتارو (http://www. الاستوديو. ر)\n
&lt;unk&gt; * يتذكر مولد كلمة المرور آخر إعدادات\n
&lt;unk&gt; &lt;unk&gt; * تعيين ظهور الإشعار لشاشة قفل أندرويد 5\n
&lt;unk&gt; * الآن مسح حقل كلمة المرور الرئيسية عند مغادرة التطبيق دون النقر على موافق\n
&lt;unk&gt; * ثابت مع غياب لغات الإدخال في إعداد لوحة المفاتيح على بعض الأجهزة\n
&lt;unk&gt; * ثابت مع تشغيل لوحة المفاتيح التلقائية على الأجهزة ذات الجذور\n
&lt;unk&gt; * إضافة فحص لقواعد البيانات الفاسدة (UIDs)\n
&lt;unk&gt; * إعادة تحميل قاعدة البيانات تلقائيا عند اكتشاف التغيير، حل المخاوف الأمنية حول الكشف عن كلمة المرور الرئيسية\n
&lt;unk&gt; * تحسين تصميم لوحة المفاتيح الصغيرة المهذبة، تثبيت إعدادات لوحة المفاتيح (بفضل ويكتور &lt;unk&gt; awski)\n</string>
<string name="ChangeLog_0_9_5">&lt;unk&gt; &lt;b&gt;الإصدار 0.9.5&lt;/b&gt;\n
&lt;unk&gt; * إصلاح المشاكل مع تصفح الملفات (خاصة على أندرويد 4. )\n
&lt;unk&gt; * مشكلة ثابتة مع التحميل. ملفات db (Keepass 1) على Nexus 5 مع Android Lollipop\n
&lt;unk&gt; &lt;unk&gt; * إضافة خيار لمنع التقاط لقطات الشاشة/عرض التطبيق في قائمة التطبيقات الحديثة\n
&lt;unk&gt; &lt;unk&gt; * إصلاح مشكلة مع تخزين ملفات Google Drive (الطبعة العادية)\n
&lt;unk&gt; * السماح للملفات الرئيسية على أنواع التخزين المتعمدة (الطبعة العادية)\n
&lt;unk&gt; * تحديث Dropbox SDK لتضمين تصحيح أمن رسمي (الطبعة العادية)\n
&lt;unk&gt; * أدوات البناء المحدثة --&gt; حجم apk زادع:-(\n
&lt;unk&gt; I وعدت ببعض التغييرات. سوف تأتي مع الإصدار التالي - آسف. أردت نشر هذه الإصلاحات الساخنة في أقرب وقت ممكن.</string>
<string name="ChangeLog_0_9_4">&lt;unk&gt; &lt;b&gt;الإصدار 0.9.&lt;/b&gt;\n
&lt;unk&gt; * إضافة دعم إضافي: راجع الإعدادات لكيفية الحصول على الإضافات!\n
&lt;unk&gt; * نشر ملحق QR (مسح كلمات المرور، عرض كلمات المرور كرمز QR ، نقل الإدخالات إلى أجهزة KP2A الأخرى)\n
&lt;unk&gt; * إضافة InputStick منشورة (نقل بيانات الاعتماد إلى جهاز الكمبيوتر الخاص بك عن طريق البلوتوث - يتطلب InputStick USB stick)\n
&lt;unk&gt; * يمكن لتطبيقات الطرف الثالث الآن ببساطة تنفيذ الاستعلام KP2A للحصول على بيانات الاعتماد. هل أنت مطور؟ الرجاء إضافة هذا إلى التطبيق الخاص بك إذا كان ذلك مناسباً!\n
&lt;unk&gt; * أضاف دعم TOTP (المتوافقة مع KeeOTP و TrayTotp)\n
&lt;unk&gt; * يجب ألا يتم قتل التطبيق بواسطة Android عندما تكون قاعدة البيانات مفتوحة\n
&lt;unk&gt; &lt;unk&gt; * لم تعد قاعدة البيانات مقفلة عند مغادرة التطبيق مع زر الرجوع (انظر الإعدادات)\n
&lt;unk&gt; &lt;unk&gt; * * عرض أسماء المجموعات في عرض نتائج البحث (*)\n
&lt;unk&gt; * إضافة قائمة السياق في عرض نتائج البحث بما في ذلك خيار \"الانتقال إلى الأصل\" (*)\n
&lt;unk&gt; * إضافة خيار لعرض اسم المجموعة في عرض المدخل (*)\n
&lt;unk&gt; * * * (*) بفضل ماثيو *) لتنفيذ هذه الميزاتيو !\n
&lt;unk&gt; * دعم KeeChallenge (مع الجسم القريب من يوبيكي). شكرا لبن روش لتشغيل الموصل!\n
&lt;unk&gt; * تحسين واجهة المستخدم\n
&lt;unk&gt; * أصلح خطأ في واجهة Google Drive\n
&lt;unk&gt; * إضافة خيار لتعطيل خيار \"التبرع\"\n
&lt;unk&gt; &lt;unk&gt; * رمز فتح القفل المخفي الآن على Android 4. + أجهزة افتراضية\n </string>
<string name="ChangeLog_0_9_3_r5">&lt;unk&gt; &lt;b&gt;الإصدار 0.9.3 r5&lt;/b&gt;\n
&lt;unk&gt; * إصلاحات مدمجة من Xamarin: Keepass2Android متوافقة الآن مع الـ ART على أندرويد 4. .2. أخيراً!\n
&lt;unk&gt; * إصلاحات الشوائب: الأخطاء في المزامنة (تحديث العرض، التحقق الصحيح من التغييرات على http)، الأخطاء على أندرويد 2. أجهزة، أخطاء في تطبيقات تخزين Google Drive و OneDrive ، مسح الحافظة على قاعدة البيانات المغلقة، خلل في فتح المرفقات، عرض المشاكل مع لوحة المفاتيح\n</string>
<string name="ChangeLog_0_9_3">&lt;unk&gt; &lt;b&gt;الإصدار 0.9.3&lt;/b&gt;\n
&lt;unk&gt; &lt;unk&gt; * لوحة مفاتيح جديدة مع العديد من التحسينات. راجع الإعدادات للتخصيص.\n
&lt;unk&gt; * دعم للكيدب للقراءة فقط (Keepass 1 files). تجريبي!\n
&lt;unk&gt; * إضافة دعم SFTP\n
&lt;unk&gt; * إضافة عمل للخلل في ART (أندرويد 4. .2)\n
&lt;unk&gt; * إصلاحات الشوائب\n</string>
<string name="ChangeLog_0_9_2">&lt;unk&gt; &lt;b&gt;الإصدار 0.9.&lt;/b&gt;\n
&lt;unk&gt; * أضيف دعم OTP (متوافق مع ملحق OtpKeyProv)\n
&lt;unk&gt; * دعم NFC المتكامل لبرمجيات OTP من الجسم القريب من YubiKey \n
&lt;unk&gt; &lt;unk&gt; * عدة تحسينات في واجهة المستخدم\n
&lt;unk&gt; &lt;unk&gt; * مدمج Keepass 2. 4 مكتبة\n
&lt;unk&gt; * إضافة خيار لقتل عملية التطبيق (انظر الإعدادات)\n
&lt;unk&gt; * تحسين التحقق من صحة شهادة SSL\n
&lt;unk&gt; &lt;unk&gt; * إصلاحات الشوائب\n</string>
<string name="ChangeLog_0_9_1">الإصدار 0.9.1
*دعم متكامل ل SkyDrive (النسخة النظامية فقط منKeepass2Android)
*حل مشاكل التكامل مع Google Drive.
*إضافة دعم لميزة NTLM</string>
<string name="ChangeLog_0_9">&lt;unk&gt; &lt;b&gt;الإصدار 0.&lt;/b&gt;\n
&lt;unk&gt; * دعم دروببوكس و Google Drive المتكامل (قواعد بيانات القراءة/الكتابة؛ Keepass2Android عدد منتظم فقط)\n
&lt;unk&gt; * متصفح الملفات المخصص المدمج (على أساس أندرويد-filechooser بواسطة HBA)\n
&lt;unk&gt; &lt;unk&gt; * تحسين واجهة المستخدم لإنشاء قواعد بيانات جديدة\n
&lt;unk&gt; * تضمين خط مخصص DjaVu Sans Mono لعرض كلمات المرور\n
&lt;unk&gt; * إصلاحات الشوائب</string>
<string name="ChangeLog_0_8_6">الإصدار 0.8.6
*دعم ميزة Twofish cipher.
*السماح بتعديل المجموعات.
*السماح بنقل القيود والمجموعات.
*يمكن جعل أيقونة(فتح القفل السريع) QuickUnlock شفافة (راجع الإعدادات).
*إصلاحات أخرى</string>
<string name="ChangeLog_0_8_5">&lt;unk&gt; &lt;b&gt;الإصدار 0.8.&lt;/b&gt;\n
&lt;unk&gt; * يتم تخزين الملفات البعيدة في ذاكرة التخزين المؤقت للتطبيق المحلي للسماح بالاستخدام دون اتصال (بما في ذلك التحرير والمزامنة لاحقا). عرض الإعدادات. \n
&lt;unk&gt; * رمز إشعار لتصور حالة قفل قاعدة البيانات (انظر الإعدادات)\n
&lt;unk&gt; * تحسين تحديد حالة القفل في بعض الحالات\n
&lt;unk&gt; * يتم تحميل ملفات قاعدة البيانات إلى الذاكرة أثناء كتابة كلمة المرور لزيادة سرعة التحميل (انظر الإعدادات)\n
&lt;unk&gt; * يمكن إضافة المدخلات إلى مجموعة الجذر\n
&lt;unk&gt; * إصلاحات الأخطاء (حل الحقول المرجعية) مشاكل مع لوحة المفاتيح على الأجهزة الإيطالية والصينية)</string>
<string name="ChangeLog_0_8_4"><b>الإصدار 0.8.4</b>\n
* يتم الكشف عن تغييرات قاعدة البيانات الخارجية ودمجها عند الحفظ\n
* تحسين أداء التحميل\n
@@ -616,6 +956,7 @@
<string-array name="design_options">
<item>فاتح</item>
<item>مظلم</item>
<item>إعدادات النظام</item>
</string-array>
<string name="design_title">التصميم</string>
<string-array name="ftp_encryption_modes">
@@ -635,23 +976,45 @@
<item>كلمة السر + سر \"كلمة السر أحادية الاستخدام\" (وضع الاسترجاع)</item>
<item>كلمة السر + إجابة السؤال الشخصي</item>
<item>كلمة السر + سر إجابة السؤال الشخصي (وضع الاسترجاع)</item>
<item>Password + Challenge-Response for Keepass XC</item>
<item>Password + Key file + Challenge-Response for Keepass XC</item>
<item>كلمة المرور + الاستجابة ل Keepass XC</item>
<item>كلمة المرور + ملف المفتاح + تحدي الاستجابة ل Keepass XC</item>
</string-array>
<string-array name="sftp_auth_modes">
<item>كلمة السر</item>
<item>Private/Public key</item>
<item>المفتاح الخاص/العمومي</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">
<item>تجاهل فشل التحقق من صحة الشهادة</item>
<item>تحذير عند فشل التحقق من الصحة</item>
<item>لا تقبل شهادات غير صالحة</item>
</string-array>
<string name="ClearClipboardWarning">تأكد من أن هذا يعمل على النظام الخاص بك والنظر في استخدام لوحة المفاتيح المدمجة إن لم تكن كذلك.</string>
<string name="PluginDescription">الوصف المقدم من العنصر الإضافي :</string>
<string name="autofill_hint">Keepass2Android يدعم ميزة التعبئة التلقائية لأندرويد، ولكن يبدو أنك لم تقم بتمكينها بعد.</string>
<string name="autofill_enable">تمكين التعبئة التلقائية</string>
<string name="autofill_enable_failed">عذراً، يبدو أن جهازك لا يدعم فتح الإعدادات من داخل التطبيق. الرجاء الذهاب يدوياً إلى إعدادات النظام من أجل التعبئة التلقائية لتمكين الخدمة.</string>
<string name="show_autofill_help">إظهار تعليمات الملء التلقائي</string>
<string name="autofill_sign_in_prompt">ملء مع Keepass2Android</string>
<string name="autofill_disable">تعطيل التعبئة التلقائية لـ %1$s</string>
<string name="autofill_enable_for">تمكين التعبئة التلقائية لـ %1$s</string>
<string name="invalid_link_association">تعذر ربط نطاق الويب %1$s بالتطبيق %2$s</string>
<string name="enable_fingerprint_hint">اكتشف Keepass2Android أجهزة قياس أحيائي. هل تريد تمكين فتح القفل الحيوي لقاعدة البيانات هذه؟</string>
<string name="understand">أنا أفهم</string>
<string name="dont_show_again">لا تظهر مرة أخرى</string>
<string name="masterkey_infotext_head">هل تذكر كلمة المرور الرئيسية؟</string>
<string name="masterkey_infotext_main">لاحظ أنك لن تتمكن من فتح قاعدة البيانات الخاصة بك بدون المفتاح الرئيسي. لا توجد طريقة \"إعادة تعيين\" كلمة المرور الرئيسية.</string>
<string name="masterkey_infotext_fingerprint_note">لاحظ أيضًا أن فتح القفل الحيوي يعمل بتخزين المفتاح الرئيسي الخاص بك في التخزين الآمن للأندرويد. يمكن حذف هذه الذاكرة بواسطة Android في أي وقت، e. . إذا قمت بإضافة بصمة جديدة في إعدادات النظام. لذلك لا تعتمد على فتح القفل الحيوي ولكن تذكر كلمة المرور الرئيسية الخاصة بك، من فضلك!</string>
<string name="backup_infotext_head">هل يتم النسخ الاحتياطي لقاعدة البيانات الخاصة بك؟</string>
<string name="backup_infotext_main">Keepass2Android يخزن كلمات المرور الخاصة بك في ملف على موقع تختاره. هل أنت متأكد من أنك لا يزال بإمكانك الوصول إلى هذا الملف عند فقدان هاتفك أو سرقته، أو عندما يتم تدمير الملف أو حذفه؟ الرجاء التأكد من أن لديك دائماً نسخة احتياطية حديثة في مكان آمن!</string>
<string name="backup_infotext_note">لإنشاء نسخة احتياطية الآن، انتقل إلى %1$s &gt; %2$s &gt; %3$s.</string>
<string name="emergency_infotext_head">هل أنت مستعد لحالات الطوارئ؟</string>
<string name="emergency_infotext_main">هل فكرت في ما يحدث إذا كنت غير قادر على الوصول إلى قاعدة بيانات كلمة المرور الخاصة بك؟ ماذا لو كان لديك حادث؟ إنه ممارسة جيدة لتمرير المفتاح الرئيسي الخاص بك إلى شخص موثوق به في حالات الطوارئ. لن يتمكن أحد من الوصول إلى كلمات المرور الخاصة بك خلاف ذلك.</string>
<string name="no_secure_display">لا يتم وضع علامة على العرض الصالح حاليا كآمن. هذا يعني أن لقطات الشاشة قد تؤخذ من تطبيقات أخرى. تم تكوين Keepass2Android لعرض المعلومات الحساسة على العروض الآمنة فقط. الرجاء التغيير إلى شاشة عرض آمنة (على سبيل المثال عن طريق فصل شاشة HDMI) أو تغيير إعدادات التطبيق.</string>
<string name="disable_secure_screen_check">تعطيل هذه الرسالة</string>
<string name="switch_ime_text">الرجاء تفعيل لوحة مفاتيح Keepass2Android.</string>
<string name="switch_ime_reopen">إعادة المحاولة</string>
<string name="AutofillWarning_title">تنبيه الأمن: رابط نطاق/تطبيق غير معترف به</string>
<string name="AutofillWarning_Intro">أنت على وشك إدراج بيانات اعتماد للنطاق \"%1$s\" في التطبيق \"%2$s\".</string>
<string name="AutofillWarning_FillDomainInUntrustedApp">إذا كنت تثق في \"%2$s\" بالانتماء إلى \"%1$s\" أو كنت تثق في التطبيق \"%2$s\" لعدم إساءة استخدام بيانات الاعتماد (e. . لأنه تطبيق متصفح موثوق به)، من المفضل المتابعة. إذا لم يكن الأمر كذلك ، يرجى الإلغاء .</string>
<string name="AutofillWarning_trustAsBrowser">قبول دائمًا في \"%1$s\"</string>
</resources>

View File

@@ -4,6 +4,9 @@
<string name="about_feedback">Əks əlaqə</string>
<string name="accept">Qəbul et</string>
<string name="deny">Rədd et</string>
<string name="add_entry">Qeyd əlavə et</string>
<string name="edit_entry">Qeydə düzəliş et</string>
<string name="add_url_entry">URL üçün qeyd yarat</string>
<string name="add_group">Qrup əlavə et</string>
<string name="add_group_title">Qrup əlavə et</string>
<string name="edit_group_title">Qrupa düzəliş et</string>
@@ -13,15 +16,28 @@
<string name="short_app_name">KP2A</string>
<string name="app_name_nonet">Keepass2Android Xətdən Kənar</string>
<string name="short_app_name_nonet">KP2A Xətdən Kənar</string>
<string name="app_timeout">Tətbiq istifadə vaxtı</string>
<string name="kill_app_label">Tətbiqi bağla</string>
<string name="show_kill_app">Bağla düyməsi</string>
<string name="application">Tətbiq</string>
<string name="application_settings">Tətbiq tənzimləmələri</string>
<string name="ShowGroupnameInSearchResult_title">Qrup adını axtarış nəticələrində göstər</string>
<string name="NavigationToGroupCompleted_message">Qrup adı indi: %1$s</string>
<string name="AutofillDisabledQueriesPreference_title">Avto-doldurma hədəfləri sıradan çıxarıldı</string>
<string name="OfferSaveCredentials_title">Kimlik məlumatlarını saxlamağı təklif et</string>
<string name="ShowGroupInEntry_title">Qeyd görünüşündə qrup adını göstər</string>
<string name="Entry_singular">Bir qeyd</string>
<string name="Entry_plural">%1$d qeyd</string>
<string name="IconSet_title">Nişan dəsti</string>
<string name="IconSet_install">Daha çox tap...</string>
<string name="security_prefs">Təhlükəsizlik</string>
<string name="display_prefs">Görüntü</string>
<string name="password_access_prefs">Şifrə qeyd müraciəti</string>
<string name="QuickUnlock_prefs">Cəld Kilid Açma</string>
<string name="FileHandling_prefs">Fayl idarəsi</string>
<string name="keyboard_prefs">Klaviatura</string>
<string name="export_prefs">Verilənlər bazasını ixrac et...</string>
<string name="fingerprint_prefs">Biometrik kilid açma</string>
<string name="import_db_prefs">Verilənlər bazasını daxili qovluğa idxal et</string>
<string name="OnlyAvailableForLocalFiles">Yalnız yerli fayllar üçün mövcuddur.</string>
<string name="enter_filename">Verilənlər bazası fayl adını daxil edin</string>
@@ -46,9 +62,24 @@
<string name="error_no_name">Ad lazımdır.</string>
<string name="hint_login_pass">Şifrə</string>
<string name="hint_url">URL</string>
<string name="root">Kök (root)</string>
<string-array name="PasswordGeneratorModes">
<item>Şifrə</item>
<item>Passphrase</item>
<item>Passphrase + Password</item>
</string-array>
<string name="select_other_entry">Başqa qeyd seç</string>
<string name="loading">Yüklənir…</string>
<string name="TemplateField_IdCard_Name">Ad</string>
<string name="DbQuicklockedChannel_name">Cəld Kilid Açma</string>
<string-array name="sftp_auth_modes">
<item>Şifrə</item>
<item>Private/Public 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>
<string name="switch_ime_reopen">Yenidən sına</string>
<string name="AutofillWarning_title">Təhlükəsizlik xəbərdarlığı: Tanınmayan domen/tətbiq bağlantısı</string>
<string name="AutofillWarning_Intro">\"%2$s\" tətbiqinə \"%1$s\" domeni üçün kimlik məlumatlarını daxil edirsiniz.</string>
<string name="AutofillWarning_trustAsBrowser">\"%1$s\" səyyahında həmişə qəbul et</string>
</resources>

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,7 @@
<string name="algorithm_colon">Алгоритъм</string>
<string name="app_name">Keepass2Android</string>
<string name="short_app_name">KP2A</string>
<string name="app_name_nonet">Keepass2Android Офлайн</string>
<string name="app_name_nonet">Keepass2Android офлайн</string>
<string name="short_app_name_nonet">KP2A Офлайн</string>
<string name="show_kill_app">Бутон-Затваряне</string>
<string name="ShowGroupnameInSearchResult_title">Показване на името на групата в резултата от търсенето</string>
@@ -39,6 +39,7 @@
<string name="export_prefs">Експортиране на база данни...</string>
<string name="import_db_prefs">Импортиране на БД във вътрешна папка</string>
<string name="import_keyfile_prefs">Импортирай ключ от вътрешна папка</string>
<string name="export_keyfile_prefs">Импортирай ключ от вътрешна папка</string>
<string name="keyboardswitch_prefs">Превключване на клавиатурата</string>
<string name="OnlyAvailableForLocalFiles">Достъпно само за локални файлове.</string>
<string name="FileIsInInternalDirectory">Файлът се съхранява във вътрешна папка</string>
@@ -197,6 +198,11 @@
<string name="sort_moddate">Сортирай по дата на модификация</string>
<string name="sort_default">Запази ред по подразбиране</string>
<string name="special">Специални</string>
<string-array name="PasswordGeneratorModes">
<item>Парола</item>
<item>Passphrase</item>
<item>Passphrase + Password</item>
</string-array>
<string name="search_hint">Търси</string>
<string name="search_results">Резултати от търсенето</string>
<string name="search_in">Търсене в</string>
@@ -250,8 +256,6 @@
<string name="QuickUnlockHideLength_title">Скрий дължината на \"Бързо отключване\".</string>
<string name="QuickUnlockHideLength_summary">При активация, дължината на кодът за \"Бързо отключване\" не се показва върху полето за \"Бързо отключване\". </string>
<string name="QuickUnlock_fail">Бързо отключване НЕУСПЕШНО: невярна парола!</string>
<string name="BinaryDirectory_title">Папка с приложени файлове</string>
<string name="BinaryDirectory_summary">Папка където се записват приложените файлове.</string>
<string name="SaveAttachmentDialog_title">Запази приложен файл</string>
<string name="SaveAttachmentDialog_text">Моля изберете къде да се запише приложения файл.</string>
<string name="SaveAttachmentDialog_open">Запази в кеша и отвори</string>

View File

@@ -237,6 +237,11 @@
<string name="sort_moddate">Ordenar per data modificació</string>
<string name="sort_default">Mantenir l\'ordre per defecte</string>
<string name="special">Especial</string>
<string-array name="PasswordGeneratorModes">
<item>Contrasenya</item>
<item>Passphrase</item>
<item>Passphrase + Password</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>
@@ -294,8 +299,6 @@
<string name="QuickUnlockHideLength_title">Amagar longitud de DesbloqueigRàpid</string>
<string name="QuickUnlockHideLength_summary">Si està activat, la longitud del codi de DesbloqueigRàpid no apareix a la pantalla de DesbloqueigRàpid.</string>
<string name="QuickUnlock_fail">DesbloqueigRàpid ha fallat: Contrasenya incorrecta!</string>
<string name="BinaryDirectory_title">Directori d\'arxius adjunts</string>
<string name="BinaryDirectory_summary">Directori on es guarden els fitxers adjunts.</string>
<string name="SaveAttachmentDialog_title">Deseu el fitxer adjunt</string>
<string name="SaveAttachmentDialog_text">Si us plau seleccioneu on voleu desar el fitxer adjunt.</string>
<string name="SaveAttachmentDialog_save">Exportar a fitxer...</string>
@@ -912,6 +915,7 @@ Revisió inicial per al públic
<string-array name="design_options">
<item>Clar</item>
<item>Fosc</item>
<item>System settings</item>
</string-array>
<string name="design_title">Disseny</string>
<string-array name="ftp_encryption_modes">

View File

@@ -30,7 +30,7 @@
<string name="ShowGroupnameInSearchResult_title">Zobrazit název skupiny ve výsledku hledání</string>
<string name="ShowGroupnameInSearchResult_resume">Zobrazovat jméno skupiny pod titulkem záznamu ve výsledcích hledání. Užitečné, máte-li několik záznamů stejného jména.</string>
<string name="NavigationToGroupCompleted_message">Zobrazení jména kategorií je nyní: %1$s</string>
<string name="AutofillDisabledQueriesPreference_title">Zakázat Automatické doplnění pro cíle</string>
<string name="AutofillDisabledQueriesPreference_title">Zakázané cíle pro Automatické vyplňování</string>
<string name="AutofillDisabledQueriesPreference_summary">Zobrazí seznam aplikací a webových stránek, pro které bylo zakázáno Automatické vyplnění</string>
<string name="OfferSaveCredentials_summary">Pokud je povoleno, Android se bude ptát zda chcete ukládat přihlašovací údaje poté, co jste tyto data ručně zadali do polí pro automatické vyplnění.</string>
<string name="OfferSaveCredentials_title">Nabízet ukládání přihlašovacích údajů</string>
@@ -50,6 +50,7 @@
<string name="fingerprint_prefs">Biometrické odemknutí</string>
<string name="import_db_prefs">Import databáze do vnitřní složky</string>
<string name="import_keyfile_prefs">Importovat soubor s klíčem do vnitřní složky</string>
<string name="export_keyfile_prefs">Exportovat soubor klíče z interní složky</string>
<string name="keyboardswitch_prefs">Přepínání klávesnice</string>
<string name="OnlyAvailableForLocalFiles">K dispozici pouze pro místní soubory.</string>
<string name="FileIsInInternalDirectory">Soubor je uložen ve vnitřním adresáři.</string>
@@ -114,7 +115,7 @@
<string name="entry_modified">Změněno</string>
<string name="entry_password">Heslo</string>
<string name="entry_save">Uložit</string>
<string name="entry_title">Název</string>
<string name="entry_title">Jméno</string>
<string name="entry_url">Adresa URL</string>
<string name="entry_user_name">Uživatelské jméno</string>
<string name="entry_extra_strings">Doplňková textová pole</string>
@@ -228,6 +229,7 @@
<string name="saving_database">Ukládám databázi…</string>
<string name="exporting_database">Exportování databáze…</string>
<string name="export_database_successful">Databáze byla úspěšně exportována!</string>
<string name="export_keyfile_successful">Soubor s klíčem úspěšně exportován!</string>
<string name="space">Mezera</string>
<string name="search_label">Hledat</string>
<string name="show_password">Zobrazit heslo</string>
@@ -237,6 +239,25 @@
<string name="sort_moddate">Seřadit podle data změny</string>
<string name="sort_default">Zachovat výchozí pořadí</string>
<string name="special">Zvláštní znaky</string>
<string name="special_extended">Rozšířené speciální</string>
<string name="at_least_one_from_each_group">Alespoň jeden z každé skupiny</string>
<string name="exclude_lookalike">Vynechat podobné znaky</string>
<string name="password_generation_profile">Profil</string>
<string name="save_password_generation_profile_text">Zadejte jméno profilu pro uložení. Zadáním existujícího jména profil přepíšete.</string>
<string name="hint_wordcount">Počet slov v heslové frázi</string>
<string name="hint_wordseparator">Oddělovač slov</string>
<string-array name="PasswordGeneratorModes">
<item>Heslo</item>
<item>Heslová fráze</item>
<item>Heslová fráze + Heslo</item>
</string-array>
<string-array name="PasswordGeneratorCaseModes">
<item>malá písmena</item>
<item>VELKÁ PÍSMENA</item>
<item>Velké počáteční písmeno</item>
</string-array>
<string name="custom_settings">Vlastní nastavení</string>
<string name="passphrase_capitalization">Velká písmena v heslové frázi</string>
<string name="search_hint">Co hledat</string>
<string name="search_results">Výsledky hledání</string>
<string name="search_in">Hledat v</string>
@@ -267,6 +288,8 @@
<string name="ShowUsernameInList_summary">Zobrazovat uživatelská jména pod názvy položek. Užitečné pro vícenásobné účty nebo TANy.</string>
<string name="RememberRecentFiles_title">Zapamatovat databáze</string>
<string name="RememberRecentFiles_summary">Pamatovat s naposledy otevřené databáze a zobrazit je na obrazovce Otevírání databáze.</string>
<string name="NoDalVerification_title">Žádné ověření DAL</string>
<string name="NoDalVerification_summary">Zrušit kontrolu pokud doména a položka v aplikaci souhlasí.</string>
<string name="kp2a_findUrl">Najít heslo</string>
<string name="excludeExpiredEntries">Vyloučit expirované položky</string>
<string name="search_options">Možnosti</string>
@@ -295,8 +318,6 @@
<string name="QuickUnlockHideLength_title">Schovej délku kódu pro Rychlé odemknutí</string>
<string name="QuickUnlockHideLength_summary">Je-li tato možnost aktivní, délka kódu Rychlého odemknutí nebude zobrazena na odemykací obrazovce.</string>
<string name="QuickUnlock_fail">Rychlé odemknutí se nezdařilo: nesprávné heslo!</string>
<string name="BinaryDirectory_title">Adresář souborů s přílohami</string>
<string name="BinaryDirectory_summary">Adresář, do kterého jsou ukládány přílohy souborů.</string>
<string name="SaveAttachmentDialog_title">Uložit přílohu</string>
<string name="SaveAttachmentDialog_text">Prosím vyberte, kam chcete uložit přílohu.</string>
<string name="SaveAttachmentDialog_save">Export do souboru...</string>
@@ -314,6 +335,13 @@
<string name="protection">Chráněné pole</string>
<string name="add_binary">Přidat soubor jako přílohu...</string>
<string name="add_extra_string">Přidat doplňkový text</string>
<string name="configure_totp">Konfigurace TOTP</string>
<string name="totp_secret_key">Tajný klíč</string>
<string name="totp_encoding_rfc6238">Výchozí nastavení RFC6238 tokenu</string>
<string name="totp_encoding_steam">Nastavení Steam tokenu</string>
<string name="totp_encoding_custom">Nastavení vlastního tokenu</string>
<string name="totp_time_step">Časový krok</string>
<string name="totp_length">Délka kódu</string>
<string name="delete_extra_string">Smazat doplňkový text</string>
<string name="database_loaded_quickunlock_enabled">%1$s: uzamčen. Rychlé odemknutí povoleno.</string>
<string name="database_loaded_unlocked">%1$s: odemčen.</string>
@@ -568,7 +596,7 @@
<string name="DefaultTemplate">Standardní položka</string>
<string name="TemplateGroupName">Šablony</string>
<string name="TemplateTitle_IdCard">Identifikační karta</string>
<string name="TemplateField_IdCard_Name">Název</string>
<string name="TemplateField_IdCard_Name">Jméno</string>
<string name="TemplateField_IdCard_PlaceOfIssue">Místo vydání</string>
<string name="TemplateField_IdCard_IssueDate">Datum vydání</string>
<string name="TemplateTitle_EMail">E-Mail</string>
@@ -632,6 +660,28 @@
<string name="EntryChannel_name">Upozornění položky</string>
<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-array name="ChangeLog_1_09a">
<item>Přidána podpora pro soubory KDBX 4.1 zavedené v KeePass 2.48</item>
<item>Přidána možnost konfigurovat TOTP nastavení pro položky</item>
<item>Vylepšená generace hesel: Přidána podpora heslových frází, více možností, profilů a odhad síly hesla</item>
<item>Vylepšení automatického doplňování (opraveny vyskakovací okna v Chrome, vylepšena podpora subdomén)</item>
<item>Vylepšení implementace OneDrive: odstranění limitu velikosti, žádné zbytečné požadavky na ověření</item>
<item>Přidána možnost výběru světlého/tmavého vzhledu z nastavení systému, včetně nočního režimu, vyžaduje Android 10+</item>
<item>Update Dropbox implementation to support new authentication method.</item>
<item>Newly setup fingerprint unlock will be invalidated after adding a fingerprint in system settings for increased security.</item>
<item>Allow to open files through system file picker, ignoring the read-only flag</item>
<item>Allow to start moving entries from the entry view menu</item>
</string-array>
<string-array name="ChangeLog_1_08d">
<item>Přidání podpory pro nový souborový formát klíče představený v Keepass 2.47</item>
<item>Přidána podpora pro Argon2id jako funkci pro odvození klíče</item>
<item>Vylepšená kompatibilita automatického vyplňování s Firefoxem a Chrome</item>
<item>Zlepšení podpory pro TOTP záznamy z desktopových programů.</item>
<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>
</string-array>
<string-array name="ChangeLog_1_08c">
<item>Již se neukládají názvy balíčků Android aplikací v poli URL</item>
<item>Vylepšení chování zamykání - již se nezobrazuje výzva pro biometrické ověření těsně po odemčení</item>
@@ -641,9 +691,9 @@
<string-array name="ChangeLog_1_08b">
<item>Vynutit HTTP/1.1 kvůli chybě v implementaci HTTP/2 v OkHttp</item>
<item>Zlepšit dialogové okno klávesnice na Android 9+</item>
<item>Change file associations of the app to avoid some unnecessary associations</item>
<item>Odstranění zbytečných asociací souborů</item>
<item>Ujistěte se, že text hesla není skrytý za ikonou oka</item>
<item>Change autofill behavior to warn when filling credentials for a domain to an unrecognized app</item>
<item>Automatické doplňování nyní varuje pokud dojde k doplnění údajů k webové stránce do neznámé aplikace</item>
<item>Aktualizace FTP knihovny</item>
<item>Opravy možných pádů aplikace</item>
<item>Další drobné opravy</item>
@@ -935,6 +985,7 @@ První veřejné vydání
<string-array name="design_options">
<item>Světlý</item>
<item>Tmavý</item>
<item>Nastavení systému</item>
</string-array>
<string name="design_title">Vzhled</string>
<string-array name="ftp_encryption_modes">
@@ -988,8 +1039,11 @@ První veřejné vydání
<string name="emergency_infotext_head">Jste připraven/a na nouzové situace?</string>
<string name="emergency_infotext_main">Uvažoval/a jste co se stane v případě že již nebude schopen/a přístupu k Vaší databázi hesel? Co se stane v případě nehody? Dobrý způsob je, pro případ mimořádných okolností, sdělit hlavní klíč osobě které věříte. V opačném případě nikdo nebude schopen otevřít Vaši databázi hesel.</string>
<string name="no_secure_display">Aktuální displej není nastavený jako zabezpečený. Toto znamená že jiné aplikace mohou pořizovat snímky obrazovky. Nastavení Keepass2Android povoluje zobrazování soukromých informací pouze na zabezpečených displejích. Prosím přepněte na zabezpečený displej (např. odpojením HDMI monitoru) nebo změňte nastavení aplikace.</string>
<string name="disable_secure_screen_check">Tuto zprávu již neukazovat</string>
<string name="switch_ime_text">Aktivujte prosím klávesnici Keepass2Android.</string>
<string name="switch_ime_reopen">Opakovat</string>
<string name="AutofillWarning_title">Bezpečnostní upozornění: Neznámá doména/aplikace</string>
<string name="AutofillWarning_Intro">Chystáte se vložit přihlašovací údaje pro doménu \"%1$s\" do aplikace \"%2$s\".</string>
<string name="AutofillWarning_FillDomainInUntrustedApp">Pokud věříte že \"%2$s\" patří k \"%1$s\", nebo důvěřujete \"%2$s\" že nezneužije přihlašovací údaje (např. pokud se jedná o důvěryhodný prohlížeč), můžete pokračovat. Pokud tomu tak není, zrušte prosím tuto akci.</string>
<string name="AutofillWarning_trustAsBrowser">Přijmout vždy v \"%1$s\"</string>
</resources>

View File

@@ -47,8 +47,10 @@
<string name="FileHandling_prefs">Filhåndtering</string>
<string name="keyboard_prefs">Tastatur</string>
<string name="export_prefs">Eksportere database...</string>
<string name="fingerprint_prefs">Biometrisk oplåsning</string>
<string name="import_db_prefs">Importere database til intern mappe</string>
<string name="import_keyfile_prefs">Importer nøglefilen til intern mappe</string>
<string name="export_keyfile_prefs">Eksporter nøglefil fra intern mappe</string>
<string name="keyboardswitch_prefs">Tastaturskiftning</string>
<string name="OnlyAvailableForLocalFiles">Kun tilgængelig for lokale filer.</string>
<string name="FileIsInInternalDirectory">Fil lagres i den interne mappe.</string>
@@ -80,7 +82,24 @@
<string name="disclaimer_formal">Keepass2Android leveres ABSOLUT UDEN GARANTI. Det er gratis software, og du er velkommen til at videredistribuere det jf. betingelserne i GPL version 2 eller senere.</string>
<string name="ellipsis">\u2026</string>
<string name="copy_to_clipboard">Kopiér til Upklipsholder</string>
<string name="fingerprint_description">Verificér for at fortsætte</string>
<string name="fingerprint_fatal">Kan ikke opsætte biometrisk oplåsning:</string>
<string name="fingerprint_not_recognized">Biometrisk verifikation fejlede. Prøv igen</string>
<string name="fingerprint_success">Biometrisk verifikation lykkedes</string>
<string name="fingerprint_os_error">Biometrisk oplåsning kræver Android 6.0 eller nyere.</string>
<string name="fingerprint_hardware_error">Ingen biometrisk hardware fundet.</string>
<string name="fingerprint_no_enrolled">Du har ikke konfigureret biometrisk verifikation på denne enhed. Gå til systemindstillinger.</string>
<string name="disable_fingerprint_unlock">Deaktivér biometrisk oplåsning</string>
<string name="enable_fingerprint_unlock">Aktivér fuld biometrisk oplåsning</string>
<string name="enable_fingerprint_quickunlock">Aktivér biometrisk oplåsning for hurtig oplåsning</string>
<string name="fingerprint_unlock_failed">Biometrisk oplåsning mislykkedes. Dekrypteringsnøglen blev ugyldiggjort af Android OS\'et. Dette sker sædvanligvis, hvis en biometrik godkendelse eller sikkerhedsindstillingerne ændres. </string>
<string name="fingerprint_disabled_wrong_masterkey">Databaseoplåsning mislykkedes: Ugyldig kombinøgle. Biometrisk oplåsning blev deaktiveret, da den lagrede hovedadgangskode tilsyneladende ikke længere er gyldig. </string>
<string name="fingerprint_reenable">Genaktivér biometrisk oplåsning for den nye hovedadgangskode.</string>
<string name="fingerprint_reenable2">Oplås med din adgangskode og genaktivér så biometrisk oplåsning i databaseindstillingerne.</string>
<string name="FingerprintInitFailed">Kunne ikke initialisere biometrisk verifikation. </string>
<string name="FingerprintSetupFailed">Mislykkedes at kryptere data. Dette kan ske, hvis du tilføjer eller fjerner fingeraftryk i systemindstillingerne, mens Keepass2Android moniterer for brug af fingeraftryk.</string>
<string name="enable_fingerprint_unlock_Info">Dette gemmer din hovedadgangskode på denne enhed, krypteret med Android Keystore og beskyttet af biometrisk verifikation. Tillader dig at oplåse din database alene via biometri.</string>
<string name="enable_fingerprint_quickunlock_Info">Tillader brug af biometrisk verifikation i stedet for hurtigoplåsningskoden. Gemmer ingen information relateret til din hovedadgangskode.</string>
<string name="enter_filename">Angiv databasefilnavn</string>
<string name="entry_accessed">Tilgået</string>
<string name="entry_cancel">Annullér</string>
@@ -151,7 +170,7 @@
<string name="maskpass_title">Maskér adgangskode</string>
<string name="maskpass_summary">Maskér som standard adgangskoder</string>
<string name="menu_about">Om</string>
<string name="menu_change_key">Skift Hovedadgangskode</string>
<string name="menu_change_key">Skift hovednøgle</string>
<string name="menu_copy_pass">Kopiér adgangskode</string>
<string name="menu_copy_user">Kopiér Bruger</string>
<string name="menu_copy_totp">Kopiér TOTP</string>
@@ -187,7 +206,7 @@
<string name="omitbackup_summary">Udelad \'Sikkerhedskopi\'- og Papirkurv-grupper i søgeresultater</string>
<string name="pass_filename">KeePass-databasefilnavn</string>
<string name="password_title">Angiv databaseadgangskode</string>
<string name="master_key_type">Vælg type af Hovedadgangskode:</string>
<string name="master_key_type">Vælg type af hovednøgle:</string>
<string name="progress_create">Opretter ny database…</string>
<string name="create_database">Opret database</string>
<string name="progress_title">Behandler…</string>
@@ -196,7 +215,7 @@
<string name="remove_from_filelist">Fjern</string>
<string name="edit">Redigér</string>
<string name="rijndael">Rijndael (AES)</string>
<string name="root">Root</string>
<string name="root">Rod</string>
<string name="AutoReturnFromQuery_title">Returnér automatisk fra forespørgselsskærmen</string>
<string name="AutoReturnFromQuery_summary">Ved opslag af en post til en app eller et websted: Returnér automatisk fra forespørgselsskærmen, hvis der kun er én matchende post i databasen.</string>
<string name="KeyDerivFunc">Nøgleafledningsfunktion</string>
@@ -210,6 +229,7 @@
<string name="saving_database">Gemmer database…</string>
<string name="exporting_database">Eksporterer database…</string>
<string name="export_database_successful">Databaseeksport udført!</string>
<string name="export_keyfile_successful">Nøglefil eksporteret!</string>
<string name="space">Mellemrum</string>
<string name="search_label">Søg</string>
<string name="show_password">Vis adgangskode</string>
@@ -219,6 +239,25 @@
<string name="sort_moddate">Sortér efter ændringsdato</string>
<string name="sort_default">Behold standardrækkefølgen</string>
<string name="special">Speciel</string>
<string name="special_extended">Udvidet Speciel</string>
<string name="at_least_one_from_each_group">Mindst én fra hver gruppe</string>
<string name="exclude_lookalike">Udeluk lignende tegn</string>
<string name="password_generation_profile">Profil</string>
<string name="save_password_generation_profile_text">Indtast navnet på den profil, der skal gemmes. Indtast et eksisterende navn for at overskrive.</string>
<string name="hint_wordcount">Adgangssætning antal ord</string>
<string name="hint_wordseparator">Ordadskiller</string>
<string-array name="PasswordGeneratorModes">
<item>Adgangskode</item>
<item>Adgangssætning</item>
<item>Adgangssætning + Adgangskode</item>
</string-array>
<string-array name="PasswordGeneratorCaseModes">
<item>små bogstaver</item>
<item>STORE BOGSTAVER</item>
<item>Første Tegn Store Bogstaver</item>
</string-array>
<string name="custom_settings">Tilpassede indstillinger</string>
<string name="passphrase_capitalization">Adgangssætning bogstavstørrelse</string>
<string name="search_hint">Find hvad</string>
<string name="search_results">Søgeresultater</string>
<string name="search_in">Søg i</string>
@@ -236,6 +275,7 @@
<string name="author">Keepass2Android er udviklet af Philipp Crocoll.</string>
<string name="further_authors">Tak for kodebidrag fra %1$s.</string>
<string name="designers">Tak til ikon- og layoutdesignbidrag fra %1$s.</string>
<string name="supporters">Takket være finansiel støtte fra %1$s.</string>
<string name="credit_plugin1">Twofish Cipher-plugin\'et til KeePass er udviklet af Scott Greenberg og er inkluderet i KP2A.</string>
<string name="credit_android_filechooser">Android-filvælger er udviklet af Hai Bison</string>
<string name="credit_keyboard">KP2A-tastaturet er baseret på Gingerbread-tastaturet fra Android Open Source Project og benytter Plugin Manager-koden fra Hacker\'s Keyboard fra Klaus Weidner.</string>
@@ -248,6 +288,9 @@
<string name="ShowUsernameInList_summary">Vis brugernavne under posttitlerne. Nyttigt ved flere konti eller TAN\'er.</string>
<string name="RememberRecentFiles_title">Husk databaser</string>
<string name="RememberRecentFiles_summary">Husk senest åbnede databaser og vis dem på skærmen Åbn database.</string>
<string name="NoDalVerification_title">Ingen DAL verifikation</string>
<string name="NoDalVerification_summary">Deaktiverer tjek af om domæne- og app-pakke matcher</string>
<string name="kp2a_findUrl">Find adgangskode</string>
<string name="excludeExpiredEntries">Udelad udløbne poster</string>
<string name="search_options">Indstillinger</string>
<string name="caseSensitive">Versal/minuskel sensitiv</string>
@@ -257,7 +300,7 @@
<string name="start_create_import">Importér fil til ny database…</string>
<string name="enter_filename_details_url">Fuld URL skal angives, inkl. protokol (f.eks. http://).</string>
<string name="enter_filename_details_create_import">Fil til import vælges i næste trin.</string>
<string name="enable_quickunlock">Aktivér Hurtigoplåsning</string>
<string name="enable_quickunlock">Aktivér hurtigoplåsning</string>
<string name="QuickUnlock_label">Angiv de sidste %1$d tegn i din adgangskode:</string>
<string name="QuickUnlock_label_secure">Angiv Hurtigoplåsningskoden:</string>
<string name="QuickUnlock_button">Hurtigoplåsning!</string>
@@ -275,8 +318,6 @@
<string name="QuickUnlockHideLength_title">Skjul Hurtigoplåsningskodens længde</string>
<string name="QuickUnlockHideLength_summary">Hvis aktiveret, vises længden af Hurtigoplåsningskoden ikke på Hurtigoplåsningsskærmen.</string>
<string name="QuickUnlock_fail">Hurtigoplåsning mislykkedes: Forkert adgangskode!</string>
<string name="BinaryDirectory_title">Filvedhæftningsmappe</string>
<string name="BinaryDirectory_summary">Mappe, hvori filvedhæftninger gemmes.</string>
<string name="SaveAttachmentDialog_title">Gem vedhæftning</string>
<string name="SaveAttachmentDialog_text">Vælg, hvor vedhæftningen skal gemmes.</string>
<string name="SaveAttachmentDialog_save">Eksportér til fil...</string>
@@ -294,6 +335,13 @@
<string name="protection">Beskyttet felt</string>
<string name="add_binary">Tilføj filvedhæftning</string>
<string name="add_extra_string">Tilføj ekstra streng</string>
<string name="configure_totp">Konfigurer TOTP</string>
<string name="totp_secret_key">Hemmelig nøgle</string>
<string name="totp_encoding_rfc6238">Standard indstillinger for RFC6238 token</string>
<string name="totp_encoding_steam">Indstillinger for Steam token</string>
<string name="totp_encoding_custom">Tilpassede indstillinger for token</string>
<string name="totp_time_step">Tidstrin</string>
<string name="totp_length">Kodelængde</string>
<string name="delete_extra_string">Slet ekstra streng</string>
<string name="database_loaded_quickunlock_enabled">%1$s: Låst. Hurtigoplåsning aktiv.</string>
<string name="database_loaded_unlocked">%1$s: Oplåst.</string>
@@ -383,7 +431,7 @@
<string name="DeletingItems">Sletter elementer…</string>
<string name="SettingPassword">Opsætter adgangskode…</string>
<string name="UndoingChanges">Fortryder ændringer…</string>
<string name="TransformingKey">Transformerer Hovednøgle…</string>
<string name="TransformingKey">Transformerer hovednøgle…</string>
<string name="DecodingDatabase">Afkoder database…</string>
<string name="ParsingDatabase">Fortolker database…</string>
<string name="CheckingTargetFileForChanges">Tjekker målfil for ændringer…</string>
@@ -611,6 +659,45 @@
<string name="DbQuicklockedChannel_desc">Notificering om låsning af databasen med Hurtigoplåsning</string>
<string name="EntryChannel_name">Indtastningsnotifikationer</string>
<string name="EntryChannel_desc">Notificering til forenkelse af adgang til den aktuelt valgte indtastning.</string>
<string name="CloseDbAfterFailedAttempts">Luk database efter tre mislykkede forsøg med biometrisk oplåsning.</string>
<string-array name="ChangeLog_1_09a">
<item>Tilføjet understøttelse af KDBX 4.1 filformat indført i KeePass 2.48</item>
<item>Tilføjet dialog til at konfigurere TOTP indstillinger for poster</item>
<item>Forbedret adgangskodegenerator: Tilføjet adgangssætning-understøttelse, flere muligheder, profiler og estimering af adgangskodestyrke</item>
<item>Forbedringer til Autofyldning (fast popup vises ikke i Chrome, bedre underdomæne understøttelse)</item>
<item>Forbedringer til OneDrive-implementering: ikke længere nogen størrelsesgrænse, ikke flere overflødige anmodninger om autentificering</item>
<item>Tilføjet mulighed for at vælge lyst / mørkt design fra systemindstillinger, herunder nat-planer, kræver Android 10+</item>
<item>Update Dropbox implementation to support new authentication method.</item>
<item>Newly setup fingerprint unlock will be invalidated after adding a fingerprint in system settings for increased security.</item>
<item>Allow to open files through system file picker, ignoring the read-only flag</item>
<item>Allow to start moving entries from the entry view menu</item>
</string-array>
<string-array name="ChangeLog_1_08d">
<item>Tilføjet understøttelse af nyt nøglefilformat indført i KeePass 2.47</item>
<item>Tilføj understøttelse af Argon2id som nøgleafledningsfunktion</item>
<item>Forbedret Autofyld kompatibilitet med Firefox og Chrome</item>
<item>Forbedre understøttelse af TOTP-poster fra skrivebordsprogrammer</item>
<item>Opdater pCloud SDK for at rette godkendelsesproblem</item>
<item>Opdater Jsch til version 0.1.55</item>
<item>Føj menu til skærm for valg af database</item>
<item>Tillad eksport af importerede nøglefiler</item>
</string-array>
<string-array name="ChangeLog_1_08c">
<item>Lagrer ikke længere pakkenavne på Android-apps i URL-feltet</item>
<item>Forbedre låsning adfærd - viser ikke længere biometrisk prompt umiddelbart efter oplåsning</item>
<item>Opdater OkHttp til at understøtte HTTP/2</item>
<item>Retter manglende oversættelser</item>
</string-array>
<string-array name="ChangeLog_1_08b">
<item>Gennemtving HTTP/1.1 grundet problem med HTTP/2 implementering af OkHttp</item>
<item>Forbedre tastaturdialogen på Android 9+</item>
<item>Skift filassociationer for app\'en for at undgå nogle unødvendige associationer</item>
<item>Sørg for at adgangskodeteksten ikke er skjult bag øjenikon</item>
<item>Skift autofyld-adfærd for at advare ved udfyldning af legitimationsoplysninger for et domæne til en ukendt app</item>
<item>Opdatér til FTP-bibliotek</item>
<item>Løser potentielle nedbrud i appen</item>
<item>Yderligere smårettelser</item>
</string-array>
<string-array name="ChangeLog_1_08">
<item>Skift til brug af FluentFTP for understøttelse af TLS 1.2</item>
<item>Skift til BiometricPrompt-API for at forbedre brugeroplevelsen med fingeraftryksoplåsning samt tillade brug af ansigtsoplåsning på f.eks. Pixel 4.</item>
@@ -789,6 +876,7 @@
<string-array name="design_options">
<item>Lys</item>
<item>Mørk</item>
<item>Systemindstillinger</item>
</string-array>
<string name="design_title">Design</string>
<string-array name="ftp_encryption_modes">
@@ -830,14 +918,23 @@
<string name="autofill_disable">Deaktivér Auto-udfyld for %1$s</string>
<string name="autofill_enable_for">Aktivér Auto-udfyld for %1$s</string>
<string name="invalid_link_association">Kunne ikke tilknytte webdomæne %1$s med app\'en %2$s</string>
<string name="enable_fingerprint_hint">Keepass2Android har registreret biometrisk-hardware. Vil du aktivere biometrisk oplåsning for denne database?</string>
<string name="understand">Forstået</string>
<string name="dont_show_again">Vis ikke igen</string>
<string name="masterkey_infotext_head">Kan du huske din hovedadgangskode?</string>
<string name="masterkey_infotext_main">Bemærk, at du ikke vil kunne åbne din database uden hovednøglen. Der er ingen måde, hvorpå hovedadgangskoden kan \"nulstilles\".</string>
<string name="masterkey_infotext_fingerprint_note">Bemærk også, at biometrisk oplåsning fungerer ved at gemme din hovednøgle i Androids sikre lager. Lageret kan til enhver tid slettes af Android f.eks. ved tilføjelse af et nyt fingeraftryk via systemindstillingerne. Sæt derfor ikke kun din lid til biometrisk oplåsning, husk også din hovedadgangskode!</string>
<string name="backup_infotext_head">Er din database sikkerhedskopieret?</string>
<string name="backup_infotext_main">Keepass2Android lagrer dine adgangskoder i en fil på en placering efter eget valg. Er du sikker på, at du stadig kan tilgå denne fil, hvis din mobil går tabt eller stjæles, eller filen ødelægges eller slettes? Sørg altid for at opbevare en opdateret sikkerhedskopi på et sikkert sted!</string>
<string name="backup_infotext_note">For at oprette en sikkerhedskopi nu, så gå til %1$s &gt; %2$s &gt; %3$s.</string>
<string name="emergency_infotext_head">Forberedt på nødsituationer?</string>
<string name="emergency_infotext_main">Har du nogensinde overvejet, hvad der sker, hvis du ikke længere kan tilgå din adgangskodedatabase? Hvis du kommer ud for en ulykke? Det er god praksis at videregive din hovednøgle til en betroet person for nødstilfælde. Ingen vil ellers kunne tilgå dine adgangskoder.</string>
<string name="no_secure_display">Den aktuelt anvendte skærm er ikke markeret som sikker. Dette betyder, at andre apps kan tage skærmfotos. Keepass2Android er konfigureret til kun at vise følsomme oplysninger på sikre skærme. Skift til en sikker skærm (f.eks. ved at frakoble en HDMI-skærm) eller ændr app-indstillingerne.</string>
<string name="disable_secure_screen_check">Deaktiver denne besked</string>
<string name="switch_ime_text">Aktivér venligst Keepass2Android-tastaturet.</string>
<string name="switch_ime_reopen">Forsøg igen</string>
<string name="AutofillWarning_title">Sikkerhedsadvarsel: Ukendt domæne/app-link</string>
<string name="AutofillWarning_Intro">Du er ved at indsætte legitimationsoplysninger for domæne \"%1$s\" i appen \"%2$s\".</string>
<string name="AutofillWarning_FillDomainInUntrustedApp">Hvis du stoler på at \"%2$s\" tilhører \"%1$s\", eller du stoler på at app\'en \"%2$s\" ikke misbruger legitimationsoplysningerne (f.eks. fordi det er en betroet browser app), er det ok at fortsætte. Ellers bedes du annullere.</string>
<string name="AutofillWarning_trustAsBrowser">Accepter altid i \"%1$s\"</string>
</resources>

View File

@@ -62,7 +62,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="unlock_database_title">Datenbank entsperren</string>
<string name="brackets">Klammern</string>
<string name="cancel">Abbrechen</string>
<string name="Ok">Ok</string>
<string name="Ok">OK</string>
<string name="disable_sensor">Sensor deaktivieren</string>
<string name="enable_sensor">Sensor aktivieren</string>
<string name="ClearClipboard">Zwischenablage geleert.</string>
@@ -99,7 +99,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="fingerprint_reenable2">Bitte mit Kennwort entsperren und anschließend in den Datenbankeinstellungen das biometrische Entsperren erneut aktivieren.</string>
<string name="FingerprintInitFailed">Initialisierung der biometrischen Authentifizierung fehlgeschlagen. </string>
<string name="FingerprintSetupFailed">Daten konnten nicht verschlüsselt werden. Dies kann passieren, wenn Fingerabdrücke in den Systemeinstellungen hinzugefügt oder gelöscht wurden, während Keepass2Android auf deinen Fingerabdruck wartet.</string>
<string name="enable_fingerprint_unlock_Info">Mit dieser Option wird das Masterkennwort verschlüsselt im Android-Keystore auf dem Gerät gespeichert, geschützt durch einen Fingerabdruck. Dadurch kann die Datenbank per biometrischer Authentifizierung entsperrt werden.</string>
<string name="enable_fingerprint_unlock_Info">Mit dieser Option wird das Master-Passwort verschlüsselt im Android-Keystore auf dem Gerät gespeichert, geschützt durch einen Fingerabdruck. Dadurch kann die Datenbank per biometrischer Authentifizierung entsperrt werden.</string>
<string name="enable_fingerprint_quickunlock_Info">Erlaubt es, den biometrische Authentifizierung anstelle des QuickUnlock-Codes zu nutzen. Speichert keine Informationen bezüglich des Masterpassworts.</string>
<string name="enter_filename">Dateinamen der Datenbank eingeben</string>
<string name="entry_accessed">Letzter Zugriff</string>
@@ -114,9 +114,9 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="entry_keyfile">Schlüsseldatei (optional)</string>
<string name="keyfile_heading">Schlüsseldatei</string>
<string name="entry_modified">Letzte Änderung</string>
<string name="entry_password">Kennwort</string>
<string name="entry_password">Passwort</string>
<string name="entry_save">Speichern</string>
<string name="entry_title">Titel</string>
<string name="entry_title">Name</string>
<string name="entry_url">URL</string>
<string name="entry_user_name">Benutzername</string>
<string name="entry_extra_strings">Zusätzliche Felder</string>
@@ -150,7 +150,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="hint_keyfile">Schlüsseldatei</string>
<string name="hint_length">Länge</string>
<string name="hint_pass">Passwort</string>
<string name="hint_login_pass">Kennwort</string>
<string name="hint_login_pass">Passwort</string>
<string name="hint_title">Name</string>
<string name="hint_url">URL</string>
<string name="hint_override_url">Override URL</string>
@@ -171,7 +171,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="maskpass_title">Kennwort ausblenden</string>
<string name="maskpass_summary">Kennwörter standardmäßig ausgeblendet</string>
<string name="menu_about">Über</string>
<string name="menu_change_key">Datenbank-Kennwort ändern</string>
<string name="menu_change_key">Master-Passwort ändern</string>
<string name="menu_copy_pass">Kennwort kopieren</string>
<string name="menu_copy_user">Benutzername kopieren</string>
<string name="menu_copy_totp">TOTP kopieren</string>
@@ -211,12 +211,12 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="progress_create">Neue Datenbank anlegen\u2026</string>
<string name="create_database">Datenbank anlegen</string>
<string name="progress_title">In Bearbeitung\u2026</string>
<string name="remember_keyfile_summary">Schlüsseldatei anlegen und ihr Speicherort merken</string>
<string name="remember_keyfile_summary">Speicherort der Schlüsseldateien merken</string>
<string name="remember_keyfile_title">Schlüsseldatei speichern</string>
<string name="remove_from_filelist">Entfernen</string>
<string name="edit">Bearbeiten</string>
<string name="rijndael">Rijndael (AES)</string>
<string name="root">Start</string>
<string name="root">Root</string>
<string name="AutoReturnFromQuery_title">Kehre automatisch vom Abfragebildschirm zurück</string>
<string name="AutoReturnFromQuery_summary">Beim Suchen eines Eintrags für eine App oder Webseite: Kehre automatisch vom Abfragebildschirm zurück, wenn nur ein passender Eintrag in der Datenbank existiert.</string>
<string name="KeyDerivFunc">Schlüsselableitungsfunktion</string>
@@ -240,6 +240,25 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="sort_moddate">Sortiere nach Änderungsdatum</string>
<string name="sort_default">Behalte Standardsortierung</string>
<string name="special">Spezialsymbole</string>
<string name="special_extended">Erweiterte Sonderzeichen</string>
<string name="at_least_one_from_each_group">Mindestens ein Zeichen aus jeder Gruppe</string>
<string name="exclude_lookalike">Ähnlich aussehende Zeichen ausschließen</string>
<string name="password_generation_profile">Profil</string>
<string name="save_password_generation_profile_text">Gib den Namen des zu speichernden Profils ein. Verwende einen bestehenden Namen, um ein Profil zu überscheiben.</string>
<string name="hint_wordcount">Anzahl Wörter der Passphrase </string>
<string name="hint_wordseparator">Wort-Trenner</string>
<string-array name="PasswordGeneratorModes">
<item>Passwort</item>
<item>Passphrase</item>
<item>Passphrase + Passwort</item>
</string-array>
<string-array name="PasswordGeneratorCaseModes">
<item>kleingeschrieben</item>
<item>GROSSGESCHRIEBEN</item>
<item>Erster Buchstabe Großgeschrieben</item>
</string-array>
<string name="custom_settings">Benutzerdefiniert</string>
<string name="passphrase_capitalization">Passphrase Großschreibung</string>
<string name="search_hint">Suchbegriff(e)</string>
<string name="search_results">Suchergebnisse</string>
<string name="search_in">Suche in</string>
@@ -250,7 +269,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="underline">Unterstrich</string>
<string name="unsupported_db_version">Datenbankversion wird nicht unterstützt.</string>
<string name="uppercase">Großbuchstaben</string>
<string name="warning_read_only">Der Speicherort hat nur Lese-Rechte. Zum Speichern ist er nicht geeignet, darum ist es unmöglich letzte Änderungen zu sichern.</string>
<string name="warning_read_only">Der Speicherort hat nur Lese-Rechte. Zum Speichern ist er nicht geeignet, darum können letzte Änderungen nicht gespeichert werden.</string>
<string name="warning_unmounted">Keine SD-Karte vorhanden oder derzeit nicht im Gerät eingebunden. Daher kann weder eine Datenbank geöffnet noch erstellt werden.</string>
<string name="version_label">Version</string>
<string name="version_history">Versionsinformationen</string>
@@ -300,8 +319,6 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="QuickUnlockHideLength_title">Länge des QuickUnlock-Codes verstecken</string>
<string name="QuickUnlockHideLength_summary">Wenn aktiviert, wird die Länge des QuickUnlock-Codes nicht auf dem QuickUnlock-Bildschirm angezeigt.</string>
<string name="QuickUnlock_fail">QuickUnlock fehlgeschlagen: falsches Kennwort!</string>
<string name="BinaryDirectory_title">Verzeichnis für Datei-Anhänge</string>
<string name="BinaryDirectory_summary">Verzeichnis, in dem Datei-Anhänge gespeichert werden.</string>
<string name="SaveAttachmentDialog_title">Anhang speichern</string>
<string name="SaveAttachmentDialog_text">Bitte wähle den Ort zum Speichern.</string>
<string name="SaveAttachmentDialog_save">In Datei exportieren...</string>
@@ -319,6 +336,13 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="protection">Geschütztes Feld</string>
<string name="add_binary">Dateianhang hinzufügen...</string>
<string name="add_extra_string">Zusätzliches Feld hinzufügen</string>
<string name="configure_totp">TOTP konfigurieren</string>
<string name="totp_secret_key">Geheimer Schlüssel</string>
<string name="totp_encoding_rfc6238">RFC-6238-Standardeinstellungen</string>
<string name="totp_encoding_steam">Steam-Token-Einstellungen</string>
<string name="totp_encoding_custom">Benutzerdefinierte Einstellungen</string>
<string name="totp_time_step">Zeitschritt</string>
<string name="totp_length">Codelänge</string>
<string name="delete_extra_string">Zusätzliches Feld löschen</string>
<string name="database_loaded_quickunlock_enabled">%1$s: gesperrt. QuickUnlock aktiviert.</string>
<string name="database_loaded_unlocked">%1$s: Entsperrt.</string>
@@ -331,7 +355,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="ClearPasswordOnLeave_summary">Leere das Eingabefeld für das Master-Passwort, wenn der Passwort-Eingabebildschirm verlassen wird, ohne dass die Datenbank entsperrt wurde.</string>
<string name="LockWhenNavigateBack_title">Beim Verlassen der App sperren</string>
<string name="LockWhenNavigateBack_summary">Datenbank sperren, wenn die App mit dem Zurück-Knopf verlassen wird.</string>
<string name="UseKp2aKeyboardInKp2a_title">Benutze Built-In-Tastatur in Keepass2Android</string>
<string name="UseKp2aKeyboardInKp2a_title">Benutze integrierte Tastatur in Keepass2Android</string>
<string name="UseKp2aKeyboardInKp2a_summary">Wenn Sie ihrer Standardtastatur nicht vertrauen, setzen Sie diese Option, um die Built-In-Tastatur beim Eingeben des Masterkennworts oder beim Bearbeiten von Einträgen zu benutzen.</string>
<string name="ActivateSearchViewOnStart_title">Suchfeld beim Start aktivieren</string>
<string name="ActivateSearchViewOnStart_summary">Aktiviert das Suchfeld in der Gruppenansicht nach dem Entsperren oder wenn ein Eintrag gesucht wird.</string>
@@ -344,7 +368,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="UseOfflineCache_summary">Bewahrt eine Kopie der Datenbankdateien im Cache-Ordner der App auf. Dies ermöglicht die Verwendung von Datenbanken, auch wenn die Datenbankdatei nicht zugänglich ist.</string>
<string name="CreateBackups_title">Lokale Backups</string>
<string name="CreateBackups_summary">Erstelle eine lokale Sicherungskopie der Datenbank, nachdem diese erfolgreich geladen wurde.</string>
<string name="UpdatingBackup">Aktualisiere lokale Sicherungskopie...</string>
<string name="UpdatingBackup">Aktualisiere lokale Sicherungskopie\u2026</string>
<string name="LocalBackupOf">Lokale Sicherung von %1$s</string>
<string name="show_local_backups">Lokale Backups anzeigen</string>
<string name="AcceptAllServerCertificates_title">SSL-Zertifikate</string>
@@ -364,7 +388,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="ShowKp2aKeyboardNotification_title">KP2A-Tastatur Benachrichtigung</string>
<string name="ShowKp2aKeyboardNotification_summary">Kompletten Eintrag über die KP2A-Tastatur bereitstellen (empfohlen).</string>
<string name="OpenKp2aKeyboardAutomatically_title">Tastatur umschalten</string>
<string name="OpenKp2aKeyboardAutomatically_summary">Dialog zum Auswählen der Eingabemethode öffnen wenn ein Eintrag nach Suche aus dem Browser heraus verfügbar ist.</string>
<string name="OpenKp2aKeyboardAutomatically_summary">Dialog zum Auswählen der Eingabemethode öffnen, wenn ein Eintrag nach Suche aus dem Browser heraus verfügbar ist.</string>
<string name="kp2a_switch_rooted">Auto-Umschalten der Tastatur</string>
<string name="kp2a_switch_rooted_summary">Wechselt automatisch zur KP2A-Tastatur, wenn ein Eintrag geöffnet wird. Benötigt korrekt konfiguriertes KeyboardSwap-Plugin oder ein gerootetes Gerät und die SecureSettings-App mit System+. </string>
<string name="get_keyboardswap">KeyboardSwap-Plugin installieren</string>
@@ -380,7 +404,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="DontCare">Ist mir egal</string>
<string name="DocumentAccessRevoked">Keepass2Android kann nicht mehr auf die Datei zugreifen. Entweder wurde sie entfernt oder die Zugriffsrechte wurden entzogen. Bitte öffne sie erneut, z.B. durch Klick auf \"Datenbank wechseln\".</string>
<string name="PreloadDatabaseEnabled_title">Datenbank vor-laden</string>
<string name="PreloadDatabaseEnabled_summary">Starte das Laden oder Herunterladen von Datenbank-Dateien im Hintergrund während dem Eingeben des Passworts.</string>
<string name="PreloadDatabaseEnabled_summary">Starte das Laden oder Herunterladen von Datenbank-Dateien im Hintergrund während der Passwort-Eingabe.</string>
<string name="AskOverwriteBinary">Möchtest du den vorhandenen Anhang mit dem gleichen Namen überschreiben?</string>
<string name="AskOverwriteBinary_title">Vorhandenen Anhang überschreiben?</string>
<string name="AskOverwriteBinary_yes">Überschreiben</string>
@@ -420,20 +444,20 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="UseOfflineMode">Nur internen Zwischenspeicher verwenden</string>
<string name="UseOnlineMode">Zwischengespeicherte Kopie mit der Quelldatei synchronisieren</string>
<string name="UseOfflineMode_Info">Datenbank wird vom internen Cache geladen. Änderungen werden nur im internen Cache gespeichert und nur synchronisiert, wenn eine Datenbanksynchronisierung manuell ausgewählt wird.</string>
<string name="InOfflineMode">Nur internen Zwischenspeicher verwenden.</string>
<string name="InOfflineMode">Verwende nur internen Zwischenspeicher.</string>
<string name="SynchronizingCachedDatabase">Synchronisiere gecachte Datenbank…</string>
<string name="DownloadingRemoteFile">Quelldatei wird geladen </string>
<string name="UploadingFile">Datei speichern </string>
<string name="RestoringRemoteFile">Quelldatei wird wiederhergestellt </string>
<string name="DownloadingRemoteFile">Quelldatei wird geladen…</string>
<string name="UploadingFile">Datei speichern…</string>
<string name="RestoringRemoteFile">Quelldatei wird wiederhergestellt…</string>
<string name="FilesInSync">Remote- und Cache-Dateien sind identisch.</string>
<string name="SynchronizedDatabaseSuccessfully">Datenbank erfolgreich synchronisiert!</string>
<string name="CheckingDatabaseForChanges">Prüfe auf Änderungen der Datenbank…</string>
<string name="CouldNotSaveToRemote">Quelldatei konnte nicht gespeichert werden: %1$s. Erneut speichern oder verwenden Sie das Menü „Synchronisieren”, sobald die Datei wieder verfügbar ist.</string>
<string name="CouldNotLoadFromRemote">Auf die Quelldatei konnte nicht zugegriffen werden: %1$s. Datei wurde aus dem internen Zwischenspeicher geladen. Sie können weiterhin Änderungen in der Datenbank vornehmen und diese später synchronisieren.</string>
<string name="UpdatedRemoteFileOnLoad">Aktualisierte Quelldatei.</string>
<string name="NotifyOpenFromLocalDueToConflict">Intern geöffnete Datei aus dem Zwischenspeicher aufgrund von Konflikten mit Änderungen in der Quelldatei. Verwenden Sie das Menü „Synchronisieren” zum Zusammenführen.</string>
<string name="CouldNotSaveToRemote">Quelldatei konnte nicht gespeichert werden: %1$s. Erneut speichern oder verwende das Menü „Synchronisieren”, sobald die Datei wieder verfügbar ist.</string>
<string name="CouldNotLoadFromRemote">Auf die Quelldatei konnte nicht zugegriffen werden: %1$s. Datei wurde aus dem internen Zwischenspeicher geladen. Du kannst weiterhin Änderungen in der Datenbank vornehmen und diese später synchronisieren.</string>
<string name="UpdatedRemoteFileOnLoad">Quelldatei aktualisiert.</string>
<string name="NotifyOpenFromLocalDueToConflict">Interne Datei aus dem Zwischenspeicher geöffnet wegen Konflikten mit Änderungen in der Quelldatei. Verwende das Menü „Synchronisieren” zum Zusammenführen.</string>
<string name="LoadedFromRemoteInSync">Quelldatei und Zwischenspeicher sind auf dem gleichen Stand.</string>
<string name="UpdatedCachedFileOnLoad">Die intern zwischengespeicherte Kopie von %1$s wurde aktualisiert.</string>
<string name="UpdatedCachedFileOnLoad">Die intern zwischengespeicherte Kopie der %1$s wurde aktualisiert.</string>
<string name="RemoteDatabaseUnchanged">Keine Änderungen gefunden.</string>
<string name="ResolvedCacheConflictByUsingRemoteOtpAux">Die zwischengespeicherte OTP-Hilfsdatei wurde aktualisiert: Der Quellenzähler war höher.</string>
<string name="ResolvedCacheConflictByUsingLocalOtpAux">Die zwischengespeicherte OTP-Hilfsdatei wurde aktualisiert: Der lokale Zähler war höher.</string>
@@ -441,15 +465,15 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="database_file">Datenbankdatei</string>
<string name="otp_aux_file">OTP-Hilfsdatei</string>
<string name="ErrorOcurred">Ein Fehler ist aufgetreten:</string>
<string name="DuplicateUuidsError">Datenbank ist korrupt: IDs kommen mehrfach vor. (Hast du mit Minikeepass gespeichert?) Bitte am PC mit Keepass 2 in eine neue Datenbank re-importieren und \'Neue IDs erstellen\' auswählen.</string>
<string name="DuplicateUuidsError">Datenbank ist beschädigt: IDs kommen mehrfach vor. (Hast du mit Minikeepass gespeichert?) Bitte am PC mit Keepass 2 in eine neue Datenbank re-importieren und \'Neue IDs erstellen\' auswählen.</string>
<string name="DuplicateUuidsErrorAdditional">Diese Fehlermeldung kann unter Einstellungen/Anwendungs-Einstellungen/Umgang mit Dateien/Prüfe auf doppelte UUIDs deaktiviert werden. Bitte beachten: Dies kann zu unerwartetem Verhalten führen. Es wird empfohlen, die Datenbank zu reparieren.</string>
<string name="synchronize_database_menu">Datenbank synchronisieren…</string>
<string name="CannotMoveGroupHere">Kann Gruppe nicht hierher verschieben.</string>
<string name="donate_question">Heute ist Oktoberfest! Wenn dir Keepass2Android gefällt: Wäre heute nicht ein guter Tag, um mir ein Bier zu spendieren?</string>
<string name="donate_bday_question">10. Mai? Heute ist mein Geburtstag! Wenn du diese App magst, warum schickst du mir nicht ein paar Geburtstagsgrüße zusammen mit einem kleinen Geburtstagsgeschenk? Das würde mich wirklich freuen :-)</string>
<string name="donate_bday_question">10. Mai? Heute ist mein Geburtstag! Wenn du diese App magst, warum schickst du mir nicht ein paar Geburtstagsgrüße zusammen mit einem kleinen Geburtstagsgeschenk? Das würde mich wirklich freuen! :-)</string>
<string name="donate_missedbday_question">Oh, du hast meinen Geburtstag am 10. Mai verpasst! Wenn du diese App magst, warum schickst du mir nicht ein paar Geburtstagsgrüße zusammen mit einem kleinen Geburtstagsgeschenk? Es ist noch nicht zu spät um mir eine Freude zu machen! :-)</string>
<string name="ok_donate">Warum nicht?</string>
<string name="no_thanks">So gut gefällt mir\'s dann auch nicht</string>
<string name="no_thanks">So gut gefällt\'s mir dann auch nicht</string>
<string name="enter_http_login_title">WebDav-Zugangsdaten eingeben:</string>
<string name="hint_http_url">URL des Ordners oder der Datei (z.B. mycloud.me.com/webdav/)</string>
<string name="enter_owncloud_login_title">OwnCloud-Zugangsdaten eingeben:</string>
@@ -482,7 +506,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="filestoragename_onedrive2">OneDrive</string>
<string name="filestoragename_onedrive2_full">Alle Dateien und freigegebene Dateien</string>
<string name="filestoragename_onedrive2_myfiles">Meine Dateien</string>
<string name="filestoragename_onedrive2_appfolder">Ordner der Keepass2Android App</string>
<string name="filestoragename_onedrive2_appfolder">Ordner der Keepass2Android-App</string>
<string name="filestoragename_sftp">SFTP (SSH File Transfer)</string>
<string name="filestoragename_content">Android-Dateibrowser</string>
<string name="filestorage_setup_title">Dateizugriff initialisieren</string>
@@ -490,20 +514,20 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="help_database_location">Du kannst deine Datenbank lokal auf deinem Android-Gerät oder in der Cloud speichern (nur in der Nicht-Offline-Version). Keepass2Android macht die Datenbank dann auch verfügbar, wenn du offline bist. Da die Datenbank sicher mit der AES 256-Bit-Verschlüsselung geschützt ist, erhält auch dann niemand außer dir Zugriff auf deine Datenbank. Wir empfehlen die Benutzung von Dropbox: Es ist auf allen Geräten verfügbar und bietet automatisch Backups aller Dateiversionen.</string>
<string name="hint_database_location">Wähle, wo du deine Datenbank speichern möchtest:</string>
<string name="button_change_location">Speicherort ändern</string>
<string name="help_quickunlock">Wenn aktiviert läuft Keepass2Android im Hintergrund weiter, auch wenn die Datenbank gesperrt ist. Das ermöglicht das Öffnen der Datenbank mit QuickUnlock.</string>
<string name="help_quickunlock">Wenn aktiviert, läuft Keepass2Android im Hintergrund weiter, auch wenn die Datenbank gesperrt ist. Das ermöglicht das Öffnen der Datenbank mit QuickUnlock.</string>
<string name="master_password">Master-Passwort</string>
<string name="help_master_password">Deine Datenbank wird mit dem hier eingegebenen Passwort verschlüsselt. Wähle ein starkes Passwort, um deine Datenbank zu schützen! Tipp: Denke dir ein oder zwei Sätze aus und nutze die Anfangsbuchstaben als Passwort. Übernimm auch die Satzzeichen.</string>
<string name="hint_master_password">Wähle ein Master-Passwort, mit dem deine Datenbank geschützt wird:</string>
<string name="key_file">Schlüsseldatei</string>
<string name="help_key_file">Eine Schlüsseldatei ist im Grunde ein Kennwort, das in einer Datei gespeichert ist. Schlüsseldateien sind üblicherweise stärker als Kennwörter, weil sie deutlich komplexer sein können; allerdings ist es schwerer, sie geheim zu halten. Wenn du deine Datenbank in der Cloud speicherst, solltest du die Schlüsseldatei keinesfalls auch dort ablegen. Das würde sie nutzlos machen! Wichtig: Ändere die Schlüsseldatei nicht mehr, nachdem du die Datenbank angelegt hast!</string>
<string name="hint_key_file">Wähle, ob du eine Schlüsseldatei zusätzlich zum Hauptpasswort nutzen möchtest:</string>
<string name="hint_key_file">Wähle, ob du eine Schlüsseldatei zusätzlich zum Master-Passwort nutzen möchtest:</string>
<string name="use_key_file">Schlüsseldatei benutzen</string>
<string name="error_adding_keyfile">Fehler beim Hinzufügen der Schlüsseldatei!</string>
<string name="init_otp">OTP-Hilfsdatei laden…</string>
<string name="otp_explanation">Gib die nächsten Einmalkennwörter (OTPs - One-Time-Passwords) an. Bewege deinen Yubikey NEO an der Rückseite deines Gerätes, für die Eingabe per NFC (erfordert Yubiclip app).</string>
<string name="otp_explanation">Gib die nächsten Einmalkennwörter (OTPs - One-Time-Passwords) an. Bewege deinen Yubikey NEO an der Rückseite deines Gerätes, für die Eingabe per NFC (erfordert Yubiclip-App).</string>
<string name="otp_hint">OTP %1$d</string>
<string name="CouldntLoadOtpAuxFile">Konnte OTP-Hilfsdatei nicht laden!</string>
<string name="CouldntLoadOtpAuxFile_Hint">Bitte nutze das OtpKeyProv-Plugin in Keepass 2.x a PC um deine Datenbank zur Verwendung von One-Time-Passwords einzureichten!</string>
<string name="CouldntLoadOtpAuxFile_Hint">Bitte nutze das OtpKeyProv-Plugin in Keepass 2.x (PC) um deine Datenbank zur Verwendung von One-Time-Passwords einzurichten!</string>
<string name="otp_discarded_because_no_db">Bitte erst Datenbank laden. OTP wird aus Sicherheitsgründen verworfen.</string>
<string name="otp_discarded_no_space">OTP verworfen. Alle OTPs bereits eingegeben!</string>
<string name="otp_discarded_because_db_open">Bitte zuerst die aktuelle Datenbank schließen. OTP wird verworfen.</string>
@@ -513,7 +537,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="OtpKeyError">Fehler beim Erstellen des OTP-Schlüssels! Bitte stelle sicher, dass du die richtigen OTPs eingegeben hast.</string>
<string name="ErrorUpdatingOtpAuxFile">Fehler beim Aktualisieren der OTP-Hilfsdatei!</string>
<string name="SavingOtpAuxFile">Speichere OTP-Hilfsdatei…</string>
<string name="NoChallengeApp">Es konnte keine App gefunden werden, die diese Anforderung erfüllt.</string>
<string name="NoChallengeApp">Es konnte keine App gefunden werden, die diese Anforderung verarbeiten kann.</string>
<string name="PleaseInstallApp">Bitte installiere %1$s von Google Play.</string>
<string name="AppOutdated">%1$s wird nicht mehr unterstützt.</string>
<string name="bad_resp">Die Challenge-Antwort ist falsch.</string>
@@ -528,7 +552,7 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="DebugLog_prefs_prefs">Log-Datei (für Debugging)</string>
<string name="DebugLog_title">Log-Datei benutzen</string>
<string name="DebugLog_summary">Ausgabe der Anwendung in eine lokale Log-Datei schreiben</string>
<string name="DebugLog_send">Debug-Log senden...</string>
<string name="DebugLog_send">Debug-Log senden\u2026</string>
<string name="loading">Lade…</string>
<string name="plugins">Plug-ins</string>
<string name="plugin_packagename">Paketname:</string>
@@ -568,12 +592,12 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="ReadOnlyReason_ReadOnlyKitKat">Das Speichern ist aufgrund von Einschränkungen, die in Android KitKat eingeführt wurden, nicht möglich. Wenn du Änderungen in der Datenbank vornehmen möchtest, schließe die Datenbank und wähle Datenbank wechseln. Öffne die Datei dann über die System-Dateiauswahl.</string>
<string name="ReadOnlyReason_LocalBackup">Lokale Sicherungskopien können nicht bearbeitet werden. Du kannst über \"Datenbank-Einstellungen\" - \"Datenbank exportieren\" dieses Backup an einen anderen Ort exportieren und dann von dort öffnen. Dann kannst du auch wieder Änderungen vornehmen.</string>
<string name="AddCustomIcon">Icon aus Datei hinzufügen...</string>
<string name="CopyingFile">Kopiere Datei...</string>
<string name="CopyingFile">Kopiere Datei\u2026</string>
<string name="DuplicateTitle">Kopie</string>
<string name="DefaultTemplate">Standardeintrag</string>
<string name="TemplateGroupName">Vorlagen</string>
<string name="TemplateTitle_IdCard">Ausweis</string>
<string name="TemplateField_IdCard_Name">Titel</string>
<string name="TemplateField_IdCard_Name">Name</string>
<string name="TemplateField_IdCard_PlaceOfIssue">Ausstellungsort</string>
<string name="TemplateField_IdCard_IssueDate">Ausstellungsdatum</string>
<string name="TemplateTitle_EMail">E-Mail</string>
@@ -604,37 +628,50 @@ Der Android Robot wird genutzt und wurde modifiziert basierend auf Arbeiten, die
<string name="HintLocalBackupInvalidCompositeKey"> \n
&#8226; Hinweis: Wenn du denkst, dass deine Datenbank-Datei beschädigt sein könnte oder du das Passwort nicht mehr weißt, nachdem du es geändert hast, kannst du zur zuletzt erfolgreich geöffneten Datei wechseln. Klicke dazu auf \"%1$s\" und wähle das lokale Backup.</string>
<string name="HintLocalBackupOtherError">\n
&#8226; Hinweis: Keepass2Android hat die zuletzt erfolgreich geöffnete Dateiversion im internen Speicher gesichert. Du kannst diese öffnen, indem du \"%1$s\" klickst und das lokale Backup wählst. </string>
&#8226; Hinweis: Keepass2Android hat die zuletzt erfolgreich geöffnete Dateiversion im internen Speicher gesichert. Du kannst diese öffnen, indem du \"%1$s\" klickst und das lokale Backup wählst. </string>
<string name="CorruptDatabaseHelp">Die Datei ist beschädigt.\n
Anbei einige Hinweise, die bei der Diagnose des Problems helfen können:\n
• Wenn du die Datei über USB kopiert hast (MTP-Modus), versuche es noch einmal mit Tools wie MyPhoneExplorer. MTP schneidet die Dateien in manchen Fällen ab.\n
• Wenn du die Datei nicht von der gleichen Stelle auf deinem PC öffnen kannst, ist es sehr wahrscheinlich, dass die Datei tatsächlich beschädigt ist. Bitte verwende in diesem Fall eine Datenbanksicherung. Wenn du annimmst, dass Keepass2Android die Datei beschädigt hat, nimm bitte Kontakt mit dem Support auf.\n
• Wenn du die Datei auf dem PC noch öffnen kannst, nimm Sie bitte Kontakt mit dem Suppot auf. Du kannst versuchen, die Datei mit anderen Einstellungen auf dem PC zu speichern (z.B. nicht ZIP-komprimiert) und sie dann noch einmal in Keepass2Android zu öffnen. </string>
• Wenn du die Datei auf dem PC noch öffnen kannst, nimm bitte Kontakt mit dem Support auf. Du kannst versuchen, die Datei mit anderen Einstellungen auf dem PC zu speichern (z.B. nicht ZIP-komprimiert) und sie dann noch einmal in Keepass2Android zu öffnen. </string>
<string name="open_other_db">Öffne weitere Datenbank…</string>
<string name="select_database">Datenbank auswählen</string>
<string name="configure_child_dbs">Kinddatenbanken konfigurieren…</string>
<string name="configure_child_dbs">Konfiguriere Kinddatenbanken…</string>
<string name="child_dbs_title">Kinddatenbanken</string>
<string name="unspecified">nicht spezifiziert</string>
<string name="child_db_explanation">Kinddatenbanken sind andere Datenbanken, die automatisch geöffnet werden können, wenn die übergeordnete Datenbank geöffnet wird. Dazu werden das Masterkennwort und der Dateipfad der Kinddatenbank in der Hauptdatenbank gespeichert. Diese Funktion erlaubt es, einige deiner Kennwörter mit einer anderen Person zu teilen. Die Implementierung ist kompatibel mit KeeAutoExec für PC.</string>
<string name="child_db_explanation">Kinddatenbanken sind andere Datenbanken, die automatisch geöffnet werden können, wenn die übergeordnete Datenbank geöffnet wird. Dazu werden das Master-Passwort und der Dateipfad der Kinddatenbank in der Hauptdatenbank gespeichert. Diese Funktion erlaubt es, einige deiner Passwörter mit einer anderen Person zu teilen. Die Implementierung ist kompatibel mit KeeAutoExec für PC.</string>
<string name="child_db_enabled_on_this_device">Auf diesem Gerät aktiviert</string>
<string name="child_db_enable_on_this_device">Auf diesem Gerät aktivieren</string>
<string name="child_db_disable_on_this_device">Auf diesem Gerät deaktivieren</string>
<string name="child_db_enable_a_copy_for_this_device">Kopie für dieses Gerät</string>
<string name="unconfigured_child_dbs">Ihre Datenbank enthält neue Unter-Datenbanken in der Gruppe \"AutoOpen\". Bitte geben Sie an, wenn diese Unter-Datenbanken auf diesem Gerät verwendet werden soll.</string>
<string name="add_child_db">Füge Kinddatenbank hinzu...</string>
<string name="EnableCopyForThisDevice_Info">Hiermit wird eine Kopie der Kinddatenbankeinstellungen erstellt und aktiviert. Diese kopierten Einstellungen können dann speziell für dieses Gerät verändert werden.</string>
<string name="unconfigured_child_dbs">Deine Datenbank enthält neue Kinddatenbanken in der Gruppe \"AutoOpen\". Bitte gib an, wenn diese Kinddatenbanken auf diesem Gerät verwendet werden sollen.</string>
<string name="add_child_db">Füge Kinddatenbank hinzu\u2026</string>
<string name="EnableCopyForThisDevice_Info">Hiermit wird eine Kopie der Kinddatenbank-Einstellungen erstellt und aktiviert. Diese kopierten Einstellungen können dann speziell für dieses Gerät verändert werden.</string>
<string name="Visible_title">Sichtbar</string>
<string name="child_db_Enabled_title">Automatisch öffnen</string>
<string name="database_file_heading">Datenbankdatei</string>
<string name="if_device_text">Aktiviere für %1$s</string>
<string name="DbUnlockedChannel_name">Datenbank entsperrt</string>
<string name="DbUnlockedChannel_desc">Benachritigung wenn die Datenbank entsperrt ist</string>
<string name="DbUnlockedChannel_desc">Benachrichtigung wenn die Datenbank entsperrt ist</string>
<string name="DbQuicklockedChannel_name">QuickUnlock</string>
<string name="DbQuicklockedChannel_desc">Benachrichtigung wenn die Datenbank mit QuickUnlock gesperrt ist</string>
<string name="EntryChannel_name">Eintrag-Benachrichtigungen</string>
<string name="EntryChannel_desc">Benachrichtigung zum schnellen Zugriff auf den aktuell gewählten Eintrag.</string>
<string name="CloseDbAfterFailedAttempts">Datenbank nach drei fehlgeschlagenen biometrischen Entsperrversuchen schließen.</string>
<string name="WarnFingerprintInvalidated">Warnung! Biometrische Authentifizierung kann von Android ungültig gemacht werden, z.B. nach dem Hinzufügen eines neuen Fingerabdrucks in den Geräteeinstellungen. Stelle sicher, dass du immer weißt, wie du mit deinem Master-Passwort entsperren kannst!</string>
<string-array name="ChangeLog_1_09a">
<item>Unterstützung für das KDBX-4.1-Dateiformat (in KeePass 2.48 eingeführt) implementiert</item>
<item>Dialog zum Konfigurieren der TOTP-Einstellungen von Einträgen hinzugefügt</item>
<item>Passwort-Generator verbessert: Unterstützung für Passphrases, mehr Einstellmöglichkeiten, Profile und Schätzung der Passwortstärke hinzugefügt</item>
<item>Verbesserungen bei Autofill: Bugfix bzgl. nicht angezeigtem Popup in Chrome, bessere Unterstützung für Subdomains</item>
<item>Verbesserungen bei OneDrive: keine Größenbegrenzung mehr, keine unnötigen Authentifizierungsanfragen</item>
<item>Option hinzugefügt, um das helle/dunkel Design anhand der Systemeinstellungen zu wählen, erfordert Android 10+</item>
<item>Dropbox-Implementierung aktualisiert um neue Authentifizierungsmethode zu unterstützen.</item>
<item>Neu eingerichtetes Entsperren mit Fingerabdruck wird ungültig, wenn ein Fingerabdruck in den Systemeinstellungen hinzugefügt wird (um die Sicherheit zu erhöhen).</item>
<item>Öffnen von Dateien über den Systemdialog ermöglicht, das Nur-Lesen-Attribut wird ignoriert.</item>
<item>Das Verschieben von Einträgen kann nun aus der Eintragsansicht begonnen werden.</item>
</string-array>
<string-array name="ChangeLog_1_08d">
<item>Unterstützung für neues Schlüsseldateiformat aus KeePass 2.47 integriert</item>
<item>Unterstützung für Argon2id als Schlüsselableitungsfunktion integriert</item>
@@ -815,7 +852,7 @@ Anbei einige Hinweise, die bei der Diagnose des Problems helfen können:\n
* Problem mit Google Drive behoben \n
* Schlüsseldateien können auf beliebigen Speicherorten abgelegt werden\n
* Dropbox-SDK aktualisiert, um offiziellen Sicherheitspatch zu integrieren\n
* Build-Tools aktualisiert -> APK-Größe leider gewachsen, sorry :-(\n
* Build-Tools aktualisiert -&gt; APK-Größe leider gewachsen, sorry :-(\n
Ich hatte noch weitere Änderungen versprochen. Diese kommen mit dem nächsten Release, dieses muss wegen aktueller Probleme so früh wie möglich veröffentlicht werden.</string>
<string name="ChangeLog_0_9_4"><b>Version 0.9.4</b> \n
* Plugin-Unterstützung hinzugefügt: siehe Einstellungen, um Plugins zu bekommen! \n
@@ -943,6 +980,7 @@ Erstes öffentliches Release</string>
<string-array name="design_options">
<item>Hell</item>
<item>Dunkel</item>
<item>Systemeinstellungen</item>
</string-array>
<string name="design_title">Design</string>
<string-array name="ftp_encryption_modes">
@@ -966,7 +1004,7 @@ Erstes öffentliches Release</string>
<item>Kennwort + Schlüsseldatei + Challenge-Response für Keepass XC</item>
</string-array>
<string-array name="sftp_auth_modes">
<item>Kennwort</item>
<item>Passwort</item>
<item>Privater/Öffentlicher Schlüssel</item>
</string-array>
<string-array name="AcceptAllServerCertificates_options">

View File

@@ -18,7 +18,7 @@
<string name="algorithm_colon">Αλγόριθμος</string>
<string name="app_name">Keepass2Android</string>
<string name="short_app_name">KP2A</string>
<string name="app_name_nonet">Keepass2Android χωρίς σύνδεση</string>
<string name="app_name_nonet">Keepass2Android εκτός σύνδεσης</string>
<string name="short_app_name_nonet">KP2A χωρίς σύνδεση</string>
<string name="app_timeout">Λήξη χρονικού ορίου εφαρμογής</string>
<string name="app_timeout_summary">Χρόνος πριν το κλείδωμα της βάσης δεδομένων όταν η εφαρμογή είναι ανενεργή.</string>
@@ -239,6 +239,25 @@
<string name="sort_moddate">Ταξινόμηση κατά ημερομηνία τροποποίησης</string>
<string name="sort_default">Διατήρηση προεπιλεγμένης ταξινόμησης</string>
<string name="special">Ειδικό</string>
<string name="special_extended">Εκτεταμένοι Ειδικοί</string>
<string name="at_least_one_from_each_group">Τουλάχιστον ένα από κάθε ομάδα</string>
<string name="exclude_lookalike">Εξαίρεση παρόμοιων χαρακτήρων</string>
<string name="password_generation_profile">Προφίλ</string>
<string name="save_password_generation_profile_text">Εισάγετε το όνομα του προφίλ που θα αποθηκευτεί. Εισάγετε ένα υπάρχον όνομα για αντικατάσταση.</string>
<string name="hint_wordcount">Αριθμός λέξεων συνθηματικής φράσης</string>
<string name="hint_wordseparator">Διαχωριστικό λέξεων</string>
<string-array name="PasswordGeneratorModes">
<item>Συνθηματικό</item>
<item>Συνθηματική φράση</item>
<item>Συνθηματική φράση + συνθηματικό</item>
</string-array>
<string-array name="PasswordGeneratorCaseModes">
<item>πεζά</item>
<item>ΚΕΦΑΛΑΙΑ</item>
<item>Πρώτος χαρακτήρας κεφαλαίος</item>
</string-array>
<string name="custom_settings">Προσαρμοσμένες ρυθμίσεις</string>
<string name="passphrase_capitalization">Συνθηματική φράση στα κεφαλαία</string>
<string name="search_hint">Βρείτε τι</string>
<string name="search_results">Αποτελέσματα αναζήτησης</string>
<string name="search_in">Αναζήτηση σε</string>
@@ -299,8 +318,6 @@
<string name="QuickUnlockHideLength_title">Απόκρυψη μήκους QuickUnlock</string>
<string name="QuickUnlockHideLength_summary">Αν ενεργοποιηθεί, αποκρύπτει το μήκος του κωδικού QuickUnlock στη σχετική οθόνη.</string>
<string name="QuickUnlock_fail">Αποτυχία QuickUnlock: λανθασμένο συνθηματικό!</string>
<string name="BinaryDirectory_title">Φάκελος για συνημμένα αρχεία</string>
<string name="BinaryDirectory_summary">Φάκελος όπου αποθηκεύονται τα συνημμένα αρχεία.</string>
<string name="SaveAttachmentDialog_title">Αποθήκευση συνημμένου</string>
<string name="SaveAttachmentDialog_text">Επιλέξτε πού θα αποθηκεύσετε το συνημμένο.</string>
<string name="SaveAttachmentDialog_save">Εξαγωγή σε αρχείο...</string>
@@ -318,6 +335,13 @@
<string name="protection">Προστατευμένο πεδίο</string>
<string name="add_binary">Προσθήκη συνημμένου αρχείου</string>
<string name="add_extra_string">Προσθήκη πρόσθετου κειμένου</string>
<string name="configure_totp">Ρύθμιση TOTP</string>
<string name="totp_secret_key">Μυστικό κλειδί</string>
<string name="totp_encoding_rfc6238">Προεπιλεγμένες ρυθμίσεις τεκμηρίου RFC6238</string>
<string name="totp_encoding_steam">Ρυθμίσεις τεκμηρίου Steam</string>
<string name="totp_encoding_custom">Προσαρμοσμένες ρυθμίσεις τεκμηρίου</string>
<string name="totp_time_step">Χρονικό βήμα</string>
<string name="totp_length">Μήκος κωδικού</string>
<string name="delete_extra_string">Διαγραφή πρόσθετου κειμένου</string>
<string name="database_loaded_quickunlock_enabled">%1$s: κλειδωμένο. Ενεργοποιήθηκε το QuickUnlock.</string>
<string name="database_loaded_unlocked">%1$s: Ξεκλείδωτο.</string>
@@ -633,6 +657,28 @@
<string name="EntryChannel_name">Ειδοποιήσεις</string>
<string name="EntryChannel_desc">Ειδοποίηση για απλοποιημένη πρόσβαση στην τρέχουσα εγγραφή.</string>
<string name="CloseDbAfterFailedAttempts">Κλείσιμο της βάσης δεδομένων μετά από 3 ανεπιτυχείς προσπάθειες βιομετρικού ξεκλειδώματος.</string>
<string-array name="ChangeLog_1_09a">
<item>Προστέθηκε υποστήριξη για τη μορφή αρχείου KDBX 4.1 που εισήχθη στο KeePass 2.48</item>
<item>Προστέθηκε ο διάλογος ρύθμισης ρυθμίσεων TOTP για τις καταχωρήσεις</item>
<item>Βελτιωμένη γεννήτρια κωδικού πρόσβασης: Προστέθηκε υποστήριξη συνθηματικής φράσης, περισσότερες επιλογές, προφίλ και εκτίμηση ισχύος κωδικού πρόσβασης</item>
<item>Βελτιώσεις στην αυτόματη συμπλήρωση (σταθερό αναδυόμενο παράθυρο δεν εμφανίζεται στο Chrome, καλύτερη υποστήριξη υποτομέα)</item>
<item>Βελτιώσεις στην υλοποίηση του OneDrive: δεν υπάρχει πλέον όριο μεγέθους, ούτε περιττές αιτήσεις ελέγχου ταυτότητας</item>
<item>Προστέθηκε επιλογή για να επιλέξετε το φωτεινό/σκούρο θέμα από τις ρυθμίσεις του συστήματος, συμπεριλαμβανομένων των νυχτερινών πλάνων, απαιτεί Android 10+</item>
<item>Update Dropbox implementation to support new authentication method.</item>
<item>Newly setup fingerprint unlock will be invalidated after adding a fingerprint in system settings for increased security.</item>
<item>Allow to open files through system file picker, ignoring the read-only flag</item>
<item>Allow to start moving entries from the entry view menu</item>
</string-array>
<string-array name="ChangeLog_1_08d">
<item>Προσθήκη υποστήριξης για νέα μορφή αρχείου κλειδιού που εισήχθη στο Keepass 2.47</item>
<item>Προσθήκη υποστήριξης για το Argon2id ως λειτουργία παραγωγής κλειδιού</item>
<item>Βελτιωμένη συμβατότητα αυτόματης συμπλήρωσης με Firefox και Chrome</item>
<item>Βελτίωση υποστήριξης καταχωρήσεων TOTP από προγράμματα υπολογιστών</item>
<item>Ενημέρωση pCloud SDK για να διορθωθεί το πρόβλημα ελέγχου ταυτότητας</item>
<item>Ενημέρωση Jsch στην έκδοση 0.1.55</item>
<item>Προσθήκη μενού στην οθόνη επιλογής βάσης δεδομένων</item>
<item>Επιτρέψτε την εξαγωγή εισαγόμενων αρχείων-κλειδιών</item>
</string-array>
<string-array name="ChangeLog_1_08c">
<item>Δεν αποθηκεύει πλέον ονόματα πακέτων εφαρμογών Android στο πεδίο URL</item>
<item>Βελτίωση της συμπεριφοράς κλειδώματος - δεν εμφανίζει πλέον βιομετρική προτροπή αμέσως μετά το ξεκλείδωμα</item>
@@ -931,6 +977,7 @@ Initial public release
<string-array name="design_options">
<item>Ανοιχτό</item>
<item>Σκοτεινό</item>
<item>Ρυθμίσεις συστήματος</item>
</string-array>
<string name="design_title">Σχεδιασμός</string>
<string-array name="ftp_encryption_modes">

Some files were not shown because too many files have changed in this diff Show More