Compare commits

..

643 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
Philipp Crocoll
cc7ee35500 fix to all entries being recognized as TOTP entries 2021-01-24 07:05:58 +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
Philipp Crocoll
2e5ee5f836 Merge branch 'l10n_master2' of https://github.com/PhilippC/keepass2android 2021-01-17 03:17:15 +01:00
Philipp Crocoll
177b36d869 fix bug preventing screen protection to work, closes #1543 2021-01-17 03:13:24 +01:00
Philipp Crocoll
cd484c0398 remove no longer valid note regarding pCloud 2021-01-17 03:12:51 +01:00
PhilippC
a6e3c2024f New translations strings.xml (Hungarian) 2021-01-17 03:07:37 +01:00
PhilippC
31e7037d4f New translations strings.xml (Greek) 2021-01-17 03:07:35 +01:00
PhilippC
13c2e297a8 New translations strings.xml (Japanese) 2021-01-17 03:07:30 +01:00
PhilippC
45d08a5762 New translations strings.xml (Dutch) 2021-01-17 03:07:26 +01:00
PhilippC
0568efc94b New translations strings.xml (Polish) 2021-01-17 03:07:22 +01:00
PhilippC
b61edb44bf New translations strings.xml (Italian) 2021-01-17 03:07:19 +01:00
PhilippC
415d01b36d New translations strings.xml (Portuguese) 2021-01-17 03:07:17 +01:00
PhilippC
b2864feb73 New translations strings.xml (German) 2021-01-17 03:07:14 +01:00
PhilippC
9ddafb6525 New translations strings.xml (Korean) 2021-01-17 03:07:12 +01:00
PhilippC
3771e86169 New translations strings.xml (French) 2021-01-17 03:07:07 +01:00
PhilippC
e8533afe78 New translations strings.xml (Spanish) 2021-01-17 03:07:03 +01:00
PhilippC
28361f4105 New translations strings.xml (Catalan) 2021-01-17 03:06:55 +01:00
PhilippC
110ef46f56 New translations strings.xml (Czech) 2021-01-17 03:06:52 +01:00
PhilippC
5cb23cd9b4 New translations strings.xml (Danish) 2021-01-17 03:06:48 +01:00
PhilippC
6d368e57af New translations strings.xml (Russian) 2021-01-17 03:06:42 +01:00
PhilippC
36bbaa7270 New translations strings.xml (Portuguese, Brazilian) 2021-01-17 03:06:41 +01:00
PhilippC
eb4888c361 New translations strings.xml (Slovak) 2021-01-17 03:06:15 +01:00
PhilippC
1773f9672e New translations strings.xml (Slovenian) 2021-01-17 03:06:11 +01:00
PhilippC
59fbaf87e6 New translations strings.xml (Swedish) 2021-01-17 03:06:05 +01:00
PhilippC
305a2aa6dc New translations strings.xml (Turkish) 2021-01-17 03:06:01 +01:00
PhilippC
ed92b3476f New translations strings.xml (Ukrainian) 2021-01-17 03:05:58 +01:00
PhilippC
b32fa9672f New translations strings.xml (Chinese Simplified) 2021-01-17 03:05:54 +01:00
PhilippC
dd07271550 New translations strings.xml (Chinese Traditional) 2021-01-17 03:05:48 +01:00
Philipp Crocoll
430db12d6f re-add Dutch translations to close #1526. They had been removed because of invalid strings causing crashes, these are now fixed. 2021-01-16 12:27:42 +01:00
PhilippC
53b881c451 New translations strings.xml (French) 2021-01-16 12:27:22 +01:00
Philipp Crocoll
a4219bb9c7 Merge branch 'master' of https://github.com/PhilippC/keepass2android 2021-01-16 11:50:43 +01:00
Philipp Crocoll
7609260218 fix incorrect Unregister/RegisterReceiver calls, closes #1539 2021-01-16 11:50:31 +01:00
PhilippC
2c8bd15474 New translations strings.xml (Slovenian) 2021-01-14 12:20:52 +01:00
PhilippC
d6f3ad8b56 New translations strings.xml (Chinese Simplified) 2021-01-14 04:53:57 +01:00
PhilippC
b9d2f57d41 New translations strings.xml (Greek) 2021-01-13 21:56:45 +01:00
PhilippC
cd34858f9a New translations strings.xml (Japanese) 2021-01-13 21:56:39 +01:00
PhilippC
1639d5abee New translations strings.xml (Dutch) 2021-01-13 21:56:33 +01:00
PhilippC
dd2e4546b9 New translations strings.xml (Korean) 2021-01-13 21:56:21 +01:00
PhilippC
5b6af723b0 New translations strings.xml (Romanian) 2021-01-13 21:56:18 +01:00
PhilippC
9e660f957f New translations strings.xml (French) 2021-01-13 21:56:14 +01:00
PhilippC
1960af5149 New translations strings.xml (Russian) 2021-01-13 21:55:48 +01:00
PhilippC
792d16aba7 New translations strings.xml (Slovenian) 2021-01-13 21:55:06 +01:00
PhilippC
e6e854138b New translations strings.xml (Ukrainian) 2021-01-13 21:54:52 +01:00
PhilippC
2117e5abf9 New translations strings.xml (Chinese Simplified) 2021-01-13 21:54:48 +01:00
PhilippC
04151becfe New translations strings.xml (Chinese Traditional) 2021-01-13 21:54:39 +01:00
Philipp Crocoll
6cc007e47e changelog and manifest for 1.08d-r3 2021-01-13 21:49:47 +01:00
Philipp Crocoll
cd1877d34d fix issue with selecting one of multiple entries for autofill as reported on #1399; also another attempt to work-around the buggy Firefox Autofill 2021-01-13 21:36:56 +01:00
PhilippC
df3bc19fc0 New translations strings.xml (Romanian) 2021-01-13 10:14:58 +01:00
PhilippC
e4e7b1cb48 New translations strings.xml (Romanian) 2021-01-13 09:14:55 +01:00
Philipp Crocoll
c46b7be051 support SHA256 and SHA512 hash algorithm for otpauth:// TOTPs, closes https://github.com/PhilippC/keepass2android/issues/1155 2021-01-12 17:08:55 +01:00
PhilippC
5a8fa21cd1 New translations strings.xml (Slovenian) 2021-01-12 13:13:16 +01:00
Philipp Crocoll
7df048263b add compatibility to Keepass 2.47 TimeOTP-Secret values (but not supporting {TIMEOTP} placeholder) 2021-01-12 12:14:34 +01:00
PhilippC
aba45f4cf5 New translations strings.xml (Slovenian) 2021-01-12 11:18:38 +01:00
Philipp Crocoll
d9bcac600c support for Keyfiles like in Keepass 2.47 2021-01-12 07:31:18 +01:00
Philipp Crocoll
533021da3f Manifest for 1.08d-r2 2021-01-12 07:30:44 +01:00
PhilippC
ceeb7e23b4 New translations strings.xml (Japanese) 2021-01-12 01:25:04 +01:00
PhilippC
f226edf018 New translations strings.xml (Japanese) 2021-01-12 00:18:21 +01:00
PhilippC
e6f0f12240 New translations strings.xml (Japanese) 2021-01-12 00:18:19 +01:00
PhilippC
9df7c03e02 New translations strings.xml (Japanese) 2021-01-11 23:18:59 +01:00
Philipp Crocoll
1b8e290b30 Merge branch 'master' of c:/ph/keepass2android 2021-01-10 22:45:07 +01:00
Philipp Crocoll
a90fd17bea fix issue with incorrect launching of PasswordActivity for AutoExecItems leading to incorrect behavior when launching the app from Autofill (when child databases are opened) 2021-01-10 22:44:33 +01:00
Philipp Crocoll
3fb358ca85 further improvements for Autofill for usability with Firefox (see https://github.com/PhilippC/keepass2android/issues/1399), also fix security bug (closes https://github.com/PhilippC/keepass2android/issues/1527) 2021-01-10 15:25:26 +01:00
PhilippC
83d638e0e5 New translations strings.xml (Dutch) 2021-01-09 14:00:09 +01:00
PhilippC
20a147fd04 New translations strings.xml (Dutch) 2021-01-09 13:00:09 +01:00
Philipp Crocoll
730af8b9ac make sure autofill only returns results when database is unlocked 2021-01-09 11:42:38 +01:00
PhilippC
9a400cecd2 New translations strings.xml (Chinese Simplified) 2021-01-09 08:30:08 +01:00
PhilippC
b5d9ac859f New translations strings.xml (Chinese Simplified) 2021-01-09 07:30:09 +01:00
PhilippC
144cc3c40a New translations strings.xml (Greek) 2021-01-08 12:20:09 +01:00
PhilippC
2b514598d4 New translations strings.xml (Greek) 2021-01-08 11:20:12 +01:00
PhilippC
9c55f7d9e0 New translations strings.xml (Greek) 2021-01-08 11:20:10 +01:00
Philipp Crocoll
7fb070c5fc manifest for 1.08d-r1 2021-01-08 06:45:52 +01:00
PhilippC
b0dccaea98 New translations strings.xml (Chinese Simplified) 2021-01-08 06:42:33 +01:00
PhilippC
7939a0c9e7 New translations strings.xml (Portuguese, Brazilian) 2021-01-08 06:42:30 +01:00
PhilippC
5bccc32bb1 New translations strings.xml (Turkish) 2021-01-08 06:42:26 +01:00
PhilippC
1bf7ce27da New translations strings.xml (Swedish) 2021-01-08 06:42:22 +01:00
PhilippC
59085fd790 New translations strings.xml (Ukrainian) 2021-01-08 06:42:16 +01:00
PhilippC
7f004ca720 New translations strings.xml (Chinese Traditional) 2021-01-08 06:42:13 +01:00
PhilippC
71098fd9ac New translations strings.xml (Czech) 2021-01-08 06:41:48 +01:00
PhilippC
1ec17c77b5 New translations strings.xml (Danish) 2021-01-08 06:41:44 +01:00
PhilippC
adb3a81b2a New translations strings.xml (German) 2021-01-08 06:41:40 +01:00
PhilippC
ccf3a53253 New translations strings.xml (Greek) 2021-01-08 06:41:37 +01:00
PhilippC
9a7a3c6400 New translations strings.xml (Catalan) 2021-01-08 06:41:33 +01:00
PhilippC
1963e2d7bb New translations strings.xml (Korean) 2021-01-08 06:41:28 +01:00
PhilippC
e385eb78e2 New translations strings.xml (French) 2021-01-08 06:41:23 +01:00
PhilippC
e80ac513c4 New translations strings.xml (Spanish) 2021-01-08 06:41:19 +01:00
PhilippC
cf36613c65 New translations strings.xml (Italian) 2021-01-08 06:41:11 +01:00
PhilippC
ca3a9a7722 New translations strings.xml (Polish) 2021-01-08 06:41:08 +01:00
PhilippC
e85d5d519a New translations strings.xml (Portuguese) 2021-01-08 06:41:04 +01:00
PhilippC
2eccf190b4 New translations strings.xml (Russian) 2021-01-08 06:41:01 +01:00
PhilippC
2c18da0fbc New translations strings.xml (Slovak) 2021-01-08 06:40:57 +01:00
PhilippC
776a13ef28 New translations strings.xml (Slovenian) 2021-01-08 06:40:53 +01:00
PhilippC
b5e6e4904c New translations strings.xml (Japanese) 2021-01-08 06:40:44 +01:00
PhilippC
1e2d1503fe New translations strings.xml (Hungarian) 2021-01-08 06:40:39 +01:00
PhilippC
50d16acee9 New translations strings.xml (Dutch) 2021-01-08 06:40:32 +01:00
Philipp Crocoll
710e0f9bba Merge branch 'master' of https://github.com/PhilippC/keepass2android
# Conflicts:
#	src/keepass2android/Resources/values/strings.xml
2021-01-08 06:37:57 +01:00
Philipp Crocoll
84efc14b00 changelog for 1.08d-r1 2021-01-08 06:36:23 +01:00
Philipp Crocoll
ddb58119be update to pCloud SDK 1.2.0 and implement support for different dataHosts (depending on data server location). Closes #1257 2021-01-08 06:35:49 +01:00
PhilippC
3ced852eb4 New translations strings.xml (Korean) 2021-01-08 00:20:13 +01:00
PhilippC
9928c85d0a New translations strings.xml (Korean) 2021-01-07 23:20:11 +01:00
PhilippC
8a095a4032 New translations strings.xml (Korean) 2021-01-07 23:20:09 +01:00
Philipp Crocoll
9372cb7190 avoid potential crash 2021-01-06 18:05:55 +01:00
Philipp Crocoll
bf3a5891e3 bug fixes to activity behavior of PasswordActivity/SelectCurrentDbActivity:
* when a PasswordActivity was still in stopped state and the user triggered opening from Share URL or Autofill and had a child database, the child would not be opened correctly because the child IOC was delivered in OnNewIntent (but not correctly loaded from there)
 * when locking the database from the SelectCurrentDbActivity, the activity would remain active (not really a big problem, DB was locked anyway, but bad UI)
2021-01-06 18:05:35 +01:00
Philipp Crocoll
ca61fa9d1d several changes to fix https://github.com/PhilippC/keepass2android/issues/1399:
* adding a lock to avoid flickering/disappearing prompt
 * returning a fill-response instead of a dataset from authentication activity
 * immediately present one search result (if there is a match) as a dataset item in the autofill prompt
2021-01-02 12:31:19 +01:00
Philipp Crocoll
42eb8222fc use special icon to indicate in search results if an entry is expired 2021-01-02 12:20:47 +01:00
PhilippC
2aca631390 New translations strings.xml (Chinese Simplified) 2021-01-02 02:10:09 +01:00
PhilippC
b175295d43 New translations strings.xml (Chinese Traditional) 2020-12-28 14:30:11 +01:00
PhilippC
0226456d41 New translations strings.xml (Chinese Traditional) 2020-12-28 13:30:10 +01:00
PhilippC
d18aba0747 New translations strings.xml (French) 2020-12-22 08:10:09 +01:00
PhilippC
449b5e3f0a New translations strings.xml (French) 2020-12-19 18:00:09 +01:00
Philipp Crocoll
301216fc2e improve error logging 2020-12-18 20:11:02 +01:00
Philipp Crocoll
c3bef01fe9 add another supporter name 2020-12-17 19:27:25 +01:00
PhilippC
19abf7a1b0 New translations strings.xml (Russian) 2020-12-16 13:40:09 +01:00
PhilippC
3e36a6e5fa New translations strings.xml (Russian) 2020-12-16 12:40:10 +01:00
PhilippC
e65bd9a524 New translations strings.xml (Ukrainian) 2020-12-12 19:50:09 +01:00
Philipp Crocoll
697b331a65 allow to disable DAL verification in Autofill for testing/debugging 2020-12-12 14:16:12 +01:00
Philipp Crocoll
3767450343 allow to disable check for secure screen to solve #1501 2020-12-12 14:14:05 +01:00
PhilippC
2ffa9b06c5 New translations strings.xml (Chinese Simplified) 2020-12-12 11:40:08 +01:00
PhilippC
c1e80d209f New translations strings.xml (Romanian) 2020-12-11 20:00:09 +01:00
PhilippC
a36f5a375a New translations strings.xml (Romanian) 2020-12-11 19:00:10 +01:00
PhilippC
4e7f6d62d2 New translations strings.xml (Russian) 2020-12-08 10:20:10 +01:00
PhilippC
f8e637cdbc New translations strings.xml (Slovenian) 2020-12-07 12:50:10 +01:00
Philipp Crocoll
abf32cf499 manifest and chanelog for 1.08d-r0 2020-12-07 11:03:23 +01:00
Philipp Crocoll
eee24819d5 add support for Argon2id (see https://github.com/keepassxreboot/keepassxc/issues/4317#issuecomment-738101431) 2020-12-07 10:43:46 +01:00
Philipp Crocoll
b7e4bf0be5 fix strings.xml 2020-12-07 10:43:10 +01:00
PhilippC
be65fe2521 New translations strings.xml (Sinhala) 2020-12-05 20:00:08 +01:00
PhilippC
05e62bd40d New translations strings.xml (Sinhala) 2020-12-05 10:00:12 +01:00
PhilippC
ca4e819183 New translations strings.xml (Sinhala) 2020-12-05 10:00:10 +01:00
PhilippC
80b75f11a0 New translations strings.xml (Sinhala) 2020-12-05 10:00:08 +01:00
PhilippC
a18f013d8c New translations strings.xml (Russian) 2020-12-03 11:40:10 +01:00
PhilippC
ffc06404c1 New translations strings.xml (German) 2020-11-30 12:32:18 +01:00
Philipp Crocoll
849cb0c707 changelog for 1.08d 2020-11-30 12:25:12 +01:00
Philipp Crocoll
219932258c Merge branch 'master' of https://github.com/PhilippC/keepass2android 2020-11-30 12:23:24 +01:00
Philipp Crocoll
59a5bb0c89 add menu in SelectCurrentDbActivity, closes https://github.com/PhilippC/keepass2android/issues/988 2020-11-30 12:16:04 +01:00
Philipp Crocoll
29fca5c1f0 allow to export imported keyfiles, closes https://github.com/PhilippC/keepass2android/issues/599 2020-11-30 11:56:39 +01:00
Philipp Crocoll
fec0e7768a add more logging output to diagnose an issue describe by a user where merge conflicts seem to be detected even if they are not there 2020-11-30 11:55:07 +01:00
Philipp Crocoll
3dcb8ed32b update JSch to version 0.1.55, closes #1478 2020-11-30 10:10:13 +01:00
PhilippC
2b1f5cfd0a New translations strings.xml (Italian) 2020-11-26 14:20:13 +01:00
PhilippC
84e08d39a4 New translations strings.xml (Czech) 2020-11-23 01:20:13 +01:00
PhilippC
0a171a6a72 New translations strings.xml (Czech) 2020-11-23 01:20:11 +01:00
PhilippC
cdcbe148c9 Merge pull request #1472 from joschahenningsen/readme-patch
Remove Google+ group from readme
2020-11-21 19:20:46 +01:00
Joscha Henningsen
fea28640b5 update readme 2020-11-20 16:47:39 +01:00
PhilippC
295f19c54e New translations strings.xml (Malayalam) 2020-11-18 14:00:09 +01:00
PhilippC
de759e5870 New translations strings.xml (German) 2020-11-18 13:00:09 +01:00
PhilippC
e9995e0c80 New translations strings.xml (Malayalam) 2020-11-18 10:50:10 +01:00
PhilippC
234fd59d22 New translations strings.xml (Malayalam) 2020-11-18 09:50:13 +01:00
PhilippC
7617012292 New translations strings.xml (Malayalam) 2020-11-18 09:50:11 +01:00
PhilippC
e8085c1c0e New translations strings.xml (Malayalam) 2020-11-17 03:30:07 +01:00
PhilippC
9e8a9e7b9e New translations strings.xml (Malayalam) 2020-11-17 02:30:09 +01:00
PhilippC
e47516b0e0 New translations strings.xml (Malayalam) 2020-11-17 02:30:08 +01:00
Philipp Crocoll
b85cb20177 improve logging on SwitchImeActivity 2020-11-16 12:14:42 +01:00
Philipp Crocoll
612c7927e9 fix to keyboard not opening on QuickUnlock screen, closes #1362 2020-11-16 11:35:09 +01:00
PhilippC
e69af9cfbe New translations strings.xml (Ukrainian) 2020-11-08 20:30:09 +01:00
PhilippC
23f324b1d8 New translations strings.xml (Ukrainian) 2020-11-08 20:30:07 +01:00
PhilippC
a1f7a4245a New translations strings.xml (Slovak) 2020-11-07 23:00:08 +01:00
PhilippC
5dab309921 New translations strings.xml (Japanese) 2020-11-07 16:30:09 +01:00
PhilippC
44d324bc7d New translations strings.xml (Serbian (Cyrillic)) 2020-10-30 19:50:10 +01:00
PhilippC
ebadedae41 New translations strings.xml (Czech) 2020-10-27 14:30:12 +01:00
PhilippC
3f89a34946 New translations strings.xml (Czech) 2020-10-27 14:30:09 +01:00
PhilippC
d823768464 New translations strings.xml (Italian) 2020-10-26 18:30:09 +01:00
PhilippC
ee340b069b New translations strings.xml (Chinese Simplified) 2020-10-24 06:50:08 +02:00
PhilippC
d13a35b9a8 New translations strings.xml (French) 2020-10-22 22:20:08 +02:00
PhilippC
0ce41b9f54 New translations strings.xml (Slovenian) 2020-10-19 18:20:09 +02:00
PhilippC
1be1829cfe New translations strings.xml (Chinese Traditional) 2020-10-19 14:20:10 +02:00
PhilippC
160922c9fb New translations strings.xml (Dutch) 2020-10-19 11:31:39 +02:00
353 changed files with 23911 additions and 6251 deletions

1
.gitignore vendored
View File

@@ -170,3 +170,4 @@ src/java/Keepass2AndroidPluginSDK2/build/generated/mockable-Google-Inc.-Google-A
/src/java/KP2ASoftkeyboard_AS/app/.cxx
/src/java/KP2ASoftkeyboard_AS/app/src/main/libs
/src/java/KP2AKdbLibrary/app/.cxx
/src/ActionViewFilterTest

View File

@@ -8,7 +8,7 @@ The password database file can be synchronized across different devices. This wo
# Where to get it?
Regular stable releases of Keepass2Android are available on [Google Play](https://play.google.com/store/apps/details?id=keepass2android.keepass2android).
Beta-releases can be obtained by opting in to the [Beta testing channel](https://play.google.com/apps/testing/keepass2android.keepass2android) or [Beta testing channel for Keepass2Android Offline](https://play.google.com/apps/testing/keepass2android.keepass2android_nonet). Please join the [Beta tester group](https://plus.google.com/communities/107293657110547776032) for news and discussions about the latest beta releases.
Beta-releases can be obtained by opting in to the [Beta testing channel](https://play.google.com/apps/testing/keepass2android.keepass2android) or [Beta testing channel for Keepass2Android Offline](https://play.google.com/apps/testing/keepass2android.keepass2android_nonet).
# How can I contribute?
* Help to translate Keepass2Android into your language or improve translations at [our Crowdin page](http://crowdin.net/project/keepass2android)
@@ -19,4 +19,4 @@ Beta-releases can be obtained by opting in to the [Beta testing channel](https:/
# How do I learn more?
Please see the [documentation](Documentation.md).
[![Build Status](https://www.bitrise.io/app/43a23ab54dee9f7e/status.svg?token=2vryTsMQzTX3XRPikhgRwA&branch=nonet)](https://www.bitrise.io/app/43a23ab54dee9f7e)
[![Build Status](https://www.bitrise.io/app/43a23ab54dee9f7e/status.svg?token=2vryTsMQzTX3XRPikhgRwA&branch=master)](https://www.bitrise.io/app/43a23ab54dee9f7e)

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

@@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aBusinessLogic", "Kp2aBu
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwofishCipher", "TwofishCipher\TwofishCipher.csproj", "{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaFileStorageBindings", "JavaFileStorageBindings\JavaFileStorageBindings.csproj", "{48574278-4779-4B3A-A9E4-9CF1BC285D0B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidFileChooserBinding", "AndroidFileChooserBinding\AndroidFileChooserBinding.csproj", "{3C0F7FE5-639F-4422-A087-8B26CF862D1B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KP2AKdbLibraryBinding", "KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj", "{70D3844A-D9FA-4A64-B205-A84C6A822196}"
@@ -107,8 +109,8 @@ Global
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Release|Win32.Build.0 = Release|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Release|x64.ActiveCfg = Release|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Release|x64.Build.0 = Release|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.ActiveCfg = Debug|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.Build.0 = Debug|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
@@ -185,8 +187,8 @@ Global
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Release|Win32.ActiveCfg = Release|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Release|x64.ActiveCfg = Release|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
@@ -203,8 +205,8 @@ Global
{70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Win32.ActiveCfg = Release|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|x64.ActiveCfg = Release|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
@@ -221,8 +223,8 @@ Global
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Win32.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|x64.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU

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

@@ -367,5 +367,27 @@ namespace KeePassLib.Cryptography
Debug.Assert(iPos == pbRes.Length);
return pbRes;
}
private static int g_iWeakSeed = 0;
public static Random NewWeakRandom()
{
long s64 = DateTime.UtcNow.ToBinary();
int s32 = (int)((s64 >> 32) ^ s64);
lock (g_oSyncRoot)
{
unchecked
{
g_iWeakSeed += 0x78A8C4B7; // Prime number
s32 ^= g_iWeakSeed;
}
}
// Prevent overflow in the Random constructor of .NET 2.0
if (s32 == int.MinValue) s32 = int.MaxValue;
return new Random(s32);
}
}
}

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-2020 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,28 +20,67 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
#if !KeePassUAP
using System.Security.Cryptography;
#endif
using KeePassLib.Native;
using KeePassLib.Utility;
namespace KeePassLib.Cryptography
{
public static class CryptoUtil
{
private static bool? g_obProtData = null;
public static bool IsProtectedDataSupported
{
get
{
if (g_obProtData.HasValue) return g_obProtData.Value;
bool b = false;
try
{
Random r = CryptoRandom.NewWeakRandom();
byte[] pbData = new byte[137];
r.NextBytes(pbData);
byte[] pbEnt = new byte[41];
r.NextBytes(pbEnt);
byte[] pbEnc = ProtectedData.Protect(pbData, pbEnt,
DataProtectionScope.CurrentUser);
if ((pbEnc != null) && !MemUtil.ArraysEqual(pbEnc, pbData))
{
byte[] pbDec = ProtectedData.Unprotect(pbEnc, pbEnt,
DataProtectionScope.CurrentUser);
if ((pbDec != null) && MemUtil.ArraysEqual(pbDec, pbData))
b = true;
}
}
catch (Exception) { Debug.Assert(false); }
Debug.Assert(b); // Should be supported on all systems
g_obProtData = b;
return b;
}
}
public static byte[] HashSha256(byte[] pbData)
{
if(pbData == null) throw new ArgumentNullException("pbData");
if (pbData == null) throw new ArgumentNullException("pbData");
return HashSha256(pbData, 0, pbData.Length);
}
public static byte[] HashSha256(byte[] pbData, int iOffset, int cbCount)
{
if(pbData == null) throw new ArgumentNullException("pbData");
if (pbData == null) throw new ArgumentNullException("pbData");
#if DEBUG
byte[] pbCopy = new byte[pbData.Length];
@@ -49,7 +88,7 @@ namespace KeePassLib.Cryptography
#endif
byte[] pbHash;
using(SHA256Managed h = new SHA256Managed())
using (SHA256Managed h = new SHA256Managed())
{
pbHash = h.ComputeHash(pbData, iOffset, cbCount);
}
@@ -66,6 +105,22 @@ namespace KeePassLib.Cryptography
return pbHash;
}
internal static byte[] HashSha256(string strFilePath)
{
byte[] pbHash = null;
using (FileStream fs = new FileStream(strFilePath, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
using (SHA256Managed h = new SHA256Managed())
{
pbHash = h.ComputeHash(fs);
}
}
return pbHash;
}
/// <summary>
/// Create a cryptographic key of length <paramref name="cbOut" />
/// (in bytes) from <paramref name="pbIn" />.
@@ -73,34 +128,34 @@ namespace KeePassLib.Cryptography
public static byte[] ResizeKey(byte[] pbIn, int iInOffset,
int cbIn, int cbOut)
{
if(pbIn == null) throw new ArgumentNullException("pbIn");
if(cbOut < 0) throw new ArgumentOutOfRangeException("cbOut");
if (pbIn == null) throw new ArgumentNullException("pbIn");
if (cbOut < 0) throw new ArgumentOutOfRangeException("cbOut");
if(cbOut == 0) return MemUtil.EmptyByteArray;
if (cbOut == 0) return MemUtil.EmptyByteArray;
byte[] pbHash;
if(cbOut <= 32) pbHash = HashSha256(pbIn, iInOffset, cbIn);
if (cbOut <= 32) pbHash = HashSha256(pbIn, iInOffset, cbIn);
else
{
using(SHA512Managed h = new SHA512Managed())
using (SHA512Managed h = new SHA512Managed())
{
pbHash = h.ComputeHash(pbIn, iInOffset, cbIn);
}
}
if(cbOut == pbHash.Length) return pbHash;
if (cbOut == pbHash.Length) return pbHash;
byte[] pbRet = new byte[cbOut];
if(cbOut < pbHash.Length)
if (cbOut < pbHash.Length)
Array.Copy(pbHash, pbRet, cbOut);
else
{
int iPos = 0;
ulong r = 0;
while(iPos < cbOut)
while (iPos < cbOut)
{
Debug.Assert(pbHash.Length == 64);
using(HMACSHA256 h = new HMACSHA256(pbHash))
using (HMACSHA256 h = new HMACSHA256(pbHash))
{
byte[] pbR = MemUtil.UInt64ToBytes(r);
byte[] pbPart = h.ComputeHash(pbR);
@@ -125,5 +180,75 @@ namespace KeePassLib.Cryptography
MemUtil.ZeroByteArray(pbHash);
return pbRet;
}
#if !KeePassUAP
private static bool? g_obAesCsp = null;
internal static SymmetricAlgorithm CreateAes()
{
if (g_obAesCsp.HasValue)
return (g_obAesCsp.Value ? CreateAesCsp() : new RijndaelManaged());
SymmetricAlgorithm a = CreateAesCsp();
g_obAesCsp = (a != null);
return (a ?? new RijndaelManaged());
}
private static SymmetricAlgorithm CreateAesCsp()
{
try
{
// On Windows, the CSP implementation is only minimally
// faster (and for key derivations it's not used anyway,
// as KeePass uses a native implementation based on
// CNG/BCrypt, which is much faster)
if (!NativeLib.IsUnix()) return null;
string strFqn = Assembly.CreateQualifiedName(
"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"System.Security.Cryptography.AesCryptoServiceProvider");
Type t = Type.GetType(strFqn);
if (t == null) return null;
return (Activator.CreateInstance(t) as SymmetricAlgorithm);
}
catch (Exception) { Debug.Assert(false); }
return null;
}
#endif
public static byte[] ProtectData(byte[] pb, byte[] pbOptEntropy,
DataProtectionScope s)
{
return ProtectDataPriv(pb, true, pbOptEntropy, s);
}
public static byte[] UnprotectData(byte[] pb, byte[] pbOptEntropy,
DataProtectionScope s)
{
return ProtectDataPriv(pb, false, pbOptEntropy, s);
}
private static byte[] ProtectDataPriv(byte[] pb, bool bProtect,
byte[] pbOptEntropy, DataProtectionScope s)
{
if (pb == null) throw new ArgumentNullException("pb");
if ((pbOptEntropy != null) && (pbOptEntropy.Length == 0))
pbOptEntropy = null;
if (CryptoUtil.IsProtectedDataSupported)
{
if (bProtect)
return ProtectedData.Protect(pb, pbOptEntropy, s);
return ProtectedData.Unprotect(pb, pbOptEntropy, s);
}
Debug.Assert(false);
byte[] pbCopy = new byte[pb.Length];
Array.Copy(pb, pbCopy, pb.Length);
return pbCopy;
}
}
}

View File

@@ -46,6 +46,8 @@ namespace KeePassLib.Cryptography.KeyDerivation
private const ulong NbBlockSizeInQW = NbBlockSize / 8UL;
private const ulong NbSyncPoints = 4;
private const ulong NbAddressesInBlock = 128;
private const int NbPreHashDigestLength = 64;
private const int NbPreHashSeedLength = NbPreHashDigestLength + 8;
@@ -56,6 +58,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
private sealed class Argon2Ctx
{
public Argon2Type Type = Argon2Type.D;
public uint Version = 0;
public ulong Lanes = 0;
@@ -89,9 +92,9 @@ namespace KeePassLib.Cryptography.KeyDerivation
}
}
private static byte[] Argon2d(byte[] pbMsg, byte[] pbSalt, uint uParallel,
ulong uMem, ulong uIt, int cbOut, uint uVersion, byte[] pbSecretKey,
byte[] pbAssocData)
private byte[] Argon2Transform(byte[] pbMsg, byte[] pbSalt, uint uParallel,
ulong uMem, ulong uIt, int cbOut, uint uVersion, byte[] pbSecretKey,
byte[] pbAssocData)
{
pbSecretKey = (pbSecretKey ?? MemUtil.EmptyByteArray);
pbAssocData = (pbAssocData ?? MemUtil.EmptyByteArray);
@@ -101,6 +104,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
#endif
Argon2Ctx ctx = new Argon2Ctx();
ctx.Type = m_t;
ctx.Version = uVersion;
ctx.Lanes = uParallel;
@@ -137,7 +141,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx(uVersion, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx(0, pbBuf, 0); // Argon2d type = 0
MemUtil.UInt32ToBytesEx((uint)m_t, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
MemUtil.UInt32ToBytesEx((uint)pbMsg.Length, pbBuf, 0);
h.TransformBlock(pbBuf, 0, pbBuf.Length, pbBuf, 0);
@@ -487,18 +491,43 @@ namespace KeePassLib.Cryptography.KeyDerivation
private static void FillSegmentThr(object o)
{
Argon2ThreadInfo ti = (o as Argon2ThreadInfo);
if(ti == null) { Debug.Assert(false); return; }
if (ti == null) { Debug.Assert(false); return; }
try
{
Argon2Ctx ctx = ti.Context;
if(ctx == null) { Debug.Assert(false); return; }
if (ctx == null) { Debug.Assert(false); return; }
Debug.Assert(ctx.Version >= MinVersion);
bool bCanXor = (ctx.Version >= 0x13U);
ulong[] pbR = new ulong[NbBlockSizeInQW];
ulong[] pbTmp = new ulong[NbBlockSizeInQW];
ulong[] pbAddrInputZero = null;
bool bDataIndependentAddr = ((ctx.Type == Argon2Type.ID) &&
(ti.Pass == 0) && (ti.Slice < (NbSyncPoints / 2)));
if (bDataIndependentAddr)
{
pbAddrInputZero = new ulong[NbBlockSizeInQW * 3];
const int iInput = (int)NbBlockSizeInQW;
pbAddrInputZero[iInput] = ti.Pass;
pbAddrInputZero[iInput + 1] = ti.Lane;
pbAddrInputZero[iInput + 2] = ti.Slice;
pbAddrInputZero[iInput + 3] = ctx.MemoryBlocks;
pbAddrInputZero[iInput + 4] = ctx.TCost;
pbAddrInputZero[iInput + 5] = (ulong)ctx.Type;
}
ulong uStart = 0;
if((ti.Pass == 0) && (ti.Slice == 0)) uStart = 2;
if ((ti.Pass == 0) && (ti.Slice == 0))
{
uStart = 2;
if (bDataIndependentAddr)
NextAddresses(pbAddrInputZero, pbR, pbTmp);
}
ulong uCur = (ti.Lane * ctx.LaneLength) + (ti.Slice *
ctx.SegmentLength) + uStart;
@@ -506,17 +535,23 @@ namespace KeePassLib.Cryptography.KeyDerivation
ulong uPrev = (((uCur % ctx.LaneLength) == 0) ?
(uCur + ctx.LaneLength - 1UL) : (uCur - 1UL));
ulong[] pbR = new ulong[NbBlockSizeInQW];
ulong[] pbTmp = new ulong[NbBlockSizeInQW];
for(ulong i = uStart; i < ctx.SegmentLength; ++i)
for (ulong i = uStart; i < ctx.SegmentLength; ++i)
{
if((uCur % ctx.LaneLength) == 1)
if ((uCur % ctx.LaneLength) == 1)
uPrev = uCur - 1UL;
ulong uPseudoRand = ctx.Mem[uPrev * NbBlockSizeInQW];
ulong uPseudoRand;
if (bDataIndependentAddr)
{
ulong iMod = i % NbAddressesInBlock;
if (iMod == 0)
NextAddresses(pbAddrInputZero, pbR, pbTmp);
uPseudoRand = pbAddrInputZero[iMod];
}
else uPseudoRand = ctx.Mem[uPrev * NbBlockSizeInQW];
ulong uRefLane = (uPseudoRand >> 32) % ctx.Lanes;
if((ti.Pass == 0) && (ti.Slice == 0))
if ((ti.Pass == 0) && (ti.Slice == 0))
uRefLane = ti.Lane;
ti.Index = i;
@@ -536,11 +571,12 @@ namespace KeePassLib.Cryptography.KeyDerivation
MemUtil.ZeroArray<ulong>(pbR);
MemUtil.ZeroArray<ulong>(pbTmp);
if (pbAddrInputZero != null) MemUtil.ZeroArray<ulong>(pbAddrInputZero);
}
catch(Exception) { Debug.Assert(false); }
catch (Exception) { Debug.Assert(false); }
try { ti.Finished.Set(); }
catch(Exception) { Debug.Assert(false); }
catch (Exception) { Debug.Assert(false); }
}
#if ARGON2_B2ROUND_ARRAYS
@@ -610,6 +646,19 @@ namespace KeePassLib.Cryptography.KeyDerivation
XorBlock(pMem, uNext, pbR, 0);
}
private static void NextAddresses(ulong[] pbAddrInputZero, ulong[] pbR,
ulong[] pbTmp)
{
// pbAddrInputZero contains an address block, an input block and a zero block
const ulong uAddr = 0;
const ulong uInput = NbBlockSizeInQW;
const ulong uZero = NbBlockSizeInQW * 2;
++pbAddrInputZero[uInput + 6];
FillBlock(pbAddrInputZero, uZero, uInput, uAddr, false, pbR, pbTmp);
FillBlock(pbAddrInputZero, uZero, uAddr, uAddr, false, pbR, pbTmp);
}
private static byte[] FinalHash(Argon2Ctx ctx, int cbOut, Blake2b h)
{
ulong[] pqBlockHash = new ulong[NbBlockSizeInQW];

View File

@@ -25,11 +25,22 @@ using System.Text;
namespace KeePassLib.Cryptography.KeyDerivation
{
public sealed partial class Argon2Kdf : KdfEngine
public enum Argon2Type
{
// The values must be the same as in the Argon2 specification
D = 0,
ID = 2
}
public sealed partial class Argon2Kdf : KdfEngine
{
private static readonly PwUuid g_uuid = new PwUuid(new byte[] {
0xEF, 0x63, 0x6D, 0xDF, 0x8C, 0x29, 0x44, 0x4B,
0x91, 0xF7, 0xA9, 0xA4, 0x03, 0xE3, 0x0A, 0x0C });
private static readonly PwUuid g_uuidD = new PwUuid(new byte[] {
0xEF, 0x63, 0x6D, 0xDF, 0x8C, 0x29, 0x44, 0x4B,
0x91, 0xF7, 0xA9, 0xA4, 0x03, 0xE3, 0x0A, 0x0C });
private static readonly PwUuid g_uuidID = new PwUuid(new byte[] {
0x9E, 0x29, 0x8B, 0x19, 0x56, 0xDB, 0x47, 0x73,
0xB2, 0x3D, 0xFC, 0x3E, 0xC6, 0xF0, 0xA1, 0xE6 });
public const string ParamSalt = "S"; // Byte[]
public const string ParamParallelism = "P"; // UInt32
@@ -55,28 +66,37 @@ namespace KeePassLib.Cryptography.KeyDerivation
internal const uint MinParallelism = 1;
internal const uint MaxParallelism = (1 << 24) - 1;
internal const ulong DefaultIterations = 2;
internal const ulong DefaultMemory = 1024 * 1024; // 1 MB
internal const uint DefaultParallelism = 2;
internal const ulong DefaultIterations = 2;
internal const ulong DefaultMemory = 64 * 1024 * 1024; // 64 MB
internal const uint DefaultParallelism = 2;
public override PwUuid Uuid
{
get { return g_uuid; }
}
private readonly Argon2Type m_t;
public override string Name
{
get { return "Argon2"; }
}
public override PwUuid Uuid
{
get { return ((m_t == Argon2Type.D) ? g_uuidD : g_uuidID); }
}
public override string Name
{
get { return ((m_t == Argon2Type.D) ? "Argon2d" : "Argon2id"); }
}
public Argon2Kdf() : this(Argon2Type.D)
{
}
public Argon2Kdf(Argon2Type t)
{
if ((t != Argon2Type.D) && (t != Argon2Type.ID))
throw new NotSupportedException();
m_t = t;
}
public override byte[] GetSeed(KdfParameters p)
{ return p.GetByteArray(ParamSalt); }
public Argon2Kdf()
{
}
public override KdfParameters GetDefaultParameters()
public override KdfParameters GetDefaultParameters()
{
KdfParameters p = base.GetDefaultParameters();
@@ -92,7 +112,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
public override void Randomize(KdfParameters p)
{
if(p == null) { Debug.Assert(false); return; }
Debug.Assert(g_uuid.Equals(p.KdfUuid));
Debug.Assert(p.KdfUuid.Equals(this.Uuid));
byte[] pb = CryptoRandom.Instance.GetRandomBytes(32);
p.SetByteArray(ParamSalt, pb);
@@ -128,46 +148,59 @@ namespace KeePassLib.Cryptography.KeyDerivation
byte[] pbSecretKey = p.GetByteArray(ParamSecretKey);
byte[] pbAssocData = p.GetByteArray(ParamAssocData);
if (pbSecretKey != null) {
throw new ArgumentOutOfRangeException("Unsupported configuration: non-null pbSecretKey");
byte[] pbRet;
if (m_t == Argon2Type.ID)
{
pbRet = Argon2Transform(pbMsg, pbSalt, uPar, uMem,
uIt, 32, v, pbSecretKey, pbAssocData);
}
else
{
if (pbSecretKey != null)
{
throw new ArgumentOutOfRangeException("Unsupported configuration: non-null pbSecretKey");
}
if (pbAssocData != null)
{
throw new ArgumentOutOfRangeException("Unsupported configuration: non-null pbAssocData");
}
/*
byte[] pbRet = Argon2d(pbMsg, pbSalt, uPar, uMem, uIt,
32, v, pbSecretKey, pbAssocData);
*/
IntPtr msgPtr = Marshal.AllocHGlobal(pbMsg.Length);
IntPtr saltPtr = Marshal.AllocHGlobal(pbSalt.Length);
IntPtr retPtr = Marshal.AllocHGlobal(32);
Marshal.Copy(pbMsg, 0, msgPtr, pbMsg.Length);
Marshal.Copy(pbSalt, 0, saltPtr, pbSalt.Length);
const UInt32 Argon2_d = 0;
int ret = argon2_hash(
(UInt32)uIt, (UInt32)(uMem / 1024), uPar,
msgPtr, (IntPtr)pbMsg.Length,
saltPtr, (IntPtr)pbSalt.Length,
retPtr, (IntPtr)32,
(IntPtr)0, (IntPtr)0, Argon2_d, v);
if (ret != 0)
{
throw new Exception("argon2_hash failed with " + ret);
}
pbRet = new byte[32];
Marshal.Copy(retPtr, pbRet, 0, 32);
Marshal.FreeHGlobal(msgPtr);
Marshal.FreeHGlobal(saltPtr);
Marshal.FreeHGlobal(retPtr);
}
if (pbAssocData != null) {
throw new ArgumentOutOfRangeException("Unsupported configuration: non-null pbAssocData");
}
/*
byte[] pbRet = Argon2d(pbMsg, pbSalt, uPar, uMem, uIt,
32, v, pbSecretKey, pbAssocData);
*/
IntPtr msgPtr = Marshal.AllocHGlobal(pbMsg.Length);
IntPtr saltPtr = Marshal.AllocHGlobal(pbSalt.Length);
IntPtr retPtr = Marshal.AllocHGlobal(32);
Marshal.Copy(pbMsg, 0, msgPtr, pbMsg.Length);
Marshal.Copy(pbSalt, 0, saltPtr, pbSalt.Length);
const UInt32 Argon2_d = 0;
int ret = argon2_hash(
(UInt32)uIt, (UInt32)(uMem / 1024), uPar,
msgPtr, (IntPtr)pbMsg.Length,
saltPtr, (IntPtr)pbSalt.Length,
retPtr, (IntPtr)32,
(IntPtr)0, (IntPtr)0, Argon2_d, v);
if (ret != 0) {
throw new Exception("argon2_hash failed with " + ret);
}
byte[] pbRet = new byte[32];
Marshal.Copy(retPtr, pbRet, 0, 32);
Marshal.FreeHGlobal(msgPtr);
Marshal.FreeHGlobal(saltPtr);
Marshal.FreeHGlobal(retPtr);
if(uMem > (100UL * 1024UL * 1024UL)) GC.Collect();
if(uMem > (100UL * 1024UL * 1024UL)) GC.Collect();
return pbRet;
}

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-2020 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

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-2020 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
@@ -41,10 +41,11 @@ namespace KeePassLib.Cryptography.KeyDerivation
private static void EnsureInitialized()
{
if(g_l.Count > 0) return;
if(g_l.Count != 0) return;
g_l.Add(new AesKdf());
g_l.Add(new Argon2Kdf());
g_l.Add(new Argon2Kdf(Argon2Type.D));
g_l.Add(new Argon2Kdf(Argon2Type.ID));
}
internal static KdfParameters GetDefaultParameters()

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

@@ -50,6 +50,7 @@
<HintPath>..\ProtoBuf\protobuf-net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Security" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Mono.Android" />
@@ -71,7 +72,9 @@
<Compile Include="Cryptography\KeyDerivation\KdfParameters.cs" />
<Compile Include="Cryptography\KeyDerivation\KdfPool.cs" />
<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" />
@@ -160,6 +163,7 @@
<Compile Include="Utility\UrlUtil.cs" />
<Compile Include="Utility\TimeUtil.cs" />
<Compile Include="Delegates\Handlers.cs" />
<Compile Include="Utility\XmlUtilEx.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -0,0 +1,281 @@
/*
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.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using KeePassLib.Cryptography;
using KeePassLib.Resources;
using KeePassLib.Serialization;
using KeePassLib.Utility;
namespace KeePassLib.Keys
{
[XmlType("KeyFile")]
public sealed class KfxFile
{
private const ulong KfxVersionCriticalMask = 0xFFFF000000000000UL;
private const int KfxDataHashLength = 4;
private KfxMeta m_meta = new KfxMeta();
public KfxMeta Meta
{
get { return m_meta; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_meta = value;
}
}
private KfxKey m_key = new KfxKey();
public KfxKey Key
{
get { return m_key; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_key = value;
}
}
public static KfxFile Create(ulong uVersion, byte[] pbKey, byte[] pbHash)
{
if(pbKey == null) throw new ArgumentNullException("pbKey");
if(pbKey.Length == 0) throw new ArgumentOutOfRangeException("pbKey");
if(uVersion == 0) uVersion = 0x0002000000000000;
// Null hash: generate one, empty hash: store no hash
if(pbHash == null) pbHash = HashData(pbKey);
VerifyHash(pbKey, pbHash);
KfxFile kf = new KfxFile();
if(uVersion == 0x0001000000000000)
kf.Meta.Version = "1.00"; // KeePass <= 2.46 used two zeros
else kf.Meta.Version = StrUtil.VersionToString(uVersion, 2);
if(uVersion == 0x0001000000000000)
kf.Key.Data.Value = Convert.ToBase64String(pbKey);
else if(uVersion == 0x0002000000000000)
{
kf.Key.Data.Value = FormatKeyHex(pbKey, 3);
if(pbHash.Length != 0)
kf.Key.Data.Hash = MemUtil.ByteArrayToHexString(pbHash);
}
else throw new NotSupportedException(KLRes.FileVersionUnsupported);
return kf;
}
internal static KfxFile Create(ulong uVersion, string strKey, string strHash)
{
byte[] pbKey = ParseKey(uVersion, strKey);
byte[] pbHash = ((strHash != null) ? ParseHash(strHash) : null);
return Create(uVersion, pbKey, pbHash);
}
internal static bool CanLoad(string strFilePath)
{
if(string.IsNullOrEmpty(strFilePath)) { Debug.Assert(false); return false; }
try
{
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
using(Stream s = IOConnection.OpenRead(ioc))
{
return (Load(s) != null);
}
}
catch(Exception) { }
return false;
}
public static KfxFile Load(Stream s)
{
return XmlUtilEx.Deserialize<KfxFile>(s);
}
public void Save(Stream s)
{
XmlUtilEx.Serialize<KfxFile>(s, this, true);
}
private static string FormatKeyHex(byte[] pb, int cTabs)
{
StringBuilder sb = new StringBuilder();
string str = MemUtil.ByteArrayToHexString(pb);
for(int i = 0; i < str.Length; ++i)
{
if((i & 0x1F) == 0)
{
sb.AppendLine();
sb.Append('\t', cTabs);
}
else if((i & 0x07) == 0) sb.Append(' ');
sb.Append(str[i]);
}
sb.AppendLine();
if(cTabs > 0) sb.Append('\t', cTabs - 1);
return sb.ToString();
}
private ulong GetVersion()
{
string str = m_meta.Version;
if(string.IsNullOrEmpty(str)) return 0;
return StrUtil.ParseVersion(str);
}
public byte[] GetKey()
{
ulong uVersion = GetVersion();
byte[] pbKey = ParseKey(uVersion, m_key.Data.Value);
if((pbKey == null) || (pbKey.Length == 0))
throw new FormatException(KLRes.FileCorrupted);
byte[] pbHash = ParseHash(m_key.Data.Hash);
VerifyHash(pbKey, pbHash);
return pbKey;
}
private static byte[] HashData(byte[] pb)
{
return MemUtil.Mid(CryptoUtil.HashSha256(pb), 0, KfxDataHashLength);
}
private static void VerifyHash(byte[] pbKey, byte[] pbHash)
{
// The hash is optional; empty hash means success
if((pbHash == null) || (pbHash.Length == 0)) return;
byte[] pbHashCmp = HashData(pbKey);
if(!MemUtil.ArraysEqual(pbHash, pbHashCmp))
throw new Exception("Keyfile hash mismatch!");
}
private static byte[] ParseKey(ulong uVersion, string strKey)
{
if(strKey == null) throw new ArgumentNullException("strKey");
strKey = StrUtil.RemoveWhiteSpace(strKey);
if(string.IsNullOrEmpty(strKey)) return MemUtil.EmptyByteArray;
uVersion &= KfxVersionCriticalMask;
byte[] pbKey;
if(uVersion == 0x0001000000000000)
pbKey = Convert.FromBase64String(strKey);
else if(uVersion == 0x0002000000000000)
pbKey = ParseHex(strKey);
else throw new NotSupportedException(KLRes.FileVersionUnsupported);
return pbKey;
}
private static byte[] ParseHash(string strHash)
{
return ParseHex(strHash);
}
private static byte[] ParseHex(string str)
{
if(str == null) throw new ArgumentNullException("str");
if(str.Length == 0) return MemUtil.EmptyByteArray;
if(((str.Length & 1) != 0) || !StrUtil.IsHexString(str, true))
throw new FormatException();
return MemUtil.HexStringToByteArray(str);
}
}
public sealed class KfxMeta
{
private string m_strVersion = string.Empty;
[DefaultValue("")]
public string Version
{
get { return m_strVersion; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strVersion = value;
}
}
}
public sealed class KfxKey
{
private KfxData m_data = new KfxData();
public KfxData Data
{
get { return m_data; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_data = value;
}
}
}
public sealed class KfxData
{
private string m_strHash = string.Empty;
[DefaultValue("")]
[XmlAttribute("Hash")]
public string Hash
{
get { return m_strHash; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strHash = value;
}
}
private string m_strValue = string.Empty;
[DefaultValue("")]
[XmlText]
public string Value
{
get { return m_strValue; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strValue = value;
}
}
}
}

View File

@@ -127,10 +127,9 @@ namespace KeePassLib.Keys
#endif
}
byte[] pbKey = LoadXmlKeyFile(pbFileData);
if(pbKey == null) pbKey = LoadKeyFile(pbFileData);
byte[] pbKey = LoadKeyFile(pbFileData);
if(pbKey == null) throw new InvalidOperationException();
if (pbKey == null) throw new InvalidOperationException();
m_ioc = iocKeyFile;
m_pbKeyData = new ProtectedBinary(true, pbKey);
@@ -150,54 +149,60 @@ namespace KeePassLib.Keys
// }
private static byte[] LoadKeyFile(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
private static byte[] LoadKeyFile(byte[] pbFileData)
{
if (pbFileData == null) throw new ArgumentNullException("pbFileData");
int iLength = pbFileData.Length;
byte[] pbKey = LoadKeyFileXml(pbFileData);
if (pbKey != null) return pbKey;
byte[] pbKey = null;
if(iLength == 32) pbKey = LoadBinaryKey32(pbFileData);
else if(iLength == 64) pbKey = LoadHexKey32(pbFileData);
int cb = pbFileData.Length;
if (cb == 32) return pbFileData;
if(pbKey == null)
pbKey = CryptoUtil.HashSha256(pbFileData);
if (cb == 64)
{
pbKey = LoadKeyFileHex(pbFileData);
if (pbKey != null) return pbKey;
}
return pbKey;
}
return CryptoUtil.HashSha256(pbFileData);
}
private static byte[] LoadBinaryKey32(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
if(pbFileData.Length != 32) { Debug.Assert(false); return null; }
private static byte[] LoadKeyFileXml(byte[] pbFileData)
{
KfxFile kf;
try
{
using (MemoryStream ms = new MemoryStream(pbFileData, false))
{
kf = KfxFile.Load(ms);
}
}
catch (Exception) { return null; }
return pbFileData;
}
// We have a syntactically valid XML key file;
// failing to verify the key should throw an exception
return ((kf != null) ? kf.GetKey() : null);
}
private static byte[] LoadHexKey32(byte[] pbFileData)
{
if(pbFileData == null) { Debug.Assert(false); return null; }
if(pbFileData.Length != 64) { Debug.Assert(false); return null; }
private static byte[] LoadKeyFileHex(byte[] pbFileData)
{
if (pbFileData == null) { Debug.Assert(false); return null; }
try
{
if(!StrUtil.IsHexString(pbFileData, true)) return null;
try
{
int cc = pbFileData.Length;
if ((cc & 1) != 0) { Debug.Assert(false); return null; }
string strHex = StrUtil.Utf8.GetString(pbFileData);
byte[] pbKey = MemUtil.HexStringToByteArray(strHex);
if((pbKey == null) || (pbKey.Length != 32))
{
Debug.Assert(false);
return null;
}
if (!StrUtil.IsHexString(pbFileData, true)) return null;
return pbKey;
}
catch(Exception) { Debug.Assert(false); }
return null;
}
string strHex = StrUtil.Utf8.GetString(pbFileData);
return MemUtil.HexStringToByteArray(strHex);
}
catch (Exception) { Debug.Assert(false); }
return null;
}
/// <summary>
/// Create a new, random key-file.
/// </summary>

View File

@@ -118,5 +118,10 @@ namespace keepass2android
sendIntent.SetType("text/plain");
ctx.StartActivity(Intent.CreateChooser(sendIntent, "Send log to..."));
}
}
public static void LogTask(object task, string activityName)
{
Log($"Task in activity {activityName} changed to {task?.GetType()?.Name ?? "null"}");
}
}
}

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

@@ -37,7 +37,7 @@ namespace KeePassLib.Utility
/// </summary>
public static class MemUtil
{
internal static readonly byte[] EmptyByteArray = new byte[0];
public static readonly byte[] EmptyByteArray = new byte[0];
private static readonly uint[] m_vSBox = new uint[256] {
0xCD2FACB3, 0xE78A7F5C, 0x6F0803FC, 0xBCF6E230,
@@ -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

@@ -0,0 +1,290 @@
/*
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.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using KeePassLib.Delegates;
using KeePassLib.Interfaces;
using KeePassLib.Serialization;
namespace KeePassLib.Utility
{
public static class XmlUtilEx
{
public static XmlDocument CreateXmlDocument()
{
XmlDocument d = new XmlDocument();
// .NET 4.5.2 and newer do not resolve external XML resources
// by default; for older .NET versions, we explicitly
// prevent resolving
d.XmlResolver = null; // Default in old .NET: XmlUrlResolver object
return d;
}
public static XmlReaderSettings CreateXmlReaderSettings()
{
XmlReaderSettings xrs = new XmlReaderSettings();
xrs.CloseInput = false;
xrs.IgnoreComments = true;
xrs.IgnoreProcessingInstructions = true;
xrs.IgnoreWhitespace = true;
#if KeePassUAP
xrs.DtdProcessing = DtdProcessing.Prohibit;
#else
// Also see PrepMonoDev.sh script
xrs.ProhibitDtd = true; // Obsolete in .NET 4, but still there
// xrs.DtdProcessing = DtdProcessing.Prohibit; // .NET 4 only
#endif
xrs.ValidationType = ValidationType.None;
xrs.XmlResolver = null;
return xrs;
}
public static XmlReader CreateXmlReader(Stream s)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
return XmlReader.Create(s, CreateXmlReaderSettings());
}
public static XmlWriterSettings CreateXmlWriterSettings()
{
XmlWriterSettings xws = new XmlWriterSettings();
xws.CloseOutput = false;
xws.Encoding = StrUtil.Utf8;
xws.Indent = true;
xws.IndentChars = "\t";
xws.NewLineOnAttributes = false;
return xws;
}
public static XmlWriter CreateXmlWriter(Stream s)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
return XmlWriter.Create(s, CreateXmlWriterSettings());
}
public static T Deserialize<T>(Stream s)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
XmlSerializer xs = new XmlSerializer(typeof(T));
T t = default(T);
using(XmlReader xr = CreateXmlReader(s))
{
t = (T)xs.Deserialize(xr);
}
return t;
}
public static void Serialize<T>(Stream s, T t)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
XmlSerializer xs = new XmlSerializer(typeof(T));
using(XmlWriter xw = CreateXmlWriter(s))
{
xs.Serialize(xw, t);
}
}
internal static void Serialize<T>(Stream s, T t, bool bRemoveXsdXsi)
{
// One way to remove the "xsd" and "xsi" namespace declarations
// is to use an XmlSerializerNamespaces object containing only
// a ""/"" pair; this seems to work, but Microsoft's
// documentation explicitly states that it isn't supported:
// https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlserializernamespaces
// There are other, more complex ways, but these either rely on
// undocumented details or require the type T to be modified.
string str;
using(MemoryStream ms = new MemoryStream())
{
Serialize<T>(ms, t);
str = StrUtil.Utf8.GetString(ms.ToArray());
}
Func<string, string, bool> fFindPfx = delegate(string strText, string strSub)
{
int i = strText.IndexOf(strSub, StringComparison.Ordinal);
if(i < 0) return false;
if(i == 0) return true;
return char.IsWhiteSpace(strText[i - 1]);
};
if(bRemoveXsdXsi)
{
if(!fFindPfx(str, "xsd:") && !fFindPfx(str, "xsi:"))
{
Debug.Assert(str.IndexOf("xmlns:xsd") > 0);
str = str.Replace(" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"", string.Empty);
Debug.Assert(str.IndexOf("xmlns:xsd") < 0);
Debug.Assert(str.IndexOf("xmlns:xsi") > 0);
str = str.Replace(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"", string.Empty);
Debug.Assert(str.IndexOf("xmlns:xsi") < 0);
}
else { Debug.Assert(false); } // "xsd"/"xsi" decl. may be required
}
MemUtil.Write(s, StrUtil.Utf8.GetBytes(str));
}
#if DEBUG
internal static void ValidateXml(string strXml, bool bReplaceStdEntities)
{
if(strXml == null) throw new ArgumentNullException("strXml");
if(strXml.Length == 0) { Debug.Assert(false); return; }
string str = strXml;
if(bReplaceStdEntities)
str = str.Replace("&nbsp;", "&#160;");
XmlDocument d = new XmlDocument();
d.LoadXml(str);
}
#endif
internal static XPathNodeIterator FindNodes(PwDatabase pd, string strXPath,
IStatusLogger sl, out XmlDocument xd)
{
if(pd == null) throw new ArgumentNullException("pd");
if(strXPath == null) { Debug.Assert(false); strXPath = string.Empty; }
KdbxFile kdbx = new KdbxFile(pd);
byte[] pbXml;
using(MemoryStream ms = new MemoryStream())
{
kdbx.Save(ms, null, KdbxFormat.PlainXml, sl);
pbXml = ms.ToArray();
}
string strXml = StrUtil.Utf8.GetString(pbXml);
xd = CreateXmlDocument();
xd.LoadXml(strXml);
XPathNavigator xpNav = xd.CreateNavigator();
return xpNav.Select(strXPath);
// XPathExpression xpExpr = xpNav.Compile(strXPath);
// xpExpr.SetContext(new XuXsltContext());
// return xpNav.Select(xpExpr);
}
/* private sealed class XuFnMatches : IXsltContextFunction
{
private readonly XPathResultType[] m_vArgTypes = new XPathResultType[] {
XPathResultType.String, XPathResultType.String, XPathResultType.String
};
public XPathResultType[] ArgTypes { get { return m_vArgTypes; } }
public int Maxargs { get { return 3; } }
public int Minargs { get { return 2; } }
public XPathResultType ReturnType { get { return XPathResultType.Boolean; } }
private static string GetArgString(object[] args, int i, string strDefault)
{
if(args == null) { Debug.Assert(false); return strDefault; }
if(i >= args.Length) return strDefault;
object o = args[i];
if(o == null) return strDefault;
XPathNodeIterator it = (o as XPathNodeIterator);
if(it != null) o = it.Current.Value;
return (o.ToString() ?? strDefault);
}
public object Invoke(XsltContext xsltContext, object[] args,
XPathNavigator docContext)
{
string strInput = GetArgString(args, 0, string.Empty);
string strPattern = GetArgString(args, 1, string.Empty);
string strFlags = GetArgString(args, 2, null);
RegexOptions ro = RegexOptions.None;
if(!string.IsNullOrEmpty(strFlags))
{
if(strFlags.IndexOf('s') >= 0) ro |= RegexOptions.Singleline;
if(strFlags.IndexOf('m') >= 0) ro |= RegexOptions.Multiline;
if(strFlags.IndexOf('i') >= 0) ro |= RegexOptions.IgnoreCase;
if(strFlags.IndexOf('x') >= 0) ro |= RegexOptions.IgnorePatternWhitespace;
}
return Regex.IsMatch(strInput, strPattern, ro);
}
}
private sealed class XuXsltContext : XsltContext
{
public override bool Whitespace { get { return false; } }
public override int CompareDocument(string baseUri, string nextbaseUri)
{
return string.CompareOrdinal(baseUri, nextbaseUri);
}
public override bool PreserveWhitespace(XPathNavigator node)
{
return false;
}
public override IXsltContextFunction ResolveFunction(string prefix,
string name, XPathResultType[] ArgTypes)
{
if(prefix != "kp") { Debug.Assert(false); return null; }
if(name == "matches") return new XuFnMatches();
Debug.Assert(false);
return null;
}
public override IXsltContextVariable ResolveVariable(string prefix,
string name)
{
Debug.Assert(false);
return null;
}
} */
}
}

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

@@ -120,10 +120,14 @@ namespace keepass2android.Io
public bool IsCached(IOConnectionInfo ioc)
{
return File.Exists(CachedFilePath(ioc))
bool result = File.Exists(CachedFilePath(ioc))
&& File.Exists(VersionFilePath(ioc))
&& File.Exists(BaseVersionFilePath(ioc));
}
Kp2aLog.Log(ioc.GetDisplayName() + " isCached = " + result);
return result;
}
public void Delete(IOConnectionInfo ioc)
{
@@ -593,11 +597,15 @@ namespace keepass2android.Io
public string GetBaseVersionHash(IOConnectionInfo ioc)
{
return File.ReadAllText(BaseVersionFilePath(ioc));
}
string hash = File.ReadAllText(BaseVersionFilePath(ioc));
Kp2aLog.Log(ioc.GetDisplayName() + " baseVersionHash = " + hash);
return hash;
}
public string GetLocalVersionHash(IOConnectionInfo ioc)
{
return File.ReadAllText(VersionFilePath(ioc));
string hash = File.ReadAllText(VersionFilePath(ioc));
Kp2aLog.Log(ioc.GetDisplayName() + " localVersionHash = " + hash);
return hash;
}
public bool HasLocalChanges(IOConnectionInfo ioc)
{

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

@@ -31,7 +31,7 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;NoNet;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<JavaMaximumHeapSize>4G</JavaMaximumHeapSize>
@@ -54,6 +54,7 @@
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
@@ -77,13 +78,23 @@
<Compile Include="Io\AndroidContentStorage.cs" />
<Compile Include="Io\BuiltInFileStorage.cs" />
<Compile Include="Io\CachingFileStorage.cs" />
<Compile Include="Io\DropboxFileStorage.cs" />
<Compile Include="Io\DropboxFileStorageKeys.cs" />
<Compile Include="Io\FileDescription.cs" />
<Compile Include="Io\FileStorageSetupActivity.cs" />
<Compile Include="Io\FileStorageSetupInitiatorActivity.cs" />
<Compile Include="Io\GDriveFileStorage.cs" />
<Compile Include="Io\IFileStorage.cs" />
<Compile Include="Io\IoUtil.cs" />
<Compile Include="Io\JavaFileStorage.cs" />
<Compile Include="Io\NetFtpFileStorage.cs" />
<Compile Include="Io\OfflineSwitchableFileStorage.cs" />
<Compile Include="Io\OneDrive2FileStorage.cs" />
<Compile Include="Io\OneDrive2PrefixContainer.cs" />
<Compile Include="Io\PCloudFileStorage.cs" />
<Compile Include="Io\SftpFileStorage.cs" />
<Compile Include="Io\OneDriveFileStorage.cs" />
<Compile Include="Io\WebDavFileStorage.cs" />
<Compile Include="IProgressDialog.cs" />
<Compile Include="PreferenceKey.cs" />
<Compile Include="SelectStorageLocationActivityBase.cs" />
@@ -119,6 +130,14 @@
<Compile Include="Utils\Spr\SprEngine.PickChars.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AndroidFileChooserBinding\AndroidFileChooserBinding.csproj">
<Project>{3c0f7fe5-639f-4422-a087-8b26cf862d1b}</Project>
<Name>AndroidFileChooserBinding</Name>
</ProjectReference>
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
<Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
<Name>JavaFileStorageBindings</Name>
</ProjectReference>
<ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj">
<Project>{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}</Project>
<Name>KeePassLib2Android</Name>
@@ -127,7 +146,10 @@
<Project>{70D3844A-D9FA-4A64-B205-A84C6A822196}</Project>
<Name>KP2AKdbLibraryBinding</Name>
</ProjectReference>
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj">
<Project>{2db80c77-d46f-4970-b967-e9ffa9b2ac2e}</Project>
<Name>PCloudBindings</Name>
</ProjectReference>
<ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj">
<Project>{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}</Project>
<Name>TwofishCipher</Name>
@@ -140,8 +162,20 @@
<None Include="app.config" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentFTP">
<Version>31.3.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.Graph">
<Version>1.21.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Graph.Auth">
<Version>1.0.0-preview.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.Identity.Client">
<Version>4.8.2</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Arch.Core.Common">
<Version>1.1.1.1</Version>
<Version>1.1.1.3</Version>
</PackageReference>
<PackageReference Include="Xamarin.Android.Arch.Core.Runtime">
<Version>1.1.1.3</Version>

File diff suppressed because it is too large Load Diff

View File

@@ -43,17 +43,21 @@ namespace keepass2android
try
{
remoteData = cachingFileStorage.GetRemoteDataAndHash(ioc, out hash);
Kp2aLog.Log("Checking for file change. Current hash = " + hash);
}
catch (FileNotFoundException)
{
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.RestoringRemoteFile));
cachingFileStorage.UpdateRemoteFile(ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions));
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
Kp2aLog.Log("Checking for file change: file not found");
return;
}
//check if remote file was modified:
if (cachingFileStorage.GetBaseVersionHash(ioc) != hash)
var baseVersionHash = cachingFileStorage.GetBaseVersionHash(ioc);
Kp2aLog.Log("Checking for file change. baseVersionHash = " + baseVersionHash);
if (baseVersionHash != hash)
{
//remote file is modified
if (cachingFileStorage.HasLocalChanges(ioc))

View File

@@ -116,14 +116,18 @@ namespace keepass2android
return;
}
}
if (
(_streamForOrigFile != null)
|| fileStorage.CheckForFileChangeFast(ioc, _db.LastFileVersion) //first try to use the fast change detection
|| (FileHashChanged(ioc, _db.KpDatabase.HashOfFileOnDisk) == FileHashChange.Changed) //if that fails, hash the file and compare:
)
bool hasStreamForOrigFile = (_streamForOrigFile != null);
bool hasChangeFast = hasStreamForOrigFile ||
fileStorage.CheckForFileChangeFast(ioc, _db.LastFileVersion); //first try to use the fast change detection;
bool hasHashChanged = hasChangeFast ||
(FileHashChanged(ioc, _db.KpDatabase.HashOfFileOnDisk) ==
FileHashChange.Changed); //if that fails, hash the file and compare:
if (hasHashChanged)
{
Kp2aLog.Log("Conflict. " + hasStreamForOrigFile + " " + hasChangeFast + " " + hasHashChanged);
//ask user...
_app.AskYesNoCancel(UiStringKey.TitleSyncQuestion, UiStringKey.MessageSyncQuestion,

Binary file not shown.

Binary file not shown.

View File

@@ -55,7 +55,7 @@
<ItemGroup>
<None Include="Jars\AboutJars.txt" />
<None Include="Additions\AboutAdditions.txt" />
<LibraryProjectZip Include="Jars\pcloud-sdk-android-1.1.0.aar" />
<LibraryProjectZip Include="Jars\pcloud-sdk-android-1.2.0.aar" />
</ItemGroup>
<ItemGroup>
<TransformFile Include="Transforms\Metadata.xml" />
@@ -71,6 +71,6 @@
</Target>
-->
<ItemGroup>
<EmbeddedReferenceJar Include="Jars\pcloud-sdk-java-core-1.1.0.jar" />
<EmbeddedReferenceJar Include="Jars\pcloud-sdk-java-core-1.2.0.jar" />
</ItemGroup>
</Project>

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

@@ -6,11 +6,11 @@ if exist "DropboxFileStorageKeys.cs" (
)
cd ..\..\keepass2android
call UseManifestNoNet.bat
call UseManifestDebug.bat
cd ..
call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64
msbuild KeePass.sln /target:keepass2android /p:BuildProjectReferences=true /p:Configuration="Release" /p:Platform="Any CPU"
msbuild KeePass.sln /target:keepass2android /p:BuildProjectReferences=true /p:Configuration="Debug" /p:Platform="Any CPU"
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,13 +35,13 @@ 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
}
compile 'com.pcloud.sdk:java-core:1.1.0'
compile 'com.pcloud.sdk:android:1.1.0'
compile 'com.pcloud.sdk:java-core:1.2.0'
compile 'com.pcloud.sdk:android:1.2.0'
compile 'com.google.code.gson:gson:2.3.1'
compile 'com.microsoft.services.msa:msa-auth:0.8.6'
compile 'com.microsoft.aad:adal:1.14.0'

View File

@@ -6,7 +6,6 @@
android:versionName="1.0">
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="14" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2006-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2005-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2013-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2004-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2004-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2004-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2004-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -93,7 +93,13 @@ class IdentityFile implements Identity{
* @return "ssh-rsa" or "ssh-dss"
*/
public String getAlgName(){
return new String(kpair.getKeyTypeName());
byte[] name = kpair.getKeyTypeName();
try {
return new String(name, "UTF-8");
}
catch (UnsupportedEncodingException e){
return new String(name);
}
}
/**

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2012-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -66,9 +66,9 @@ public class JSch{
config.put("diffie-hellman-group-exchange-sha256",
"com.jcraft.jsch.DHGEX256"); // available since JDK1.4.2.
// On JDK8, 2048bits will be used.
config.put("ecdsa-sha2-nistp256", "com.jcraft.jsch.jce.SignatureECDSA");
config.put("ecdsa-sha2-nistp384", "com.jcraft.jsch.jce.SignatureECDSA");
config.put("ecdsa-sha2-nistp521", "com.jcraft.jsch.jce.SignatureECDSA");
config.put("ecdsa-sha2-nistp256", "com.jcraft.jsch.jce.SignatureECDSA256");
config.put("ecdsa-sha2-nistp384", "com.jcraft.jsch.jce.SignatureECDSA384");
config.put("ecdsa-sha2-nistp521", "com.jcraft.jsch.jce.SignatureECDSA521");
config.put("ecdh-sha2-nistp256", "com.jcraft.jsch.DHEC256");
config.put("ecdh-sha2-nistp384", "com.jcraft.jsch.DHEC384");
@@ -94,7 +94,6 @@ public class JSch{
config.put("md5", "com.jcraft.jsch.jce.MD5");
config.put("signature.dss", "com.jcraft.jsch.jce.SignatureDSA");
config.put("signature.rsa", "com.jcraft.jsch.jce.SignatureRSA");
config.put("signature.ecdsa", "com.jcraft.jsch.jce.SignatureECDSA");
config.put("keypairgen.dsa", "com.jcraft.jsch.jce.KeyPairGenDSA");
config.put("keypairgen.rsa", "com.jcraft.jsch.jce.KeyPairGenRSA");
config.put("keypairgen.ecdsa", "com.jcraft.jsch.jce.KeyPairGenECDSA");

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -301,7 +301,7 @@ public abstract class KeyExchange{
SignatureECDSA sig=null;
try{
Class c=Class.forName(session.getConfig("signature.ecdsa"));
Class c=Class.forName(session.getConfig(alg));
sig=(SignatureECDSA)(c.newInstance());
sig.init();
}

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -599,7 +599,7 @@ public abstract class KeyPair{
}
else if(_type.equals("ecdsa-sha2-nistp256") ||
_type.equals("ecdsa-sha2-nistp384") ||
_type.equals("ecdsa-sha2-nistp512")){
_type.equals("ecdsa-sha2-nistp521")){
kpair=KeyPairECDSA.fromSSHAgent(jsch, buf);
}
else{
@@ -924,7 +924,7 @@ public abstract class KeyPair{
KeyPair kpair=null;
if(type==DSA){ kpair=new KeyPairDSA(jsch); }
else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
else if(type==ECDSA){ kpair=new KeyPairECDSA(jsch); }
else if(type==ECDSA){ kpair=new KeyPairECDSA(jsch, pubkey); }
else if(vendor==VENDOR_PKCS8){ kpair = new KeyPairPKCS8(jsch); }
if(kpair!=null){

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -55,6 +55,23 @@ public class KeyPairECDSA extends KeyPair{
this(jsch, null, null, null, null);
}
public KeyPairECDSA(JSch jsch , byte[] pubkey){
this(jsch, null, null, null, null);
if(pubkey!=null){
byte[] name = new byte[8];
System.arraycopy(pubkey, 11, name, 0, 8);
if(Util.array_equals(name, Util.str2byte("nistp384"))){
key_size=384;
this.name=name;
}
if(Util.array_equals(name, Util.str2byte("nistp521"))){
key_size=521;
this.name=name;
}
}
}
public KeyPairECDSA(JSch jsch,
byte[] name,
byte[] r_array,
@@ -283,8 +300,8 @@ public class KeyPairECDSA extends KeyPair{
}
public byte[] getSignature(byte[] data){
try{
Class c=Class.forName((String)jsch.getConfig("signature.ecdsa"));
try{
Class c=Class.forName((String)jsch.getConfig("ecdsa-sha2-"+new String(name)));
SignatureECDSA ecdsa=(SignatureECDSA)(c.newInstance());
ecdsa.init();
ecdsa.setPrvKey(prv_array);
@@ -304,8 +321,8 @@ public class KeyPairECDSA extends KeyPair{
}
public Signature getVerifier(){
try{
Class c=Class.forName((String)jsch.getConfig("signature.ecdsa"));
try{
Class c=Class.forName((String)jsch.getConfig("ecdsa-sha2-"+new String(name)));
final SignatureECDSA ecdsa=(SignatureECDSA)(c.newInstance());
ecdsa.init();

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@@ -1,6 +1,6 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

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