Compare commits
1511 Commits
1.08d-r5
...
update-lib
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
830494851d | ||
|
|
6e30dd35ee | ||
|
|
dad9b0e53f | ||
|
|
b2b0b8ddc9 | ||
|
|
9494f28acf | ||
|
|
a98cd33cff | ||
|
|
65092dcccf | ||
|
|
e464c59796 | ||
|
|
7dcccf1105 | ||
|
|
8d354bb3f0 | ||
|
|
5882263ee8 | ||
|
|
38e305bb4a | ||
|
|
177b1fc9a3 | ||
|
|
8bde5ed262 | ||
|
|
247b37262d | ||
|
|
84230b32ef | ||
|
|
a54444e919 | ||
|
|
4b55e2ce8b | ||
|
|
5ae718c58e | ||
|
|
669022ddf0 | ||
|
|
f0fd21a6ec | ||
|
|
73a8ec27d4 | ||
|
|
51902c9bc4 | ||
|
|
32d3abf5d6 | ||
|
|
543eeb69ef | ||
|
|
ec6e1593b0 | ||
|
|
ee27bea111 | ||
|
|
de93b12877 | ||
|
|
71d2a332c7 | ||
|
|
37d1481658 | ||
|
|
a8cc1ad66d | ||
|
|
3056ee9d45 | ||
|
|
0ec5996c8d | ||
|
|
01666d8402 | ||
|
|
422fa01ba1 | ||
|
|
ca9c19e8a9 | ||
|
|
093f7683b8 | ||
|
|
d4cf908f4a | ||
|
|
55d62144fb | ||
|
|
c181565aaa | ||
|
|
c0c4524562 | ||
|
|
eac1803084 | ||
|
|
58904f1166 | ||
|
|
54eb1baee2 | ||
|
|
ada01924ba | ||
|
|
e38e7df221 | ||
|
|
fb4ab84ceb | ||
|
|
4475fac51e | ||
|
|
13ef4ca9ff | ||
|
|
f297ebcd40 | ||
|
|
a5ef4ccc7a | ||
|
|
dfd9c32251 | ||
|
|
5397a1c88f | ||
|
|
fdcd4321e0 | ||
|
|
11013791ef | ||
|
|
6958a2d189 | ||
|
|
94ec8cf1ac | ||
|
|
63631fa81f | ||
|
|
107d9c6235 | ||
|
|
8e4ee4f588 | ||
|
|
f0a06faae1 | ||
|
|
99db263833 | ||
|
|
e2e42cd177 | ||
|
|
a376c6ee0b | ||
|
|
0326e02ddd | ||
|
|
d75482f3bd | ||
|
|
bca0d042a1 | ||
|
|
29eaf5f205 | ||
|
|
78da5e2973 | ||
|
|
20541618f9 | ||
|
|
e459d280f2 | ||
|
|
705e2e4a86 | ||
|
|
cf9b368afc | ||
|
|
f1c6a5365c | ||
|
|
6a61bf6364 | ||
|
|
814571c746 | ||
|
|
fc5587260f | ||
|
|
674ba7bd71 | ||
|
|
556f82f786 | ||
|
|
58c5c5882b | ||
|
|
a706571e66 | ||
|
|
3997b21aec | ||
|
|
c354612369 | ||
|
|
4fea731c87 | ||
|
|
e189776ba9 | ||
|
|
31255f0c52 | ||
|
|
059280efd0 | ||
|
|
5edc070aa8 | ||
|
|
95843b1134 | ||
|
|
d2ea9b18a8 | ||
|
|
828425ab0e | ||
|
|
4395f422b3 | ||
|
|
be2c28811c | ||
|
|
fcc4d44786 | ||
|
|
337e6324ff | ||
|
|
310143c612 | ||
|
|
49cb33a4da | ||
|
|
c934755e1c | ||
|
|
64355a3da7 | ||
|
|
9579f3bf51 | ||
|
|
674ae26bd7 | ||
|
|
d2778e8496 | ||
|
|
cc19e6f326 | ||
|
|
dd7a2718c9 | ||
|
|
9c50d2d98a | ||
|
|
8ee13acdde | ||
|
|
aeda304919 | ||
|
|
9f08e0039b | ||
|
|
dfd101da77 | ||
|
|
f4b5eee171 | ||
|
|
98e31942e1 | ||
|
|
385aad8fb0 | ||
|
|
3e383d50f8 | ||
|
|
8c089a8711 | ||
|
|
098123787d | ||
|
|
a22b8474c3 | ||
|
|
8fc4607a34 | ||
|
|
4b56405960 | ||
|
|
c4b41001b3 | ||
|
|
cb51be349e | ||
|
|
13170bb88c | ||
|
|
88a20d947c | ||
|
|
4fcc2625c0 | ||
|
|
324cf74a2b | ||
|
|
e3d14221f9 | ||
|
|
7d0a43397a | ||
|
|
a6d1b26479 | ||
|
|
79ad753218 | ||
|
|
24ee49ea9f | ||
|
|
ab6e8e3685 | ||
|
|
81e8820732 | ||
|
|
93c72ee04e | ||
|
|
8a53357e3d | ||
|
|
c12ae13077 | ||
|
|
071fc3fd51 | ||
|
|
ad6ced3aad | ||
|
|
6ef8b8fc3b | ||
|
|
825793f385 | ||
|
|
bd6af10fd5 | ||
|
|
2e9400cf4d | ||
|
|
c719043159 | ||
|
|
c6e32937ce | ||
|
|
081b77c2bd | ||
|
|
a950298c11 | ||
|
|
eafd3bb702 | ||
|
|
0e53f91d01 | ||
|
|
96156bf8b9 | ||
|
|
8efc1f3c1f | ||
|
|
67d20124ff | ||
|
|
b93739926d | ||
|
|
4afac75bb4 | ||
|
|
cea41d1446 | ||
|
|
dd5b744bfb | ||
|
|
47cbe5b0ab | ||
|
|
99950e3c93 | ||
|
|
df8f375b59 | ||
|
|
fdc213bfc1 | ||
|
|
428b008017 | ||
|
|
3d4a0a79f9 | ||
|
|
008e55598c | ||
|
|
7c3832830e | ||
|
|
c3e234da25 | ||
|
|
6ba0b29c77 | ||
|
|
b16f747913 | ||
|
|
35ac1cf642 | ||
|
|
5d2da784b9 | ||
|
|
86b225034c | ||
|
|
727cf74201 | ||
|
|
6f8ae7be34 | ||
|
|
a84d26b151 | ||
|
|
6f7419e38a | ||
|
|
bdb11f4873 | ||
|
|
87603cd9c1 | ||
|
|
e6cec96504 | ||
|
|
fb478af6c7 | ||
|
|
9991964c9b | ||
|
|
d761f07fc9 | ||
|
|
b18515dd8c | ||
|
|
2677cae5e6 | ||
|
|
cb832c412f | ||
|
|
97018b15f7 | ||
|
|
6b06d4ba8d | ||
|
|
902fc6f6d3 | ||
|
|
d4fd8db455 | ||
|
|
9f1be03dc4 | ||
|
|
7b863e115f | ||
|
|
06fa5a5fcd | ||
|
|
b1837468d7 | ||
|
|
0ffe6cda16 | ||
|
|
0ba1e946d1 | ||
|
|
ba2890cc80 | ||
|
|
ee9750e689 | ||
|
|
3f358fed38 | ||
|
|
4eb7b4519e | ||
|
|
9b61f651c4 | ||
|
|
c716fa0c12 | ||
|
|
013d69b520 | ||
|
|
fec2875e6a | ||
|
|
1a1036f7b8 | ||
|
|
b9fcf8deda | ||
|
|
a9a88dbdbe | ||
|
|
d3f505fb55 | ||
|
|
da10ebd2f4 | ||
|
|
daeee50e09 | ||
|
|
f602367a6c | ||
|
|
db2ad49f36 | ||
|
|
a782843b29 | ||
|
|
e35babb8eb | ||
|
|
e6b296c0b9 | ||
|
|
44692afa98 | ||
|
|
491912a6ab | ||
|
|
5cb02e88bf | ||
|
|
69ce92a7b7 | ||
|
|
a09e2656be | ||
|
|
445923e12c | ||
|
|
6499c97206 | ||
|
|
41ef1900a1 | ||
|
|
290f61d114 | ||
|
|
11f45c61e8 | ||
|
|
ac6df5d10f | ||
|
|
206ab3ac42 | ||
|
|
33847deb00 | ||
|
|
baf9a29646 | ||
|
|
30d45e086c | ||
|
|
66166e44a0 | ||
|
|
8ace491d84 | ||
|
|
39deef4053 | ||
|
|
1faa0b06bd | ||
|
|
1eb1e1cb2b | ||
|
|
d551969b04 | ||
|
|
0f0c1ddbfd | ||
|
|
b46f2984a3 | ||
|
|
cce1e2794e | ||
|
|
c19b8d2238 | ||
|
|
141d2f3ddb | ||
|
|
3d2ae980b7 | ||
|
|
a8f4fcde7b | ||
|
|
add8b2dad6 | ||
|
|
50074d547f | ||
|
|
e6b425a30e | ||
|
|
9af9d34d87 | ||
|
|
4fee92f591 | ||
|
|
1b658f1c39 | ||
|
|
4dbd33ba97 | ||
|
|
f8f18152c3 | ||
|
|
dbf5e46e94 | ||
|
|
a23c1a2360 | ||
|
|
d2bd91ba6a | ||
|
|
95352ef0ee | ||
|
|
24b8c27d26 | ||
|
|
191b90d974 | ||
|
|
21db4b612b | ||
|
|
a23101b812 | ||
|
|
8042470488 | ||
|
|
4bbec4367f | ||
|
|
4b583cc0c0 | ||
|
|
cd07de56df | ||
|
|
962c4dbf63 | ||
|
|
09b56d85cf | ||
|
|
8281888608 | ||
|
|
44d9456e20 | ||
|
|
7b01e4494f | ||
|
|
fda68a1114 | ||
|
|
4849c089b3 | ||
|
|
580668c5cb | ||
|
|
da8f1122e8 | ||
|
|
7d44518ac7 | ||
|
|
1a2c1267c4 | ||
|
|
85e0fe487f | ||
|
|
fa9a9f2602 | ||
|
|
5c7d626f4b | ||
|
|
b3ce9c64b1 | ||
|
|
dc809941e8 | ||
|
|
b7d69c33c8 | ||
|
|
de765f3451 | ||
|
|
1581d79666 | ||
|
|
297fa267e5 | ||
|
|
77e2d67b6c | ||
|
|
a3ba2d8367 | ||
|
|
48d59aa0f6 | ||
|
|
cff6595b79 | ||
|
|
798f633af7 | ||
|
|
f5681c4e62 | ||
|
|
690de2761c | ||
|
|
92238436d5 | ||
|
|
9282e80938 | ||
|
|
588e203442 | ||
|
|
7e96055e0b | ||
|
|
c90d623d15 | ||
|
|
86a03d8b9a | ||
|
|
17f7d1b8eb | ||
|
|
d3fecaf4e3 | ||
|
|
03dee4f262 | ||
|
|
2b502df566 | ||
|
|
9ea064108c | ||
|
|
682736d119 | ||
|
|
150bd336d8 | ||
|
|
d13ee3d2ca | ||
|
|
99b5df4c94 | ||
|
|
9a70442d69 | ||
|
|
cd04050e57 | ||
|
|
c13bb15fc0 | ||
|
|
84939c70e1 | ||
|
|
2b108d9818 | ||
|
|
06f338fdd5 | ||
|
|
fa5e8c1656 | ||
|
|
3652e2ee25 | ||
|
|
e8f3eb1bc8 | ||
|
|
233f612479 | ||
|
|
dc1e790ab5 | ||
|
|
bab77538c9 | ||
|
|
09165af0a8 | ||
|
|
4502d3d2bf | ||
|
|
eb03d448d8 | ||
|
|
7798ec8454 | ||
|
|
7424bb324f | ||
|
|
b18432add6 | ||
|
|
e9a66d688c | ||
|
|
d9add0d5f6 | ||
|
|
aed00420fc | ||
|
|
8dc546e640 | ||
|
|
c8f3d5f3e2 | ||
|
|
1f3786189b | ||
|
|
d7bdde0585 | ||
|
|
f213f05477 | ||
|
|
cb73144da7 | ||
|
|
689a1710c4 | ||
|
|
da116bbb4d | ||
|
|
2fd76ad28f | ||
|
|
bdc7bf9cf6 | ||
|
|
b3ef4f817a | ||
|
|
3fb2f2e858 | ||
|
|
d8f60aa7f1 | ||
|
|
31f3a30a54 | ||
|
|
474b90f331 | ||
|
|
fa0a52b328 | ||
|
|
ccb6ece463 | ||
|
|
ffa33ed190 | ||
|
|
c4923c57bf | ||
|
|
2602bf7bee | ||
|
|
7df86fd134 | ||
|
|
cf2f57b372 | ||
|
|
7c2500af63 | ||
|
|
748a71bc03 | ||
|
|
e3ae3233fe | ||
|
|
bc464b0eba | ||
|
|
9b3d7250ec | ||
|
|
b04f7f6c81 | ||
|
|
41151a184b | ||
|
|
9d9b24cb98 | ||
|
|
087e3f5931 | ||
|
|
c8abb4d76a | ||
|
|
18f81e6927 | ||
|
|
b8c094554a | ||
|
|
1c6831bb78 | ||
|
|
a5e7bbc081 | ||
|
|
be2218afcc | ||
|
|
32c1d2a379 | ||
|
|
9c7182f85a | ||
|
|
100ed6e58e | ||
|
|
31abf68031 | ||
|
|
a5bce53a12 | ||
|
|
489ed8e2b4 | ||
|
|
d63e11b307 | ||
|
|
c9be806b01 | ||
|
|
0e9da69f47 | ||
|
|
18ecfd5396 | ||
|
|
0fef5f0f8c | ||
|
|
83529dd3b5 | ||
|
|
9204c4ca8f | ||
|
|
46fdba1bfa | ||
|
|
006f5497e5 | ||
|
|
da3665c25b | ||
|
|
464fe43323 | ||
|
|
bded2394bb | ||
|
|
0fe2ca8238 | ||
|
|
ae33ca219f | ||
|
|
c16eeff130 | ||
|
|
fb0f83c37a | ||
|
|
da5533ef3b | ||
|
|
03ea073426 | ||
|
|
681dfb6ded | ||
|
|
cde5d31845 | ||
|
|
20f334f0d3 | ||
|
|
d8268d4f0f | ||
|
|
325e8a8e32 | ||
|
|
7e9e91da05 | ||
|
|
80eaf39f04 | ||
|
|
ddffdb48aa | ||
|
|
85709e4058 | ||
|
|
5a406fe5df | ||
|
|
4e2603ae27 | ||
|
|
bcf980eed5 | ||
|
|
05c94a3af8 | ||
|
|
3526aa1889 | ||
|
|
72a3b55341 | ||
|
|
b11d5e667e | ||
|
|
93a4529fe9 | ||
|
|
7582274903 | ||
|
|
158349c005 | ||
|
|
2fffe5988c | ||
|
|
3f6e51b126 | ||
|
|
c0345d1309 | ||
|
|
14f7e17fa4 | ||
|
|
05acba4309 | ||
|
|
746dcd4c6b | ||
|
|
37a6da5a3b | ||
|
|
1efe2e16a5 | ||
|
|
542984ca2f | ||
|
|
f8746f69f8 | ||
|
|
5cbddb4fcc | ||
|
|
b3a73f20d4 | ||
|
|
53913e66ab | ||
|
|
badf99c20d | ||
|
|
b8318f7fa5 | ||
|
|
f0e30459a2 | ||
|
|
e101ffb01e | ||
|
|
a05ef51d44 | ||
|
|
8aacdf683b | ||
|
|
0502efde14 | ||
|
|
94ede3a696 | ||
|
|
9933fa1f9d | ||
|
|
4910c73a5e | ||
|
|
cf222a2db1 | ||
|
|
a9ad3725dc | ||
|
|
40d3fe1cd9 | ||
|
|
1e90a52275 | ||
|
|
8596edaa67 | ||
|
|
38f1aa4d3d | ||
|
|
984da3fd3b | ||
|
|
ed7138991d | ||
|
|
1e78527164 | ||
|
|
a6540b4462 | ||
|
|
230b3941e8 | ||
|
|
554f88c723 | ||
|
|
4cd32d30c6 | ||
|
|
a76c43a800 | ||
|
|
d0da83182f | ||
|
|
ec5f26e0cd | ||
|
|
6110166af8 | ||
|
|
6f10a04589 | ||
|
|
eedeeafd80 | ||
|
|
e0c003fcb2 | ||
|
|
ad3b1500bb | ||
|
|
5f2a976fde | ||
|
|
dd0becdfd8 | ||
|
|
cacd204ac2 | ||
|
|
728fd2f8ae | ||
|
|
944f44bc4b | ||
|
|
58047d5386 | ||
|
|
c0a06c9f3a | ||
|
|
d0c041a0e2 | ||
|
|
df060e2f4b | ||
|
|
aea55dad45 | ||
|
|
5442dbf441 | ||
|
|
317476d9b5 | ||
|
|
ad0acb7a69 | ||
|
|
b66ae5d264 | ||
|
|
d87706fa43 | ||
|
|
cb25d12709 | ||
|
|
dce536009e | ||
|
|
656e785214 | ||
|
|
35d50a6eb0 | ||
|
|
786bb646c2 | ||
|
|
72cc6ff768 | ||
|
|
404e07e5c0 | ||
|
|
1c7159ede9 | ||
|
|
2378cd0d7c | ||
|
|
b149bab761 | ||
|
|
5ebd8e5e33 | ||
|
|
db6b266a59 | ||
|
|
7de28c5aba | ||
|
|
ed79df0c6d | ||
|
|
4949fede32 | ||
|
|
bddef6442c | ||
|
|
48a6d0a2ad | ||
|
|
ac5f3c9ca5 | ||
|
|
93e1cf1147 | ||
|
|
a805787a95 | ||
|
|
85315d0ecc | ||
|
|
595a451f77 | ||
|
|
914224e4fa | ||
|
|
e350e8788c | ||
|
|
ca5f6dc43c | ||
|
|
0d4955622d | ||
|
|
886daa6b27 | ||
|
|
8fa0803474 | ||
|
|
4cad70e750 | ||
|
|
c29b789a2b | ||
|
|
cd34896661 | ||
|
|
1e02db86d6 | ||
|
|
994741cbf5 | ||
|
|
5e265d1816 | ||
|
|
83e77b2a31 | ||
|
|
893cf2b3c8 | ||
|
|
58844be6eb | ||
|
|
2d899fa067 | ||
|
|
060bf6a6ee | ||
|
|
890f1bd704 | ||
|
|
139abcaec6 | ||
|
|
78a48b75b8 | ||
|
|
3918b06b1f | ||
|
|
40847ebe31 | ||
|
|
34cac86a9b | ||
|
|
d8598a53e0 | ||
|
|
92d9eb1512 | ||
|
|
1be7b33f6b | ||
|
|
8464fa4f29 | ||
|
|
eff9a96bd5 | ||
|
|
bd4e321b0e | ||
|
|
47aaedbfb5 | ||
|
|
3043f8981d | ||
|
|
15b3b76b27 | ||
|
|
cfd413f1f4 | ||
|
|
30df03eec6 | ||
|
|
5048f63204 | ||
|
|
7557c0b9fd | ||
|
|
9346f6bb32 | ||
|
|
23d7efff53 | ||
|
|
c7eb2bf873 | ||
|
|
632121f3ec | ||
|
|
f7feddcf1f | ||
|
|
e745fee6e2 | ||
|
|
bcc17d91bd | ||
|
|
8c8a8e3968 | ||
|
|
3c41550404 | ||
|
|
76107b1207 | ||
|
|
bb0c13b9d8 | ||
|
|
405166ba9d | ||
|
|
f5cb60770e | ||
|
|
9958a73d4a | ||
|
|
fd287b8da7 | ||
|
|
9bea5b13e3 | ||
|
|
aa6a728e8c | ||
|
|
1c8431a3f9 | ||
|
|
6a0eacd8f1 | ||
|
|
d27976b737 | ||
|
|
f312b50f0c | ||
|
|
deb169fece | ||
|
|
2df656211d | ||
|
|
c86f9b3a4f | ||
|
|
1c517dceb8 | ||
|
|
bede68b60a | ||
|
|
8ab7fe84dd | ||
|
|
305946b552 | ||
|
|
f1fdfa5205 | ||
|
|
c1f7460c3e | ||
|
|
1f92e449f5 | ||
|
|
50aec43b38 | ||
|
|
dc75565d7c | ||
|
|
91b59c7c9f | ||
|
|
e42b4c9654 | ||
|
|
d6486d4b98 | ||
|
|
5bdf93633f | ||
|
|
81f582bb8b | ||
|
|
7ba6a44888 | ||
|
|
187d4c8f41 | ||
|
|
58b0b666ce | ||
|
|
8393694e6c | ||
|
|
b99f82d3d7 | ||
|
|
dfbfeb3fa3 | ||
|
|
3c9a8341ae | ||
|
|
8f69a60307 | ||
|
|
14c2e64e2f | ||
|
|
554338012e | ||
|
|
3185cf1a9c | ||
|
|
ed91d0db0b | ||
|
|
dd7cb0a088 | ||
|
|
874e1dbae8 | ||
|
|
649c0739f6 | ||
|
|
3db5ea4713 | ||
|
|
96b5e1c10e | ||
|
|
2bf5222505 | ||
|
|
24d6909297 | ||
|
|
1c3f4106dd | ||
|
|
4b3f6657a8 | ||
|
|
fd75737331 | ||
|
|
195cdb8ad7 | ||
|
|
a8e519660b | ||
|
|
54047e8a26 | ||
|
|
e84c5bacb1 | ||
|
|
48037d667c | ||
|
|
41682aab0a | ||
|
|
9561c99711 | ||
|
|
1fd538999f | ||
|
|
a4396af352 | ||
|
|
d7c403cf30 | ||
|
|
089eb9bcb2 | ||
|
|
178383d549 | ||
|
|
f6e6d0e59c | ||
|
|
30c1cc1e3f | ||
|
|
e7f579862f | ||
|
|
22fc984432 | ||
|
|
33b3c78676 | ||
|
|
0052641b86 | ||
|
|
b5b67ed6c4 | ||
|
|
08e0b6e72d | ||
|
|
6e23b1ef22 | ||
|
|
e0da7c6bf5 | ||
|
|
138bd8adef | ||
|
|
ab527368c0 | ||
|
|
335c53360d | ||
|
|
7b31f6ec5a | ||
|
|
63a8791752 | ||
|
|
e3ce7fe95a | ||
|
|
ce6556496e | ||
|
|
eab5e22c8c | ||
|
|
827905e690 | ||
|
|
7fe407d25e | ||
|
|
b9d0f56e0f | ||
|
|
5d06a8b62a | ||
|
|
39c1f671d4 | ||
|
|
4eb8c77304 | ||
|
|
c933748077 | ||
|
|
3347722d6d | ||
|
|
56c35ac573 | ||
|
|
68a61dfcd8 | ||
|
|
664c260b99 | ||
|
|
a07ce67c95 | ||
|
|
6cd71b2bf5 | ||
|
|
eb3001a57d | ||
|
|
cfd44501d4 | ||
|
|
62f7592e1d | ||
|
|
5b07f5d726 | ||
|
|
3c7cdd150a | ||
|
|
3bafb4e022 | ||
|
|
477bb87322 | ||
|
|
6584303cdf | ||
|
|
cbcb72158e | ||
|
|
43c8c7d243 | ||
|
|
9d31559916 | ||
|
|
3483654830 | ||
|
|
a74b162e37 | ||
|
|
52dd73fa9f | ||
|
|
548b92ffe9 | ||
|
|
1df84ae7a3 | ||
|
|
ba40179c82 | ||
|
|
1add52a459 | ||
|
|
2f105d30fd | ||
|
|
e473940c26 | ||
|
|
5f836c381f | ||
|
|
7282fdae0b | ||
|
|
df2a7412d3 | ||
|
|
ae4cdeedbf | ||
|
|
fe6115aefb | ||
|
|
414dfa57b2 | ||
|
|
193a39390c | ||
|
|
43455959e2 | ||
|
|
2d0e142a79 | ||
|
|
27714c0cdf | ||
|
|
37db758ee5 | ||
|
|
faccbc10e5 | ||
|
|
748bd493c4 | ||
|
|
8616956df1 | ||
|
|
e0da5470e2 | ||
|
|
27e237c5e9 | ||
|
|
22d4ebbea4 | ||
|
|
586b7fc389 | ||
|
|
63f6c1e5cf | ||
|
|
2c128794ac | ||
|
|
cf0e8a1926 | ||
|
|
06bd468658 | ||
|
|
7b6ff73fb9 | ||
|
|
54231b23f3 | ||
|
|
77ee958587 | ||
|
|
69137b2212 | ||
|
|
208f080bd2 | ||
|
|
34b738e2fe | ||
|
|
1664171794 | ||
|
|
5690ff6c99 | ||
|
|
e7a43ab7a6 | ||
|
|
96fcc79c93 | ||
|
|
b99272e850 | ||
|
|
b04aba7644 | ||
|
|
cef13eafa0 | ||
|
|
37befe2c46 | ||
|
|
dd84af3bfc | ||
|
|
65a4d64925 | ||
|
|
0b2d62917a | ||
|
|
9fc7dca138 | ||
|
|
b50c7e6cf6 | ||
|
|
944bce14b8 | ||
|
|
269952ee67 | ||
|
|
d1ccd16987 | ||
|
|
78a7c79376 | ||
|
|
41c66ecb28 | ||
|
|
b2f21094b5 | ||
|
|
32666a0d86 | ||
|
|
61afc4ed58 | ||
|
|
9904946ce2 | ||
|
|
78f76054ad | ||
|
|
6e71b3fb66 | ||
|
|
37a8b51a5f | ||
|
|
02a43799a7 | ||
|
|
a5994ebe63 | ||
|
|
7319652db7 | ||
|
|
35f91fd842 | ||
|
|
801fd86db5 | ||
|
|
afb376a3bc | ||
|
|
3baaef19d4 | ||
|
|
2e0e0c1d86 | ||
|
|
c091c5aeda | ||
|
|
74dd3b67e2 | ||
|
|
2b3d090fd6 | ||
|
|
d875f3fb2f | ||
|
|
92f90eddc7 | ||
|
|
3feae3b211 | ||
|
|
03855a604a | ||
|
|
b88000a5b4 | ||
|
|
136ba783c8 | ||
|
|
df6fb03245 | ||
|
|
9509f5ae52 | ||
|
|
c69177be6f | ||
|
|
daaa7d4016 | ||
|
|
d6914456dc | ||
|
|
4c3efb6b52 | ||
|
|
f6193103e2 | ||
|
|
68a78c32e3 | ||
|
|
241ea648ee | ||
|
|
a7a4e45c8b | ||
|
|
0b17c5dcda | ||
|
|
60b79a9b8a | ||
|
|
9dfd8bbadf | ||
|
|
7bfd80a122 | ||
|
|
8b6c6707c7 | ||
|
|
d1ee19243f | ||
|
|
3aca98d1e2 | ||
|
|
b76c6063b0 | ||
|
|
584b4321ca | ||
|
|
b499d12768 | ||
|
|
246f6f8132 | ||
|
|
e07a350159 | ||
|
|
ac05f5c93c | ||
|
|
41cec5ffc3 | ||
|
|
f5ca01c529 | ||
|
|
6b115616b7 | ||
|
|
611efe05ca | ||
|
|
9e3d30e81e | ||
|
|
ba20fd79e0 | ||
|
|
37e6ea89b7 | ||
|
|
a8457067b3 | ||
|
|
6e82ca9ac2 | ||
|
|
57a6e4f175 | ||
|
|
7621ae0c98 | ||
|
|
70b87adf62 | ||
|
|
55e6bd42b1 | ||
|
|
b5711591d5 | ||
|
|
f29463ef6d | ||
|
|
18b623548a | ||
|
|
6c394c22da | ||
|
|
738f9e7547 | ||
|
|
d885fddfe6 | ||
|
|
95a1713c8a | ||
|
|
6661ffb580 | ||
|
|
99b7a6ccf7 | ||
|
|
c63865f043 | ||
|
|
a00312df84 | ||
|
|
9ec3374240 | ||
|
|
81c8e17333 | ||
|
|
cf576dcbfd | ||
|
|
3cbaa09769 | ||
|
|
89f795e953 | ||
|
|
86c9ab40cf | ||
|
|
681a7d17d5 | ||
|
|
5a1d0ea51b | ||
|
|
9aa3c02907 | ||
|
|
0cfdfed476 | ||
|
|
a205614d65 | ||
|
|
db27bcbbe7 | ||
|
|
943430cfce | ||
|
|
6c55d2aec6 | ||
|
|
7d3ecaca7e | ||
|
|
f2e8de6938 | ||
|
|
26090e87b3 | ||
|
|
af82d9fec1 | ||
|
|
86defbb144 | ||
|
|
d0a5557807 | ||
|
|
74788c77e0 | ||
|
|
f77bdc4b9c | ||
|
|
7f659178c5 | ||
|
|
b67e8e6c85 | ||
|
|
df4e7c0774 | ||
|
|
8d5882cb81 | ||
|
|
d540fda5b0 | ||
|
|
d39957b8ca | ||
|
|
7160bcc33f | ||
|
|
36d8a0aa8d | ||
|
|
0dec97fad1 | ||
|
|
9da08e94ac | ||
|
|
62b3fe61b6 | ||
|
|
50287025a0 | ||
|
|
ed234c898e | ||
|
|
f2775fe0f0 | ||
|
|
135168c1bb | ||
|
|
35f74f5ea4 | ||
|
|
26f0ab6661 | ||
|
|
6213c753fe | ||
|
|
3f1b23dc82 | ||
|
|
dda76323c6 | ||
|
|
c41128401d | ||
|
|
9a5fe131cd | ||
|
|
e45fe2f279 | ||
|
|
4698a948cc | ||
|
|
3648213be2 | ||
|
|
40146a42ce | ||
|
|
35501c4c48 | ||
|
|
dc28e5708d | ||
|
|
d4f3f69129 | ||
|
|
936bc7ed72 | ||
|
|
8a59fbdb41 | ||
|
|
2c93fd1542 | ||
|
|
2981497da9 | ||
|
|
6745e0486c | ||
|
|
76c6f56d73 | ||
|
|
21b0c63422 | ||
|
|
093ebb424e | ||
|
|
c344b0f62a | ||
|
|
b447d2e5ad | ||
|
|
2b85ed473c | ||
|
|
d536b38acc | ||
|
|
b4a82511ff | ||
|
|
35a84adde3 | ||
|
|
f1de28e6c1 | ||
|
|
2336255763 | ||
|
|
a3022963c0 | ||
|
|
396a035cfd | ||
|
|
2001fd26f8 | ||
|
|
9daf2d0448 | ||
|
|
b2d14ff400 | ||
|
|
675fd5b735 | ||
|
|
14c7663167 | ||
|
|
788a04aceb | ||
|
|
99c70f96d8 | ||
|
|
9f729e8c36 | ||
|
|
35f1a351ba | ||
|
|
5f59de2fba | ||
|
|
95b7c59606 | ||
|
|
7962b5c8ea | ||
|
|
d2818a35c6 | ||
|
|
44183178c6 | ||
|
|
2ebb35b631 | ||
|
|
173a5fedf0 | ||
|
|
00c0bbd942 | ||
|
|
1dfb38207b | ||
|
|
df0d087646 | ||
|
|
8f84e292b2 | ||
|
|
45bf4247c1 | ||
|
|
daecd42d8e | ||
|
|
952aaa76ff | ||
|
|
fe57c05579 | ||
|
|
309fd9f4d1 | ||
|
|
9af0d74ba4 | ||
|
|
c1292b585f | ||
|
|
87a6bbe0fd | ||
|
|
b807431053 | ||
|
|
1442af3148 | ||
|
|
eb92645ed0 | ||
|
|
714be0f6ae | ||
|
|
e77c3ce1d3 | ||
|
|
48d080bcda | ||
|
|
531cb51fac | ||
|
|
e8bc949230 | ||
|
|
d99d43dde7 | ||
|
|
b64c304a92 | ||
|
|
d392b1c434 | ||
|
|
1f9b5df44b | ||
|
|
edd4067efc | ||
|
|
1a23b8363a | ||
|
|
04df628fe8 | ||
|
|
2bb5a18f27 | ||
|
|
6cc7c3b1fb | ||
|
|
21c1b1e913 | ||
|
|
a1cdba5baa | ||
|
|
3b14ffb331 | ||
|
|
b0e6de8a47 | ||
|
|
2a3138ca76 | ||
|
|
56a0c6325d | ||
|
|
69c0485fe8 | ||
|
|
21205f85aa | ||
|
|
9d10d75d54 | ||
|
|
2757f657c5 | ||
|
|
1c4040707e | ||
|
|
ca4a29a991 | ||
|
|
9120da776c | ||
|
|
24b2f1fca5 | ||
|
|
f4f0bb166a | ||
|
|
27c8d78ac9 | ||
|
|
fe32105491 | ||
|
|
ad7ef30f68 | ||
|
|
c1df53e986 | ||
|
|
36b8f52929 | ||
|
|
f84ab77a9a | ||
|
|
1ec842c274 | ||
|
|
e06849cd24 | ||
|
|
3f31533909 | ||
|
|
5a408ccd4a | ||
|
|
07d3a4ef84 | ||
|
|
eb50ed1bc3 | ||
|
|
681d120e8b | ||
|
|
74ae266fed | ||
|
|
028f16af06 | ||
|
|
fa7980f379 | ||
|
|
4ddc173757 | ||
|
|
8bc93d7f23 | ||
|
|
de72df99ea | ||
|
|
580fd60fd9 | ||
|
|
c884f93ac3 | ||
|
|
a57ff299b5 | ||
|
|
159a90d7e3 | ||
|
|
b8fe4639ba | ||
|
|
6a80021bd1 | ||
|
|
d2dc75d556 | ||
|
|
2aa93b56cb | ||
|
|
11037daca9 | ||
|
|
93932e78f9 | ||
|
|
7ad476d543 | ||
|
|
9b487f94de | ||
|
|
68e34eb3bd | ||
|
|
88f95b96b0 | ||
|
|
e22a69caf5 | ||
|
|
bb339504d4 | ||
|
|
2666c5248b | ||
|
|
c0df8e9a04 | ||
|
|
bead854cf1 | ||
|
|
48da600598 | ||
|
|
3b0b349f4c | ||
|
|
294e6f5edf | ||
|
|
02fb27f48e | ||
|
|
e57a02408e | ||
|
|
5325381f2f | ||
|
|
4176b06578 | ||
|
|
7014f5d9f1 | ||
|
|
afbef4272c | ||
|
|
45df2d6605 | ||
|
|
1c2369c419 | ||
|
|
81211ecb74 | ||
|
|
17a06adfd9 | ||
|
|
da565e43bb | ||
|
|
641497a8d3 | ||
|
|
7cecadcf82 | ||
|
|
cd776754b5 | ||
|
|
9d2d920fda | ||
|
|
cb30d3a79e | ||
|
|
3842cbffc4 | ||
|
|
1cf7cbc144 | ||
|
|
73e8291cb0 | ||
|
|
1e89c8dac1 | ||
|
|
81c4477453 | ||
|
|
adb0865aa7 | ||
|
|
d65ae7767a | ||
|
|
d83474d6f6 | ||
|
|
5cffec4724 | ||
|
|
8af2f51eae | ||
|
|
39579b0183 | ||
|
|
caff2e25e2 | ||
|
|
0fee6e2298 | ||
|
|
feaad1300a | ||
|
|
1a59129d88 | ||
|
|
1c7fa8d41d | ||
|
|
1480657857 | ||
|
|
c8ab5836eb | ||
|
|
2bb7a2065a | ||
|
|
0564a103e1 | ||
|
|
cd0954badb | ||
|
|
2e5a1a9820 | ||
|
|
8acc6dec1b | ||
|
|
3f0bbbb19e | ||
|
|
16e9077cd4 | ||
|
|
53bb822013 | ||
|
|
37c1da8962 | ||
|
|
75535c8edd | ||
|
|
36c7e9eecc | ||
|
|
fee5103bdd | ||
|
|
4a5a77de21 | ||
|
|
1b5f25f10b | ||
|
|
4cf67734a1 | ||
|
|
b6778fb331 | ||
|
|
2ae50c2fbd | ||
|
|
f33ad9e403 | ||
|
|
4f7d58d8b4 | ||
|
|
6582faa8d7 | ||
|
|
387f584b2c | ||
|
|
b320c0fd5a | ||
|
|
30294a0195 | ||
|
|
252778bf7c | ||
|
|
80bd3c2043 | ||
|
|
eb968ba676 | ||
|
|
1abe428559 | ||
|
|
9854a2ad33 | ||
|
|
54ebe89d44 | ||
|
|
2565a04c83 | ||
|
|
bb11bdf423 | ||
|
|
d6983265ce | ||
|
|
0034999256 | ||
|
|
10004bd67e | ||
|
|
a5c35b8fa9 | ||
|
|
c0d7626ec3 | ||
|
|
1fbd310371 | ||
|
|
5f0b5919a4 | ||
|
|
ae41b34f13 | ||
|
|
4ecb7ef8a6 | ||
|
|
bfef18bfe7 | ||
|
|
0448aca24e | ||
|
|
af67e29ea3 | ||
|
|
f12ee10b9d | ||
|
|
aea585a93d | ||
|
|
a5b30b557a | ||
|
|
a52ebcf576 | ||
|
|
62fcfdfc95 | ||
|
|
a69e5d9c5f | ||
|
|
72ffaf39f4 | ||
|
|
08734d733f | ||
|
|
e3ddad2c64 | ||
|
|
693cf3608a | ||
|
|
ad9f49f66e | ||
|
|
6910414ba3 | ||
|
|
f8bb39384b | ||
|
|
e7b4cfe53e | ||
|
|
8e6146dd46 | ||
|
|
50254c497a | ||
|
|
66a7469cd5 | ||
|
|
70cf30b8a3 | ||
|
|
1549d71af0 | ||
|
|
69c95a4073 | ||
|
|
6c79c1539a | ||
|
|
0b7d45d3b9 | ||
|
|
edd1b683dd | ||
|
|
eccd8e3810 | ||
|
|
8855541ee1 | ||
|
|
fd300add4f | ||
|
|
3161e04752 | ||
|
|
ca1a51749a | ||
|
|
5aba209857 | ||
|
|
80cfb9e098 | ||
|
|
b98c887d71 | ||
|
|
be8e8f9b02 | ||
|
|
87cbf65654 | ||
|
|
6a0e132945 | ||
|
|
438cf5793a | ||
|
|
74a736ef87 | ||
|
|
4ce451b99c | ||
|
|
f017d3fdf2 | ||
|
|
54eb656726 | ||
|
|
9d63578110 | ||
|
|
06baeeb79a | ||
|
|
a77fe463d8 | ||
|
|
c016fc164d | ||
|
|
a409bb1318 | ||
|
|
7eeaf205cd | ||
|
|
85882b2547 | ||
|
|
12e4af7b0d | ||
|
|
774406fe00 | ||
|
|
e7c8534c01 | ||
|
|
0655dd41bb | ||
|
|
48bb71d5dc | ||
|
|
b2641fe4d3 | ||
|
|
e58de5a99f | ||
|
|
68df9ada01 | ||
|
|
a91a711499 | ||
|
|
780ab1125d | ||
|
|
2e83495542 | ||
|
|
b69b7a8b5f | ||
|
|
00f264c313 | ||
|
|
ba9eb65fa6 | ||
|
|
824a7484e0 | ||
|
|
7ebab48b97 | ||
|
|
708bd9bc8a | ||
|
|
960b00b9ca | ||
|
|
d654137caa | ||
|
|
e4b36c172b | ||
|
|
05f8a067f1 | ||
|
|
d3db23d1b7 | ||
|
|
30288a3106 | ||
|
|
89410e60c7 | ||
|
|
3e971b5fe9 | ||
|
|
7673e4d638 | ||
|
|
9a76ea1433 | ||
|
|
92ff60c66d | ||
|
|
dfad45f83d | ||
|
|
6c0a11d300 | ||
|
|
54f58067d3 | ||
|
|
edb23bef1e | ||
|
|
cddd0705d8 | ||
|
|
fc4447748b | ||
|
|
433310030f | ||
|
|
388fb2ba34 | ||
|
|
d2a19967bc | ||
|
|
b2538c0133 | ||
|
|
dde2fdb103 | ||
|
|
7a83064745 | ||
|
|
970ebfede5 | ||
|
|
4ecc56fa79 | ||
|
|
1e25bc6c7c | ||
|
|
8eca4a1d19 | ||
|
|
c6f7a0ba1a | ||
|
|
fe07cf6060 | ||
|
|
022c26c89d | ||
|
|
a13bcaf223 | ||
|
|
8b469ed263 | ||
|
|
ff631935fc | ||
|
|
6fd363e071 | ||
|
|
57556764e9 | ||
|
|
fbef5de705 | ||
|
|
bff035f8a5 | ||
|
|
272a124d3a | ||
|
|
ae5fd9765d | ||
|
|
2900b293c3 | ||
|
|
b8fbca0d49 | ||
|
|
370f5a5b4e | ||
|
|
9558786b76 | ||
|
|
c0260974bd | ||
|
|
f864af6003 | ||
|
|
30fc5e4646 | ||
|
|
3dee4935c8 | ||
|
|
0e088a48b8 | ||
|
|
0928ab0224 | ||
|
|
ec07a61cab | ||
|
|
14d7eaf361 | ||
|
|
0f2892bd28 | ||
|
|
51ae5a2a0c | ||
|
|
5bb5fc9496 | ||
|
|
9e550f85cc | ||
|
|
4ef46e302d | ||
|
|
079806c857 | ||
|
|
deed92af0d | ||
|
|
48367c5926 | ||
|
|
0f3cdf77d4 | ||
|
|
4b06833c3f | ||
|
|
8a6bfe3288 | ||
|
|
2d106f7b87 | ||
|
|
130ef249d7 | ||
|
|
5b55cf0edc | ||
|
|
1d824049cb | ||
|
|
2eef3cfa00 | ||
|
|
f82cfeed3d | ||
|
|
3aba034155 | ||
|
|
114a0832cb | ||
|
|
66f627fd1a | ||
|
|
1ce915cd9e | ||
|
|
712f476da8 | ||
|
|
2d0e5d208b | ||
|
|
053cef944a | ||
|
|
ff2ba3f4ed | ||
|
|
ec7d908941 | ||
|
|
1afbd4c632 | ||
|
|
a5af23b2f7 | ||
|
|
78ac8f45e9 | ||
|
|
c9ade3ad3f | ||
|
|
c588d3d581 | ||
|
|
0140b4f74e | ||
|
|
eb42d33630 | ||
|
|
f6b0eab8b1 | ||
|
|
b316c21623 | ||
|
|
bd661fddb1 | ||
|
|
fd5ca57f36 | ||
|
|
d38d446a70 | ||
|
|
4f97e6e8b9 | ||
|
|
e79450a54d | ||
|
|
44fd071c9d | ||
|
|
8487b935c4 | ||
|
|
f497194281 | ||
|
|
614e014cab | ||
|
|
5bf976747c | ||
|
|
5c2a7f27e6 | ||
|
|
4c059b625c | ||
|
|
1860a92fa0 | ||
|
|
f4799acf31 | ||
|
|
a704e523d9 | ||
|
|
725cdf3ec3 | ||
|
|
f3a3956d79 | ||
|
|
167cd39c8f | ||
|
|
681492f5f8 | ||
|
|
80766034a6 | ||
|
|
4e7b96462c | ||
|
|
761cae4a2b | ||
|
|
d0d526d36d | ||
|
|
f29cfd55fa | ||
|
|
7a4116c016 | ||
|
|
5639517128 | ||
|
|
17c7b9c526 | ||
|
|
e40c5aa821 | ||
|
|
1915a8b1db | ||
|
|
c105312b99 | ||
|
|
2b5d4365c2 | ||
|
|
f145c68cfe | ||
|
|
2fff53f468 | ||
|
|
6a1e75bf51 | ||
|
|
4d903239ca | ||
|
|
570133d365 | ||
|
|
9cc49389e4 | ||
|
|
cfcc472c4c | ||
|
|
79ff2c1adf | ||
|
|
01e5aad370 | ||
|
|
50ba6c2750 | ||
|
|
49a4c8e771 | ||
|
|
97de25e2ed | ||
|
|
7a6d9392d3 | ||
|
|
4738db9b7d | ||
|
|
172e75e4e8 | ||
|
|
c4795547c8 | ||
|
|
fefc6ca04b | ||
|
|
b00945a815 | ||
|
|
3ad7112011 | ||
|
|
9b49df08e3 | ||
|
|
06ba923c28 | ||
|
|
67c5e58c2c | ||
|
|
e8c5e0d911 | ||
|
|
668836e646 | ||
|
|
e1cd8123cf | ||
|
|
6cfeb715e5 | ||
|
|
19419dc483 | ||
|
|
1aac650a3d | ||
|
|
7030c9e29b | ||
|
|
730fbe211c | ||
|
|
0993ea5e46 | ||
|
|
0cc4fc76fd | ||
|
|
3a6f536cd9 | ||
|
|
4809037fdc | ||
|
|
3d9c084e59 | ||
|
|
75f64c31a9 | ||
|
|
4d64c06f79 | ||
|
|
f6a4f46b12 | ||
|
|
df68fd70f7 | ||
|
|
2631982e62 | ||
|
|
bcbc89f3a3 | ||
|
|
d5f5d9a6b1 | ||
|
|
01028a956a | ||
|
|
eb55d15a2f | ||
|
|
8219ea74b2 | ||
|
|
18b1cb6805 | ||
|
|
b0f9dbb062 | ||
|
|
94118d31f1 | ||
|
|
44c52ee47c | ||
|
|
b71b9c523c | ||
|
|
06b19dc05f | ||
|
|
6a05c0348b | ||
|
|
c04184d715 | ||
|
|
c3bfdfcc29 | ||
|
|
b6a83339e8 | ||
|
|
5a50b1b91f | ||
|
|
31f3b70fdb | ||
|
|
c4673f87d7 | ||
|
|
3b5822fd8f | ||
|
|
f0d40713ae | ||
|
|
a1a249421c | ||
|
|
fe927f8111 | ||
|
|
b639a0bc9f | ||
|
|
1ce9afb8be | ||
|
|
a277511be9 | ||
|
|
25e9b51d04 | ||
|
|
b20076e7b8 | ||
|
|
0c4d096e36 | ||
|
|
6b3ab59f13 | ||
|
|
98e937a61b | ||
|
|
b10893dfc6 | ||
|
|
ec6d5bb56e | ||
|
|
7dbaf6feaa | ||
|
|
6cffa74196 | ||
|
|
7906c477c4 | ||
|
|
5cc139e7f0 | ||
|
|
a3b2ccfbb9 | ||
|
|
f0cde44dc1 | ||
|
|
c7cf38de48 | ||
|
|
dad72764e8 | ||
|
|
10ef165112 | ||
|
|
47fcc8007d | ||
|
|
bb9a53e6cc | ||
|
|
46544570ad | ||
|
|
db71a06371 | ||
|
|
f5b88df4c1 | ||
|
|
a4dfc59126 | ||
|
|
c980d37aa2 | ||
|
|
411740cf01 | ||
|
|
9d4ca67722 | ||
|
|
ad5b7e5b46 | ||
|
|
785c0bba8d | ||
|
|
f6395908ee | ||
|
|
4636f5800c | ||
|
|
212418f11a | ||
|
|
7f443e8505 | ||
|
|
488d11b693 | ||
|
|
f0255703c1 | ||
|
|
f7e33728f2 | ||
|
|
86fcfb49eb | ||
|
|
17ae8f7e99 | ||
|
|
638a2ec462 | ||
|
|
789e474c04 | ||
|
|
65ade96fd8 | ||
|
|
05e2502831 | ||
|
|
f1a4536a42 | ||
|
|
0bb372c2e9 | ||
|
|
98c38e6f8f | ||
|
|
7739a1bb85 | ||
|
|
5dbc4df7c1 | ||
|
|
3bee5fc4c9 | ||
|
|
62fe696921 | ||
|
|
603f30cd9d | ||
|
|
764d208695 | ||
|
|
147c83f62b | ||
|
|
da89fd674f | ||
|
|
6bc4677f3a | ||
|
|
0676cd6e7c | ||
|
|
7dceb7e363 | ||
|
|
6a2be81241 | ||
|
|
b26c9bfa35 | ||
|
|
19ecbfae85 | ||
|
|
221277bf24 | ||
|
|
41d864e69f | ||
|
|
46341b5d37 | ||
|
|
b3569f1459 | ||
|
|
963df05ed3 | ||
|
|
f693d93e03 | ||
|
|
b3a78194dd | ||
|
|
2d27c95388 | ||
|
|
3e760df174 | ||
|
|
9dca986efe | ||
|
|
6967bd2b84 | ||
|
|
b32e599223 | ||
|
|
727863e4ee | ||
|
|
2c157bc76f | ||
|
|
f78f2b9361 | ||
|
|
3859ce78a5 | ||
|
|
1e1c79e8f8 | ||
|
|
beadd426c4 | ||
|
|
edc650a66e | ||
|
|
562a80132a | ||
|
|
f5ee9e3955 | ||
|
|
a0a8a2810c | ||
|
|
1d6dadea58 | ||
|
|
5144fe6fc9 | ||
|
|
7e0913fe8b | ||
|
|
15f1aa68da | ||
|
|
1eb11928c2 | ||
|
|
982527b4bf | ||
|
|
2a2622ddd8 | ||
|
|
39939790c2 | ||
|
|
ddabf82ea7 | ||
|
|
7dd6745cd9 | ||
|
|
3ac02da522 | ||
|
|
d5982ae1e3 | ||
|
|
fe2ebfcfd9 | ||
|
|
977fe33d6a | ||
|
|
951e11f6b0 | ||
|
|
2fa1cd175d | ||
|
|
5c9ab11aff | ||
|
|
1f9327db80 | ||
|
|
e16429f77b | ||
|
|
3e58ef5dba | ||
|
|
84ac5fef4f | ||
|
|
303d98bad7 | ||
|
|
e39a126229 | ||
|
|
7257657432 | ||
|
|
99999dde2c | ||
|
|
60fc41d524 | ||
|
|
559a6f87b2 | ||
|
|
02fbe3ce93 | ||
|
|
aa221c221e | ||
|
|
796efc490e | ||
|
|
b3c6b9b8e3 | ||
|
|
bc44d8a0a6 | ||
|
|
a43c291794 | ||
|
|
2a4c0a8427 | ||
|
|
bca9156a38 | ||
|
|
cd97ac8106 | ||
|
|
e464413b05 | ||
|
|
39c50eda0c | ||
|
|
4901485dc3 | ||
|
|
0762c227c1 | ||
|
|
09537689cf | ||
|
|
92ffe596ce | ||
|
|
7037fbfe94 | ||
|
|
9eab59dcbe | ||
|
|
d705371f87 | ||
|
|
09206a387b | ||
|
|
65fc67a4b2 | ||
|
|
7faa6a8efc | ||
|
|
4f4b7fdfa4 | ||
|
|
ce09d9c8a9 | ||
|
|
c9744aeb94 | ||
|
|
a65dbfafa3 | ||
|
|
e40c117228 | ||
|
|
46bded5c16 | ||
|
|
47ab6df725 | ||
|
|
94a366a13e | ||
|
|
f23de8f380 | ||
|
|
f1fb2a47f0 | ||
|
|
175c64bdc8 | ||
|
|
07ab9f21db | ||
|
|
56ece19994 | ||
|
|
1163e9956a | ||
|
|
d02b9438eb | ||
|
|
40cbd51e8b | ||
|
|
490de22dc7 | ||
|
|
ef71df7d5f | ||
|
|
0fbf0179f3 | ||
|
|
5a45360ab6 | ||
|
|
94ac246d1f | ||
|
|
85ef205fab | ||
|
|
4dd0215ac8 | ||
|
|
4fa940af47 | ||
|
|
6e76e4908e | ||
|
|
8991538449 | ||
|
|
9a51267b8f | ||
|
|
373128f678 | ||
|
|
563faf6cc5 | ||
|
|
aff243389d | ||
|
|
a38be4ad80 | ||
|
|
bd52090236 | ||
|
|
5ae11a91c1 | ||
|
|
f1cc61e8ce | ||
|
|
5af68c5d9b | ||
|
|
dbe804f223 | ||
|
|
2bbbc4fe94 | ||
|
|
9cd66fc30c | ||
|
|
65f6731b61 | ||
|
|
25defeaf3a | ||
|
|
e8d8f13888 | ||
|
|
4a9a767fc6 | ||
|
|
d8b22b4914 | ||
|
|
ef22f19dec | ||
|
|
4bcf651e5e | ||
|
|
19aea7808d | ||
|
|
afa4c9c0f2 | ||
|
|
d2d853e916 | ||
|
|
3ead147be4 | ||
|
|
c863ff1fd1 | ||
|
|
6028b10bf1 | ||
|
|
e74bb8d3b5 | ||
|
|
7bbe3fb9b5 | ||
|
|
e00eb2a5bc | ||
|
|
23ca92039b | ||
|
|
54f8916ddd | ||
|
|
a4b3c933ef | ||
|
|
d0633e883e | ||
|
|
8071e0692a | ||
|
|
47f07c66bf | ||
|
|
5db30a2b98 | ||
|
|
fcd3cddbc7 | ||
|
|
3c49d30de6 | ||
|
|
377bffd7a4 | ||
|
|
6af1da01db | ||
|
|
a6e430e569 | ||
|
|
15d3492ae3 | ||
|
|
7a731da44f | ||
|
|
64a53ce887 | ||
|
|
45355ba972 | ||
|
|
3b769e2a9f | ||
|
|
ed70c3fcb1 | ||
|
|
86d56be044 | ||
|
|
448492cbc1 | ||
|
|
5010eb3a7c | ||
|
|
e429f082e4 | ||
|
|
53be95ac35 | ||
|
|
1a47b603c7 | ||
|
|
4b956af0f7 | ||
|
|
8a9495edce | ||
|
|
b96e8a5b2b | ||
|
|
a37789fbfe | ||
|
|
8d95fb1660 | ||
|
|
db83855fee | ||
|
|
56d896ca22 | ||
|
|
1169e7183e | ||
|
|
fe0b377561 | ||
|
|
5528d6bc8d | ||
|
|
7856ea8130 | ||
|
|
dbc684306b | ||
|
|
19187352b5 | ||
|
|
63ffd56fd9 | ||
|
|
4ea3da158d | ||
|
|
00daf35918 | ||
|
|
18bfc06091 | ||
|
|
0cf78f5c8e | ||
|
|
1193b37a53 | ||
|
|
6cb3fb2354 | ||
|
|
1109b40af4 | ||
|
|
479ab9377e | ||
|
|
5e01b3c0ab | ||
|
|
3b876dde1c | ||
|
|
36fdc5e737 | ||
|
|
0410687ae1 | ||
|
|
d05be2b599 | ||
|
|
39b0d4f0c2 | ||
|
|
661aefaebc | ||
|
|
d25c951c88 | ||
|
|
6d5d8142b3 | ||
|
|
dee8bdec24 | ||
|
|
e5010b761d | ||
|
|
8b345d4fac | ||
|
|
d9679801e2 | ||
|
|
293864e62b | ||
|
|
ddd1a25d93 | ||
|
|
a44e3bae90 | ||
|
|
97c682416c | ||
|
|
490d32e8d6 | ||
|
|
36739e1b11 | ||
|
|
8639cc313a | ||
|
|
20cacc6dd5 | ||
|
|
b60b0f2bf6 | ||
|
|
4db37a0f18 | ||
|
|
6a28219b7f | ||
|
|
904885db6c | ||
|
|
6a7b003246 | ||
|
|
a2aeb67d6d | ||
|
|
412b4f26b7 | ||
|
|
b95e380bd8 | ||
|
|
bf46b015a5 | ||
|
|
d23f4c8095 | ||
|
|
70c6c59e74 | ||
|
|
e69f662608 | ||
|
|
8d934663c5 | ||
|
|
12070b042f |
53
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Bug Report
|
||||
description: Report a bug.
|
||||
title: "[BUG] "
|
||||
labels: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please check out the [FAQ section](https://github.com/PhilippC/keepass2android/blob/master/docs/Documentation.md#faq) and [search for open issues](https://github.com/PhilippC/keepass2android/issues?q=is%3Aopen+is%3Aissue+label%3Abug) first.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checks
|
||||
options:
|
||||
- label: I have read the FAQ section, searched the open issues, and still think this is a new bug.
|
||||
required: true
|
||||
- type: textarea
|
||||
id: bug
|
||||
attributes:
|
||||
label: "Describe the bug you encountered:"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: "Describe what you expected to happen:"
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please follow these steps to find your app version:
|
||||
1. Click the **⁝** icon in the top right corner
|
||||
2. Select **Settings**
|
||||
3. Click **About**
|
||||
4. Find the "Version" information and provide it below
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: "What version of Keepass2Android are you using?"
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please follow these steps to find your Android version:
|
||||
1. Open your device's **Settings** app
|
||||
2. Scroll down and select **About phone** or **About tablet**
|
||||
3. Find the **Android version** section and provide it below
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: "Which version of Android are you on?"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project.
|
||||
title: '[FEAT] '
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
16
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a question about 'Keepass2Android'.
|
||||
title: '[QUESTION] '
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**What version of Keepass2Android are you using?**
|
||||
Please follow these steps to find your app version:
|
||||
1. Click the **⁝** icon in the top right corner
|
||||
2. Select **Settings**
|
||||
3. Click **About**
|
||||
4. Find the "Version" information and provide it here:
|
||||
|
||||
352
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,352 @@
|
||||
name: Build keepass2android app
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
# macos:
|
||||
# Disabled. Does not work, maybe due to nuget version, see https://github.com/PhilippC/keepass2android/actions/runs/4297640426/jobs/7490853348
|
||||
# should work again when the Project solution is converted to sdk style .csproj files.
|
||||
|
||||
# runs-on: macos-12
|
||||
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# with:
|
||||
# submodules: true
|
||||
|
||||
# - name: Setup Gradle
|
||||
# uses: gradle/actions/setup-gradle@v3
|
||||
|
||||
# - name: Cache NuGet packages
|
||||
# uses: actions/cache@v4
|
||||
# with:
|
||||
# path: ~/.nuget/packages
|
||||
# key: ${{ runner.os }}-nuget-${{ hashFiles('src/**/*.csproj', 'src/**/packages.config') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-nuget-
|
||||
|
||||
# # As per https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#visual-studio-for-mac
|
||||
# - name: Switch to Visual Studio 2019
|
||||
# if: ${{ false }} # Not needed. We stay with the default 'Visual Studio 2022' of macos-12 runner.
|
||||
# run: |
|
||||
# mv "/Applications/Visual Studio.app" "/Applications/Visual Studio 2022.app"
|
||||
# mv "/Applications/Visual Studio 2019.app" "/Applications/Visual Studio.app"
|
||||
|
||||
# # As of 2022-12-02, keepass2android doesn't build with Xamarin >= 12.1 because there is some issue with SamsungPass. Removing SamsungPass would make the build succeed.
|
||||
# - name: Set default Xamarin SDK versions
|
||||
# run: |
|
||||
# # If using the github runner 'macos-12'
|
||||
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.3
|
||||
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.0
|
||||
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.1 # Build fails in this case, as of 2022-12-02 : Xamarin/Android/Xamarin.Android.D8.targets(79,5): error : java.lang.ArrayIndexOutOfBoundsException : Index 4 out of bounds for length 4
|
||||
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.2 # Build fails in this case, as of 2022-12-02 : Xamarin/Android/Xamarin.Android.D8.targets(79,5): error : java.lang.ArrayIndexOutOfBoundsException : Index 4 out of bounds for length 4
|
||||
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.3 # Build fails in this case, as of 2022-12-02
|
||||
# $VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=13.1
|
||||
|
||||
# # If using the github runner 'macos-11'
|
||||
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.0
|
||||
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.0
|
||||
|
||||
# # If using the github runner 'macos-10.15'
|
||||
# # $VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.2
|
||||
|
||||
# - name: Switch to JDK-11
|
||||
# uses: actions/setup-java@v4
|
||||
# with:
|
||||
# java-version: '11'
|
||||
# distribution: 'temurin'
|
||||
|
||||
# - name: Display java version
|
||||
# run: java -version
|
||||
|
||||
# # Some components of Keepass2Android currently target android API 26 which are not available on the runner
|
||||
# - name: Download android-26 API
|
||||
# run: $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-26"
|
||||
|
||||
# - name: Build native dependencies
|
||||
# run: make native
|
||||
|
||||
# - name: Build java dependencies
|
||||
# run: make java
|
||||
|
||||
# - name: Install NuGet dependencies (net)
|
||||
# run: make nuget Flavor=Net
|
||||
|
||||
# - name: Build keepass2android (net)
|
||||
# run: |
|
||||
# make msbuild Flavor=Net
|
||||
|
||||
# - name: Build APK (net)
|
||||
# run: |
|
||||
# make apk Flavor=Net
|
||||
|
||||
# - name: Archive production artifacts (net)
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: signed APK ('net' built on ${{ github.job }})
|
||||
# path: |
|
||||
# src/keepass2android/bin/*/*-Signed.apk
|
||||
|
||||
# - name: Install NuGet dependencies (nonet)
|
||||
# run: make nuget Flavor=NoNet
|
||||
|
||||
# - name: Build keepass2android (nonet)
|
||||
# run: |
|
||||
# make msbuild Flavor=NoNet
|
||||
|
||||
# - name: Build APK (nonet)
|
||||
# run: |
|
||||
# make apk Flavor=NoNet
|
||||
|
||||
# - name: Archive production artifacts (nonet)
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: signed APK ('nonet' built on ${{ github.job }})
|
||||
# path: |
|
||||
# src/keepass2android/bin/*/*-Signed.apk
|
||||
|
||||
# - name: Perform "make distclean"
|
||||
# run: make distclean
|
||||
|
||||
# linux:
|
||||
# disabled.
|
||||
# As per: xamarin/xamarin-android#7235 (comment)
|
||||
# > Unfortunately the Classic OSS Xamarin.Android packages for Linux are no longer being built and as such they are not available for the v13.0 tag.
|
||||
# we can re-enable this after porting to .net 6.0
|
||||
|
||||
# runs-on: ubuntu-22.04
|
||||
|
||||
# env:
|
||||
# # Build Artifact of xamarin.android-oss dated 2021-02-02, master branch (= version 11.2.99) - *.deb cannot be installed because "lxd" package is not anymore shipped in current ubuntu version
|
||||
# #xamarin_url: https://artprodcus3.artifacts.visualstudio.com/Ad0adf05a-e7d7-4b65-96fe-3f3884d42038/6fd3d886-57a5-4e31-8db7-52a1b47c07a8/_apis/artifact/cGlwZWxpbmVhcnRpZmFjdDovL3hhbWFyaW4vcHJvamVjdElkLzZmZDNkODg2LTU3YTUtNGUzMS04ZGI3LTUyYTFiNDdjMDdhOC9idWlsZElkLzM0NTE3L2FydGlmYWN0TmFtZS9JbnN0YWxsZXJzKy0rTGludXg1/content?format=zip
|
||||
|
||||
# # Build Artifact of xamarin.android-oss dated 2021-03-23, d16-9 branch (= version 11.2.2) - *.deb cannot be installed because "lxd" package is not anymore shipped in current ubuntu version
|
||||
# #xamarin_url: https://artprodcus3.artifacts.visualstudio.com/Ad0adf05a-e7d7-4b65-96fe-3f3884d42038/6fd3d886-57a5-4e31-8db7-52a1b47c07a8/_apis/artifact/cGlwZWxpbmVhcnRpZmFjdDovL3hhbWFyaW4vcHJvamVjdElkLzZmZDNkODg2LTU3YTUtNGUzMS04ZGI3LTUyYTFiNDdjMDdhOC9idWlsZElkLzM3Njg0L2FydGlmYWN0TmFtZS9JbnN0YWxsZXJzKy0rTGludXg1/content?format=zip
|
||||
|
||||
# # Build Artifact of xamarin.android-oss dated 2021-07-21, master branch (= version 11.4.99)
|
||||
# # xamarin_url: https://artprodcus3.artifacts.visualstudio.com/Ad0adf05a-e7d7-4b65-96fe-3f3884d42038/6fd3d886-57a5-4e31-8db7-52a1b47c07a8/_apis/artifact/cGlwZWxpbmVhcnRpZmFjdDovL3hhbWFyaW4vcHJvamVjdElkLzZmZDNkODg2LTU3YTUtNGUzMS04ZGI3LTUyYTFiNDdjMDdhOC9idWlsZElkLzQzNjU5L2FydGlmYWN0TmFtZS9pbnN0YWxsZXJzLXVuc2lnbmVkKy0rTGludXg1/content?format=zip
|
||||
|
||||
# # Build Artifact of xamarin.android-oss dated 2022-02-16, master branch (= version 12.2.99)
|
||||
# xamarin_url: https://artprodcus3.artifacts.visualstudio.com/Ad0adf05a-e7d7-4b65-96fe-3f3884d42038/6fd3d886-57a5-4e31-8db7-52a1b47c07a8/_apis/artifact/cGlwZWxpbmVhcnRpZmFjdDovL3hhbWFyaW4vcHJvamVjdElkLzZmZDNkODg2LTU3YTUtNGUzMS04ZGI3LTUyYTFiNDdjMDdhOC9idWlsZElkLzU0OTUzL2FydGlmYWN0TmFtZS9pbnN0YWxsZXJzLXVuc2lnbmVkKy0rTGludXg1/content?format=zip
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# with:
|
||||
# submodules: true
|
||||
|
||||
# - name: Setup Gradle
|
||||
# uses: gradle/actions/setup-gradle@v3
|
||||
|
||||
# - name: Cache NuGet packages
|
||||
# uses: actions/cache@v4
|
||||
# with:
|
||||
# path: ~/.nuget/packages
|
||||
# key: ${{ runner.os }}-nuget-${{ hashFiles('src/**/*.csproj', 'src/**/packages.config') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-nuget-
|
||||
|
||||
# - name: Cache Xamarin.Android packages
|
||||
# id: xamarin_cache
|
||||
# uses: actions/cache@v4
|
||||
# with:
|
||||
# path: ~/xamarin.android-oss
|
||||
# key: ${{ runner.os }}-xamarin.android-oss-${{ env.xamarin_url }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-xamarin.android-oss-${{ env.xamarin_url }}
|
||||
|
||||
# - name: Install Mono
|
||||
# if: ${{ false }} # disable for now since it is already installed on the runner which uses the same repo https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md#language-and-runtime
|
||||
# run: |
|
||||
# sudo apt install gnupg ca-certificates &&
|
||||
# sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF &&
|
||||
# echo "deb https://download.mono-project.com/repo/ubuntu stable-focal main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list &&
|
||||
# sudo apt update &&
|
||||
# sudo apt-get -y -t stable-focal install mono-complete
|
||||
|
||||
# - name: Download & unpack Xamarin.Android
|
||||
# if: steps.xamarin_cache.outputs.cache-hit != 'true'
|
||||
# run: |
|
||||
# set -x
|
||||
# cd $HOME &&
|
||||
# wget -O "installers-unsigned - Linux.zip" ${{ env.xamarin_url }} &&
|
||||
# unzip "installers-unsigned - Linux.zip" &&
|
||||
# mkdir -p xamarin.android-oss &&
|
||||
# DIR=$(unzip -Z -1 installers-unsigned\ -\ Linux.zip | cut -d '/' -f1 | sort -u) &&
|
||||
# tar -xvf "$DIR"/xamarin.android-oss-*.tar.* --strip-components=1 -C xamarin.android-oss &&
|
||||
# mv "$DIR"/*.deb xamarin.android-oss
|
||||
|
||||
# - name: Setup Xamarin.Android
|
||||
# run: |
|
||||
# cd $HOME &&
|
||||
# sudo apt install -y ./xamarin.android-oss/*.deb &&
|
||||
# echo "$HOME/xamarin.android-oss/bin/Release/bin" >> $GITHUB_PATH
|
||||
|
||||
# - name: Switch to JDK-11
|
||||
# uses: actions/setup-java@v4
|
||||
# with:
|
||||
# java-version: '11'
|
||||
# distribution: 'temurin'
|
||||
|
||||
# - name: Display java version
|
||||
# run: java -version
|
||||
|
||||
# # Some components of Keepass2Android currently target android API 26 which are not available on the runner
|
||||
# - name: Download android-26 API
|
||||
# run: $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-26"
|
||||
|
||||
# - name: Install libzip4
|
||||
# if: ${{ false }} # disable for now since it is already installed on the runner
|
||||
# run: sudo apt -y install libzip4
|
||||
|
||||
# - name: Build native dependencies
|
||||
# run: make native
|
||||
|
||||
# - name: Build java dependencies
|
||||
# run: make java
|
||||
|
||||
# - name: Install NuGet dependencies (net)
|
||||
# run: make nuget Flavor=Net
|
||||
|
||||
# - name: Build keepass2android (net)
|
||||
# run: |
|
||||
# make msbuild Flavor=Net
|
||||
|
||||
# - name: Build APK (net)
|
||||
# run: |
|
||||
# make apk Flavor=Net
|
||||
|
||||
# - name: Archive production artifacts (net)
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: signed APK ('net' built on ${{ github.job }})
|
||||
# path: |
|
||||
# src/keepass2android/bin/*/*-Signed.apk
|
||||
|
||||
# - name: Install NuGet dependencies (nonet)
|
||||
# run: make nuget Flavor=NoNet
|
||||
|
||||
# - name: Build keepass2android (nonet)
|
||||
# run: |
|
||||
# make msbuild Flavor=NoNet
|
||||
|
||||
# - name: Build APK (nonet)
|
||||
# run: |
|
||||
# make apk Flavor=NoNet
|
||||
|
||||
# - name: Archive production artifacts (nonet)
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: signed APK ('nonet' built on ${{ github.job }})
|
||||
# path: |
|
||||
# src/keepass2android/bin/*/*-Signed.apk
|
||||
|
||||
# - name: Perform "make distclean"
|
||||
# run: make distclean
|
||||
|
||||
windows:
|
||||
|
||||
# on windows-2022 it builds with:
|
||||
# Microsoft Visual Studio\2022\Enterprise
|
||||
# Found Java SDK version 11.0.12
|
||||
# Found Xamarin.Android 13.1.0.1
|
||||
#
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('src/**/*.csproj', 'src/**/packages.config') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-nuget-
|
||||
|
||||
# Workaround an issue when building on windows-2022. Error was
|
||||
# D8 : OpenJDK 64-Bit Server VM warning : INFO: os::commit_memory(0x00000000ae400000, 330301440, 0) failed; error='The paging file is too small for this operation to complete' (DOS error/errno=1455) [D:\a\keepass2android\keepass2android\src\keepass2android\keepass2android-app.csproj]
|
||||
# C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Xamarin\Android\Xamarin.Android.D8.targets(81,5): error MSB6006: "java.exe" exited with code 1. [D:\a\keepass2android\keepass2android\src\keepass2android\keepass2android-app.csproj]
|
||||
- name: Configure Pagefile
|
||||
uses: al-cheb/configure-pagefile-action@a3b6ebd6b634da88790d9c58d4b37a7f4a7b8708 # v1.4
|
||||
with:
|
||||
minimum-size: 8GB
|
||||
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
# If we want to also have nmake, use this instead
|
||||
#uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Switch to JDK-17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Display java version
|
||||
run: java -version
|
||||
|
||||
# Some components of Keepass2Android currently target android API 26 which are not available on the runner
|
||||
- name: Download android-26 API
|
||||
shell: cmd
|
||||
run: |
|
||||
%ANDROID_SDK_ROOT%\cmdline-tools\latest\bin\sdkmanager --install "platforms;android-26"
|
||||
|
||||
- name: Build native dependencies
|
||||
shell: cmd
|
||||
run: |
|
||||
make native
|
||||
|
||||
- name: Build java dependencies
|
||||
shell: cmd
|
||||
run: |
|
||||
make java
|
||||
|
||||
- name: Install NuGet dependencies (net)
|
||||
run: make nuget Flavor=Net
|
||||
|
||||
- name: Build keepass2android (net)
|
||||
run: |
|
||||
make msbuild Flavor=Net
|
||||
|
||||
- name: Build APK (net)
|
||||
run: |
|
||||
make apk Flavor=Net
|
||||
|
||||
- name: Archive production artifacts (net)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: signed APK ('net' built on ${{ github.job }})
|
||||
path: |
|
||||
src/keepass2android/bin/*/*-Signed.apk
|
||||
|
||||
- name: Install NuGet dependencies (nonet)
|
||||
run: make nuget Flavor=NoNet
|
||||
|
||||
- name: Build keepass2android (nonet)
|
||||
run: |
|
||||
make msbuild Flavor=NoNet
|
||||
- name: Test Autofill
|
||||
working-directory: ./src/Kp2aAutofillParserTest
|
||||
run: dotnet test
|
||||
|
||||
- name: Build APK (nonet)
|
||||
run: |
|
||||
make apk Flavor=NoNet
|
||||
|
||||
- name: Archive production artifacts (nonet)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: signed APK ('nonet' built on ${{ github.job }})
|
||||
path: |
|
||||
src/keepass2android/bin/*/*-Signed.apk
|
||||
|
||||
- name: Perform "make distclean"
|
||||
run: make distclean
|
||||
8
.gitignore
vendored
@@ -7,6 +7,10 @@ PCtest
|
||||
bin
|
||||
obj
|
||||
|
||||
# Makefile-related files
|
||||
/allow_git_clean
|
||||
/stamp.nuget_*
|
||||
|
||||
Resource.designer.cs
|
||||
R.java
|
||||
|
||||
@@ -105,7 +109,6 @@ Thumbs.db
|
||||
/src/java/JavaFileStorageTest/gen/group/pals/android/lib/ui/filechooser/R.java
|
||||
/src/java/JavaFileStorageTest/gen/keepass2android/javafilestorage/R.java
|
||||
|
||||
/src/TwofishCipher/Resources/Resource.Designer.cs
|
||||
/src/BindingLibrary1
|
||||
|
||||
/src/PluginTOTP
|
||||
@@ -171,3 +174,6 @@ src/java/Keepass2AndroidPluginSDK2/build/generated/mockable-Google-Inc.-Google-A
|
||||
/src/java/KP2ASoftkeyboard_AS/app/src/main/libs
|
||||
/src/java/KP2AKdbLibrary/app/.cxx
|
||||
/src/ActionViewFilterTest
|
||||
/docs/gdrive-verification
|
||||
/src/MegaTest
|
||||
*.dtbcache.json
|
||||
|
||||
361
Makefile
Normal file
@@ -0,0 +1,361 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
#
|
||||
# This Makefile can be used on both unix-like (use make) & windows (with GNU make)
|
||||
#
|
||||
# append the Configuration variable to 'make' call with value to use in '/p:Configuration='
|
||||
# of msbuild command.
|
||||
#
|
||||
# append the Flavor variable to 'make' call with value to use in '/p:Flavor='
|
||||
# of msbuild command.
|
||||
#
|
||||
# Example:
|
||||
# make Configuration=Release Flavor=NoNet
|
||||
#
|
||||
#
|
||||
# Some targets:
|
||||
# - all: everything (including APK)
|
||||
# - native: build the native libs
|
||||
# - java: build the java libs
|
||||
# - nuget: restore NuGet packages
|
||||
# - msbuild: build the project
|
||||
# - apk: same as all
|
||||
#
|
||||
# - distclean: run a 'git clean -xdff'. Remove everyhing that is not in the git tree.
|
||||
# - clean: all clean_* targets below
|
||||
# - clean_native: clean native lib
|
||||
# - clean_java: call clean target of java libs
|
||||
# - clean_nuget: cleanup the 'nuget restore'
|
||||
# - clean_msbuild: call clean target of msbuild
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
# Disable built-in rules to speed-up the Makefile processing.
|
||||
# for example when running 'make java' on Windows it could take ~10 sec more than on linux to start building
|
||||
# from what this option disables, the "clearing out the default list of suffixes for suffix rules"
|
||||
# gives the most speed gain.
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
|
||||
ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10...
|
||||
detected_OS := Windows
|
||||
WHICH := where
|
||||
RM := RMDIR /S /Q
|
||||
RMFILE := DEL
|
||||
CP := copy
|
||||
GRADLEW := gradlew.bat
|
||||
# Force use of cmd shell (don't use POSIX shell because the user may not have one installed)
|
||||
SHELL := cmd
|
||||
else
|
||||
detected_OS := $(shell uname)
|
||||
WHICH := which
|
||||
RM := rm -rf
|
||||
RMFILE := $(RM)
|
||||
CP := cp
|
||||
GRADLEW := ./gradlew
|
||||
endif
|
||||
|
||||
$(info MAKESHELL: $(MAKESHELL))
|
||||
$(info SHELL: $(SHELL))
|
||||
$(info )
|
||||
|
||||
# On linux use xabuild, on Windows use MSBuild.exe, otherwise (macos?) use msbuild.
|
||||
ifeq ($(detected_OS),Linux)
|
||||
MSBUILD_binary := xabuild
|
||||
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary))
|
||||
else ifeq ($(detected_OS),Windows)
|
||||
MSBUILD_binary := MSBuild.exe
|
||||
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary) 2> nul)
|
||||
ifeq ($(MSBUILD),)
|
||||
# Additional heuristic to find MSBUILD_BINARY on Windows
|
||||
VSWHERE := "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
VSWHERE_CHECK := $(shell @echo off & $(VSWHERE) 2> nul || echo VSWHERE_NOT_FOUND)
|
||||
ifneq ($(VSWHERE_CHECK),VSWHERE_NOT_FOUND)
|
||||
MSBUILD := $(shell @echo off & $(VSWHERE) -latest -prerelease -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe)
|
||||
VS_INSTALL_PATH := $(shell @echo off & $(VSWHERE) -property installationPath)
|
||||
endif
|
||||
endif
|
||||
else
|
||||
MSBUILD_binary := msbuild
|
||||
MSBUILD := $(shell $(WHICH) $(MSBUILD_binary))
|
||||
endif
|
||||
|
||||
ifeq ($(MSBUILD),)
|
||||
$(info )
|
||||
$(info '$(MSBUILD_binary)' binary could not be found. Check it is in your PATH.)
|
||||
ifeq ($(detected_OS),Windows)
|
||||
ifneq ($(VSWHERE_CHECK),VSWHERE_NOT_FOUND)
|
||||
$(info )
|
||||
$(info You may retry after running in the command prompt:)
|
||||
$(info )
|
||||
$(info "$(VS_INSTALL_PATH)\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64)
|
||||
$(info )
|
||||
$(info If this doesn't work, install/find the location of vcvarsall.bat)
|
||||
$(info or install and add msbuild.exe to your PATH)
|
||||
$(info )
|
||||
endif
|
||||
endif
|
||||
$(error )
|
||||
endif
|
||||
$(info MSBUILD: $(MSBUILD))
|
||||
$(info )
|
||||
|
||||
ifeq ($(ANDROID_SDK_ROOT),)
|
||||
$(error set ANDROID_SDK_ROOT environment variable)
|
||||
endif
|
||||
$(info ANDROID_SDK_ROOT: $(ANDROID_SDK_ROOT))
|
||||
|
||||
ifeq ($(ANDROID_HOME),)
|
||||
$(error set ANDROID_HOME environment variable)
|
||||
endif
|
||||
$(info ANDROID_HOME: $(ANDROID_SDK_ROOT))
|
||||
|
||||
ifeq ($(ANDROID_NDK_ROOT),)
|
||||
$(error set ANDROID_NDK_ROOT environment variable)
|
||||
endif
|
||||
$(info ANDROID_NDK_ROOT: $(ANDROID_NDK_ROOT))
|
||||
|
||||
ifneq ($(Configuration),)
|
||||
MSBUILD_PARAM = -p:Configuration="$(Configuration)"
|
||||
else
|
||||
$(warning Configuration environment variable not set.)
|
||||
endif
|
||||
ifneq ($(Flavor),)
|
||||
MSBUILD_PARAM += -p:Flavor="$(Flavor)"
|
||||
else
|
||||
$(warning Flavor environment variable not set.)
|
||||
endif
|
||||
|
||||
ifneq ($(KeyStore),)
|
||||
MSBUILD_PARAM += -p:AndroidKeyStore=True -p:AndroidSigningKeyStore="$(KeyStore)" -p:AndroidSigningStorePass=env:MyAndroidSigningStorePass -p:AndroidSigningKeyPass=env:MyAndroidSigningKeyPass
|
||||
endif
|
||||
|
||||
ifeq ($(detected_OS),Windows)
|
||||
to_win_path=$(subst /,\,$(1))
|
||||
to_posix_path=$(subst \,/,$(1))
|
||||
define remove_dir
|
||||
if exist $(1) ( $(RM) $(1) )
|
||||
endef
|
||||
define remove_files
|
||||
$(foreach file,$(call to_win_path,$(1)), IF EXIST $(file) ( $(RMFILE) $(file) ) & )
|
||||
endef
|
||||
else
|
||||
define remove_dir
|
||||
$(RM) $(1)
|
||||
endef
|
||||
define remove_files
|
||||
$(RMFILE) $(1)
|
||||
endef
|
||||
endif
|
||||
|
||||
# Recursive wildcard: https://stackoverflow.com/a/18258352
|
||||
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
|
||||
|
||||
$(info MSBUILD_PARAM: $(MSBUILD_PARAM))
|
||||
$(info nuget path: $(shell $(WHICH) nuget))
|
||||
$(info )
|
||||
|
||||
NATIVE_COMPONENTS := argon2
|
||||
NATIVE_CLEAN_TARGETS := clean_argon2
|
||||
|
||||
OUTPUT_argon2 = src/java/argon2/libs/armeabi-v7a/libargon2.so \
|
||||
src/java/argon2/libs/arm64-v8a/libargon2.so \
|
||||
src/java/argon2/libs/x86/libargon2.so \
|
||||
src/java/argon2/libs/x86_64/libargon2.so
|
||||
|
||||
JAVA_COMPONENTS := \
|
||||
JavaFileStorageTest-AS \
|
||||
KP2ASoftkeyboard_AS \
|
||||
Keepass2AndroidPluginSDK2 \
|
||||
KP2AKdbLibrary
|
||||
#PluginQR # Doesn't seem required
|
||||
JAVA_CLEAN_TARGETS := \
|
||||
clean_JavaFileStorageTest-AS \
|
||||
clean_KP2ASoftkeyboard_AS \
|
||||
clean_Keepass2AndroidPluginSDK2 \
|
||||
clean_KP2AKdbLibrary \
|
||||
clean_PluginQR
|
||||
|
||||
INPUT_android-filechooser-AS := $(filter-out $(filter %/app,$(wildcard src/java/android-filechooser-AS/*)),$(wildcard src/java/android-filechooser-AS/*)) \
|
||||
$(filter-out $(filter %/build,$(wildcard src/java/android-filechooser-AS/app/*)),$(wildcard src/java/android-filechooser-AS/app/*)) \
|
||||
$(call rwildcard,src/java/android-filechooser-AS/app/src,*)
|
||||
|
||||
INPUT_JavaFileStorage := $(filter-out $(filter %/app,$(wildcard src/java/JavaFileStorage/*)),$(wildcard src/java/JavaFileStorage/*)) \
|
||||
$(filter-out $(filter %/build,$(wildcard src/java/JavaFileStorage/app/*)),$(wildcard src/java/JavaFileStorage/app/*)) \
|
||||
$(wildcard src/java/JavaFileStorage/libs/*) \
|
||||
$(call rwildcard,src/java/JavaFileStorage/app/src,*) \
|
||||
|
||||
INPUT_JavaFileStorageTest-AS := $(filter-out $(filter %/app,$(wildcard src/java/JavaFileStorageTest-AS/*)),$(wildcard src/java/JavaFileStorageTest-AS/*)) \
|
||||
$(filter-out $(filter %/build,$(wildcard src/java/JavaFileStorageTest-AS/app/*)),$(wildcard src/java/JavaFileStorageTest-AS/app/*)) \
|
||||
$(call rwildcard,src/java/JavaFileStorageTest-AS/app/src,*) \
|
||||
$(INPUT_android-filechooser-AS) \
|
||||
$(INPUT_JavaFileStorage)
|
||||
OUTPUT_JavaFileStorageTest-AS = src/java/android-filechooser-AS/app/build/outputs/aar/android-filechooser-debug.aar \
|
||||
src/java/android-filechooser-AS/app/build/outputs/aar/android-filechooser-release.aar \
|
||||
src/java/JavaFileStorage/app/build/outputs/aar/JavaFileStorage-debug.aar \
|
||||
src/java/JavaFileStorage/app/build/outputs/aar/JavaFileStorage-release.aar \
|
||||
src/java/JavaFileStorageTest-AS/app/build/outputs/apk/debug/app-debug.apk \
|
||||
src/java/JavaFileStorageTest-AS/app/build/outputs/apk/release/app-release-unsigned.apk
|
||||
|
||||
INPUT_KP2ASoftkeyboard_AS := $(wildcard src/java/KP2ASoftkeyboard_AS/*) \
|
||||
$(wildcard src/java/KP2ASoftkeyboard_AS/app/*) \
|
||||
$(call rwildcard,src/java/KP2ASoftkeyboard_AS/app/src,*)
|
||||
OUTPUT_KP2ASoftkeyboard_AS = src/java/KP2ASoftkeyboard_AS/app/build/outputs/aar/app-debug.aar \
|
||||
src/java/KP2ASoftkeyboard_AS/app/build/outputs/aar/app-release.aar
|
||||
|
||||
INPUT_Keepass2AndroidPluginSDK2 := $(wildcard src/java/Keepass2AndroidPluginSDK2/*) \
|
||||
$(wildcard src/java/Keepass2AndroidPluginSDK2/app/*) \
|
||||
$(call rwildcard,src/java/Keepass2AndroidPluginSDK2/app/src,*)
|
||||
OUTPUT_Keepass2AndroidPluginSDK2 = src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/app-debug.aar \
|
||||
src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/app-release.aar
|
||||
|
||||
INPUT_KP2AKdbLibrary := $(wildcard src/java/KP2AKdbLibrary/*) \
|
||||
$(wildcard src/java/KP2AKdbLibrary/app/*) \
|
||||
$(call rwildcard,src/java/KP2AKdbLibrary/app/src,*)
|
||||
OUTPUT_KP2AKdbLibrary = src/java/KP2AKdbLibrary/app/build/outputs/aar/app-debug.aar \
|
||||
src/java/KP2AKdbLibrary/app/build/outputs/aar/app-release.aar
|
||||
|
||||
INPUT_PluginQR := $(wildcard src/java/PluginQR/*) \
|
||||
$(wildcard src/java/PluginQR/app/*) \
|
||||
$(call rwildcard,src/java/PluginQR/app/src,*) \
|
||||
$(INPUT_Keepass2AndroidPluginSDK2)
|
||||
OUTPUT_PluginQR = src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/Keepass2AndroidPluginSDK2-debug.aar \
|
||||
src/java/Keepass2AndroidPluginSDK2/app/build/outputs/aar/Keepass2AndroidPluginSDK2-release.aar \
|
||||
src/java/PluginQR/app/build/outputs/apk/debug/app-debug.apk \
|
||||
src/java/PluginQR/app/build/outputs/apk/debug/app-release-unsigned.apk
|
||||
|
||||
##### Targets definition
|
||||
|
||||
.PHONY: native $(NATIVE_COMPONENTS) clean_native $(NATIVE_CLEAN_TARGETS) \
|
||||
java $(JAVA_COMPONENTS) clean_java $(JAVA_CLEAN_TARGETS) \
|
||||
nuget clean_nuget \
|
||||
msbuild clean_msbuild \
|
||||
apk all clean
|
||||
|
||||
all: apk
|
||||
|
||||
##### Native Dependencies
|
||||
|
||||
native: $(NATIVE_COMPONENTS)
|
||||
|
||||
argon2: $(OUTPUT_argon2)
|
||||
$(OUTPUT_argon2): $(wildcard src/java/argon2/phc-winner-argon2/src/*) $(wildcard src/java/argon2/phc-winner-argon2/src/blake2/*)
|
||||
cd src/java/argon2 && $(ANDROID_NDK_ROOT)/ndk-build
|
||||
|
||||
##### Java Dependencies
|
||||
|
||||
java: $(JAVA_COMPONENTS)
|
||||
|
||||
JavaFileStorageTest-AS: $(OUTPUT_JavaFileStorageTest-AS)
|
||||
KP2ASoftkeyboard_AS: $(OUTPUT_KP2ASoftkeyboard_AS)
|
||||
Keepass2AndroidPluginSDK2: $(OUTPUT_Keepass2AndroidPluginSDK2)
|
||||
KP2AKdbLibrary: $(OUTPUT_KP2AKdbLibrary)
|
||||
PluginQR: $(OUTPUT_PluginQR)
|
||||
|
||||
$(OUTPUT_JavaFileStorageTest-AS): $(INPUT_JavaFileStorageTest-AS)
|
||||
$(call remove_files,$(OUTPUT_JavaFileStorageTest-AS))
|
||||
cd src/java/JavaFileStorageTest-AS && $(GRADLEW) assemble
|
||||
$(OUTPUT_KP2ASoftkeyboard_AS): $(INPUT_KP2ASoftkeyboard_AS)
|
||||
$(call remove_files,$(OUTPUT_KP2ASoftkeyboard_AS))
|
||||
cd src/java/KP2ASoftkeyboard_AS && $(GRADLEW) assemble
|
||||
$(OUTPUT_Keepass2AndroidPluginSDK2): $(INPUT_Keepass2AndroidPluginSDK2)
|
||||
$(call remove_files,$(OUTPUT_Keepass2AndroidPluginSDK2))
|
||||
cd src/java/Keepass2AndroidPluginSDK2 && $(GRADLEW) assemble
|
||||
$(OUTPUT_KP2AKdbLibrary): $(INPUT_KP2AKdbLibrary)
|
||||
$(call remove_files,$(OUTPUT_KP2AKdbLibrary))
|
||||
cd src/java/KP2AKdbLibrary && $(GRADLEW) assemble
|
||||
$(OUTPUT_PluginQR): $(INPUT_PluginQR)
|
||||
$(call remove_files,$(OUTPUT_PluginQR))
|
||||
cd src/java/PluginQR && $(GRADLEW) assemble
|
||||
|
||||
|
||||
##### Nuget Dependencies
|
||||
|
||||
nuget: stamp.nuget_$(Flavor)
|
||||
stamp.nuget_$(Flavor): src/KeePass.sln $(wildcard src/*/*.csproj) $(wildcard src/*/packages.config)
|
||||
ifeq ($(shell $(WHICH) nuget),)
|
||||
$(error "nuget" command not found. Check it is in your PATH)
|
||||
endif
|
||||
$(RMFILE) stamp.nuget_*
|
||||
nuget restore src/KeePass.sln
|
||||
$(MSBUILD) src/KeePass.sln -t:restore $(MSBUILD_PARAM) -p:RestorePackagesConfig=true
|
||||
@echo "" > stamp.nuget_$(Flavor)
|
||||
|
||||
#####
|
||||
src/Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs:
|
||||
ifeq ($(detected_OS),Windows)
|
||||
$(CP) src\Kp2aBusinessLogic\Io\DropboxFileStorageKeysDummy.cs src\Kp2aBusinessLogic\Io\DropboxFileStorageKeys.cs
|
||||
else
|
||||
$(CP) src/Kp2aBusinessLogic/Io/DropboxFileStorageKeysDummy.cs $@
|
||||
endif
|
||||
|
||||
msbuild: native java nuget src/Kp2aBusinessLogic/Io/DropboxFileStorageKeys.cs
|
||||
$(MSBUILD) src/KeePass.sln -target:keepass2android-app -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -p:BuildProjectReferences=true $(MSBUILD_PARAM) -p:Platform="Any CPU" -m
|
||||
|
||||
apk: msbuild
|
||||
$(MSBUILD) src/keepass2android/keepass2android-app.csproj -p:AndroidSdkDirectory="$(ANDROID_SDK_ROOT)" -t:SignAndroidPackage $(MSBUILD_PARAM) -p:Platform=AnyCPU -m
|
||||
|
||||
build_all: msbuild
|
||||
|
||||
##### Cleanup targets
|
||||
|
||||
clean_native: $(NATIVE_CLEAN_TARGETS)
|
||||
clean_argon2:
|
||||
cd src/java/argon2 && $(ANDROID_NDK_ROOT)/ndk-build clean
|
||||
|
||||
clean_java: $(JAVA_CLEAN_TARGETS)
|
||||
clean_JavaFileStorageTest-AS:
|
||||
cd src/java/JavaFileStorageTest-AS && $(GRADLEW) clean
|
||||
clean_KP2ASoftkeyboard_AS:
|
||||
cd src/java/KP2ASoftkeyboard_AS && $(GRADLEW) clean
|
||||
clean_Keepass2AndroidPluginSDK2:
|
||||
cd src/java/Keepass2AndroidPluginSDK2 && $(GRADLEW) clean
|
||||
clean_KP2AKdbLibrary:
|
||||
cd src/java/KP2AKdbLibrary && $(GRADLEW) clean
|
||||
clean_PluginQR:
|
||||
cd src/java/PluginQR && $(GRADLEW) clean
|
||||
clean_rm:
|
||||
rm -rf src/*/obj
|
||||
rm -rf src/*/bin
|
||||
rm -rf src/java/*/app/build
|
||||
rm -rf src/java/argon2/obj
|
||||
rm -rf src/java/argon2/libs
|
||||
rm -rf src/packages
|
||||
rm -rf src/java/KP2AKdbLibrary/app/.cxx
|
||||
rm -rf src/java/KP2ASoftkeyboard_AS/app/.cxx
|
||||
rm -rf src/SamsungPass/Xamarin.SamsungPass/SamsungPass/bin
|
||||
rm -rf src/SamsungPass/Xamarin.SamsungPass/SamsungPass/obj
|
||||
|
||||
|
||||
# https://learn.microsoft.com/en-us/nuget/consume-packages/package-restore-troubleshooting#other-potential-conditions
|
||||
clean_nuget:
|
||||
cd src && $(call remove_dir,packages)
|
||||
ifeq ($(detected_OS),Windows)
|
||||
DEL /S src\project.assets.json
|
||||
DEL /S src\*.nuget.*
|
||||
else
|
||||
$(RM) src/*/obj/project.assets.json
|
||||
$(RM) src/*/obj/*.nuget.*
|
||||
endif
|
||||
$(RMFILE) stamp.nuget_*
|
||||
|
||||
clean_msbuild:
|
||||
$(MSBUILD) src/KeePass.sln -target:clean $(MSBUILD_PARAM)
|
||||
|
||||
clean: clean_native clean_java clean_nuget clean_msbuild
|
||||
|
||||
distclean: clean
|
||||
ifneq ("$(wildcard ./allow_git_clean)","")
|
||||
ifeq ($(shell $(WHICH) git),)
|
||||
$(error "git" command not found. Check it is in your PATH)
|
||||
endif
|
||||
git clean -xdff src
|
||||
else
|
||||
$(warning 'git clean' skipped for safety reasons. See hint below:)
|
||||
$(info )
|
||||
$(info 'git clean' would delete all untracked files, those in '.gitignore' and those in '.git/info/exclude'.)
|
||||
$(info )
|
||||
$(info Check which files would be deleted by running: "git clean -n -xdff src")
|
||||
$(info If listed files are acceptable, you can enable the call to "git clean" by creating an empty file named 'allow_git_clean' next to the Makefile.)
|
||||
$(info )
|
||||
endif
|
||||
239
docs/Build.readme.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# How to build Keepass2Android
|
||||
|
||||
## Overview
|
||||
|
||||
Keepass2Android is a Mono for Android app. This means that you need Xamarin's Mono for Android to build it. However, it also uses several components written in Java, so there are also Android-Studio projects involved. To make things even worse, parts of the keyboard and kdb-library are written in native code.
|
||||
|
||||
To build KP2A from scratch, you need:
|
||||
- Xamarin's Mono for Android (also included in Visual Studio)
|
||||
- Android SDK & NDK
|
||||
|
||||
Prior to building Keepass2Android, you need to build some of its components (from command line). Then you can build the full project either through Visual Studio, or through command line.
|
||||
|
||||
By using the command line, you can build on Windows, macOS or Linux.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Common to all architectures
|
||||
- Install Android SDK & NDK (either manually with Google's [sdkmanager](https://developer.android.com/studio/command-line/sdkmanager), or through Android Studio). Visual Studio also installs a version of it, but in the end the directory must be writable and in a path without spaces (see below) so as to be able to build the components.
|
||||
- Fetch the main repository of Keepass2Android and all submodules
|
||||
- Note that VisualStudio can do this for you, otherwise run:
|
||||
- `git submodule init && git submodule update`
|
||||
|
||||
### On Windows or macOS
|
||||
- Install Visual Studio (for example 2019) with Xamarin.Android (ie. with capability to build Android apps). This should provide the needed tools like
|
||||
- Xamarin.Android
|
||||
- MSBuild
|
||||
- Java JDK
|
||||
- If you plan to build also from the command line:
|
||||
- Install the MSVC build tools of visual studio. They provide the `vcvarsall.bat` file which among other things adds MSBuild to the PATH.
|
||||
- Install [NuGet](https://www.nuget.org/downloads) to build also with "make". Alternatively, on Windows, if you use [chocolatey](https://chocolatey.org), run as administrator:
|
||||
- `choco install nuget.commandline`
|
||||
- Check that you have access to 'GNU make'.
|
||||
- On Windows, it is usually not available by default. But the Android NDK provides it. You can find it in `%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make.exe`. Alternatively, on Windows, if you use [chocolatey](https://chocolatey.org), run as administrator:
|
||||
- `choco install make`
|
||||
- On macOS, it is usually only installed if you have developer command line tools installed or if you use [homebrew](https://brew.sh) or [macports](https://www.macports.org/). As an alternative it may be available in the Android NDK at `%ANDROID_NDK_ROOT%/prebuilt/darwin-x86_64/bin/make`.
|
||||
|
||||
### On Linux
|
||||
- Install Java's JDK
|
||||
- On Debian, for example: `apt install default-jdk-headless`.
|
||||
|
||||
- Install [Mono](https://www.mono-project.com/)
|
||||
- This should provide `msbuild` & `xabuild` binary
|
||||
- On Debian, after having added the repo from above, install with `apt install -t <repo_name> mono-devel msbuild`. A value for `<repo_name>` could be `stable-buster` for example, depending on which one you chose. You could also install the `mono-complete` package if you prefer.
|
||||
|
||||
- Install Xamarin.Android
|
||||
- ~~Option 1: Use the mono-project [CI builds](https://dev.azure.com/xamarin/public/_build/latest?definitionId=48&branchName=main&stageName=Linux)~~ **NOTE:** KP2A now requires Xamarin.Android v13, which is newer than the current CI build; until a more recent CI build is available, this option is unfortunately no longer viable.
|
||||
- Option 2: [Build it from source](https://github.com/xamarin/xamarin-android/blob/master/Documentation/README.md#building-from-source)
|
||||
|
||||
- Install NuGet package of your distribution
|
||||
- On Debian/Ubuntu: `apt install nuget`
|
||||
|
||||
- Install [libzip](https://libzip.org/) for your distribution for some Xamarin.Android versions
|
||||
- This may not be relevant anymore: for example, with Xamarin.Android 11.4.99. this is not needed.
|
||||
- Some versions of Xamarin may require `libzip4`. If you are in this case:
|
||||
- On Debian/Ubuntu, install it with `apt install libzip4`.
|
||||
- Other distributions ship only `libzip5`. As a dirty workaround, it's possible to symlink `libzip.so.5` to `libzip.so.4`. Luckily, it appears to be working. For example:
|
||||
- `sudo ln -s /usr/lib/libzip.so.5 /usr/lib/libzip.so.4`
|
||||
- or `sudo ln -s /usr/lib64/libzip.so.5 /usr/lib/libzip.so.4`
|
||||
|
||||
## Building the required components:
|
||||
|
||||
This is done on the command line and requires the Android SDK & NDK and Java JDK.
|
||||
|
||||
### On Windows
|
||||
- Setup your environment:
|
||||
- Set these environment variables for Android's SDK & NDK
|
||||
- `ANDROID_HOME` (for example `set ANDROID_HOME=C:\PATH\TO\android-sdk`)
|
||||
- `ANDROID_SDK_ROOT` (for example `set ANDROID_SDK_ROOT=C:\PATH\TO\android-sdk`)
|
||||
- `ANDROID_NDK_ROOT` (for example `set ANDROID_NDK_ROOT=C:\PATH\TO\android-sdk\ndk\version`)
|
||||
|
||||
**Note:** Care must be taken when setting the above variables to **not** include a trailing backslash in the path. A trailing backslash may cause `make` to fail.
|
||||
|
||||
**Note**: If the path to the Android SDK contains spaces, you **must** do one of these:
|
||||
- either put the Android SDK into a path without spaces.
|
||||
- or create a symlink to that path which doesn't contain spaces. Attention: this requires **administrator** priveleges. For example:
|
||||
|
||||
```
|
||||
IF NOT EXIST C:\Android ( MKDIR C:\Android ) &&
|
||||
MKLINK /D C:\Android\android-sdk "C:\Program Files (x86)\Android\android-sdk"
|
||||
```
|
||||
This is because [Android NDK doesn't support being installed in a path with spaces](https://github.com/android/ndk/issues/1400).
|
||||
|
||||
**Note**: The Android SDK path will require to be writeable because during the build, some missing components might be downloaded & installed.
|
||||
|
||||
- If you have "GNU make" available on your windows system, you may build by using the Makefile. You can also find a `make` executable in `%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make.exe`. To use it, see the instructions for Linux/macOS. Basically, just run `make` or `mingw32-make` depending on which distribution of GNU make for windows you have installed.
|
||||
|
||||
- Otherwise proceed as below:
|
||||
|
||||
1. Build argon2
|
||||
|
||||
```
|
||||
cd src/java/argon2
|
||||
%ANDROID_NDK_ROOT%/ndk-build.cmd
|
||||
```
|
||||
1. Build the other java components
|
||||
|
||||
```
|
||||
cd src/build-scripts
|
||||
build-java.bat
|
||||
```
|
||||
|
||||
`build-java.bat` will call `gradlew` for several Java modules.
|
||||
|
||||
**Notes:**
|
||||
|
||||
- For building the java parts, it is suggested to keep a short name (e.g. "c:\projects\keepass2android") for the root project directory. Otherwise the Windows path length limit might be hit when building.
|
||||
- Before building the java parts, make sure you have set the ANDROID_HOME variable or create a local.properties file inside the directories with a gradlew file. It is recommended to use the same SDK location as that of the Xamarin build.
|
||||
- On some environments, `make` can fail to properly use the detected `MSBUILD` tools. This seems to be due to long pathnames and/or spaces in pathnames. It may be required to explicitly set the `MSBUILD` path using 8.3 "short" path notation:
|
||||
- Determine the location of `MSBUILD` (e.g. `C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe`)
|
||||
- [Generate the "short" path](https://superuser.com/a/728792) of that location (e.g.: `C:\PROGRA~1\MICROS~2\2022\COMMUN~1\MSBuild\Current\Bin\MSBuild.exe`)
|
||||
- When running `make` specify the location of ``MSBUILD` explicitly (e.g.: `make MSBUILD="C:\PROGRA~1\MICROS~2\2022\COMMUN~1\MSBuild\Current\Bin\MSBuild.exe`
|
||||
|
||||
|
||||
### On Linux/macOS
|
||||
|
||||
- Setup your environment:
|
||||
- Set these environment variables for Android's SDK & NDK
|
||||
- `ANDROID_HOME` (for example `export ANDROID_HOME=/path/to/android-sdk/`)
|
||||
- `ANDROID_SDK_ROOT` (for example `export ANDROID_SDK_ROOT=/path/to/android-sdk/`)
|
||||
- `ANDROID_NDK_ROOT` (for example `export ANDROID_NDK_ROOT=/path/to/android-sdk/ndk/version`)
|
||||
|
||||
- Update your PATH environment variable so that it can access `nuget`, `msbuild` or `xabuild` (for linux):
|
||||
- On Linux:
|
||||
- add `xabuild` to your path: `export PATH=/path/to/xamarin.android-oss/bin/Release/bin/:$PATH`
|
||||
- On macOS:
|
||||
- you may similarly need to add `msbuild` & `nuget` to your PATH.
|
||||
|
||||
- Start the build:
|
||||
- This will use the Makefile at the root of the project (requires GNU make). To build everything (components & Keepass2Android APK) in a single command simply run:
|
||||
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
- Otherwise, if you prefer to do step by step
|
||||
|
||||
1. Build argon2
|
||||
|
||||
```
|
||||
make native
|
||||
```
|
||||
|
||||
1. Build the other java components
|
||||
|
||||
```
|
||||
make java
|
||||
```
|
||||
|
||||
## Building Keepass2Android:
|
||||
|
||||
These are the basic steps to build Keepass2Android. You can also build Keepass2Android Offline. For this, configure the build by using the [Flavors](#Flavors).
|
||||
|
||||
### With Visual Studio
|
||||
|
||||
- On windows or on macOS open the src/KeePass.sln file with visual studio, and choose to build the project named 'keepass2android-app'
|
||||
|
||||
### Command Line
|
||||
|
||||
#### Windows, Macos & Linux
|
||||
to build the APK, simply run:
|
||||
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
or to skip building the APK:
|
||||
|
||||
```
|
||||
make msbuild
|
||||
```
|
||||
|
||||
## Where is the APK ?
|
||||
The Apk can be installed on a device.
|
||||
It is located in `src/keepass2android/bin/*/*-Signed.apk`
|
||||
|
||||
If you build with Visual Studio, the APK is not produced automatically. You need to perform some extra step. See the documentation of Visual Studio on how to proceed.
|
||||
|
||||
## Flavors
|
||||
|
||||
Keepass2Android is distributed in two flavors.
|
||||
- Keepass2Android (aka `net`)
|
||||
- Keepass2Android Offline (aka `nonet`)
|
||||
|
||||
The flavor is set through a MSBuild Property named "`Flavor`". The possible values are '`Net`' and '`NoNet`'.
|
||||
|
||||
The value of the Flavor property is used in 2 projects:
|
||||
- `keepass2android-app` (in `src/keepass2android`)
|
||||
- `Kp2aBusinessLogic` (in `src/keepass2android`)
|
||||
|
||||
Its value is set inside the `*.csproj` file (XML format) of each project in the `Project`/`PropertyGroup`/`Flavor` node.
|
||||
By default its value is set to an empty string so that development is made with `AndroidManifest_debug.xml` on the '`net`' flavor.
|
||||
|
||||
This is the behaviour of the build system depending on the value of Flavor:
|
||||
| Flavor | What is built | `AndroidManifest.xml` used |
|
||||
| ----- | ----- | ----- |
|
||||
| `` (empty string): This is the default value. | Keepass2Android | `AndroidManifest_debug.xml` |
|
||||
| `Net` | Keepass2Android | `AndroidManifest_net.xml` |
|
||||
| `NoNet` | Keepass2Android Offline | `AndroidManifest_nonet.xml` |
|
||||
|
||||
### Select/Change flavor:
|
||||
|
||||
When building, by default, the flavor is not set. So the value used is the value of the Flavor property in *.csproj file. This should result on doing a build of the 'net' flavor.
|
||||
|
||||
You can force the Flavor by setting the Flavor property.
|
||||
|
||||
Proceed this way:
|
||||
|
||||
#### Command line
|
||||
|
||||
##### Windows, Macos & Linux
|
||||
|
||||
To force building 'net' with `make`, run:
|
||||
|
||||
```
|
||||
make Flavor=Net
|
||||
```
|
||||
|
||||
To build 'nonet' with `make`, run:
|
||||
|
||||
```
|
||||
make Flavor=NoNet
|
||||
```
|
||||
|
||||
##### MSBuild
|
||||
|
||||
To build with MSBuild directly on the command line, set the flavor with `-p:Flavor=value` argument. For example:
|
||||
|
||||
```
|
||||
MSBuild src/KeePass.sln ... -p:Flavor=NoNet
|
||||
```
|
||||
|
||||
#### Visual Studio
|
||||
When building with Visual Studio, edit the `*.csproj` file (XML format) and set the value in the `Project`/`PropertyGroup`/`Flavor` node. This is needed only for the projects that use the flavors.
|
||||
|
||||
**Note:** When switching between flavors, be sure to clean the previous build before.
|
||||
|
||||
## Makefile
|
||||
|
||||
It is possible to override the project's default 'Flavor' (Net, NoNet) and 'Configuration' (Release, Debug) by passing it as argument to `make`. See the header of the Makefile to see what can be done.
|
||||
@@ -68,6 +68,9 @@ Please see the [How to use Keepass2Android with YubiKey NEO](How-to-use-Keepass2
|
||||
## Advanced usage of the Keepass2Android keyboard
|
||||
Please see the [Advanced usage of the Keepass2Android keyboard](Advanced-usage-of-the-Keepass2Android-keyboard.md) page.
|
||||
|
||||
## Using Keepass2Android like an authenticator app to generate Time-based One-Time-Passwords (TOTPs)
|
||||
Please see [Generating TOTPs with Keepass2Android](Generating-TOTPs.md)
|
||||
|
||||
# FAQ
|
||||
|
||||
## Should I use the KP2A keyboard for entering passwords?
|
||||
@@ -99,7 +102,7 @@ It's time for action! As soon as possible, select Settings - Database - Export a
|
||||
Please see [Keepass2Android Apk](Keepass2Android-Apk.md) for more information.
|
||||
|
||||
## I get a message "File is trashed" when reading or writing a file on Google Drive
|
||||
This happens because ocaml-fuse (I guess you are on Linux and use that) moves files to trash and then creates a new one instead of correctly updating the file on Google Drive (each file has a unique ID which Keepass2Android uses). Fortunately, this was fixed: https://github.com/astrada/google-drive-ocamlfuse/issues/494After activating this option, please select "Change database" in KP2A, tap ,"Open file" and browse to the file on Google Drive again. After that, the message should no longer pop up.
|
||||
This happens because ocaml-fuse (I guess you are on Linux and use that) moves files to trash and then creates a new one instead of correctly updating the file on Google Drive (each file has a unique ID which Keepass2Android uses). Fortunately, this was fixed: https://github.com/astrada/google-drive-ocamlfuse/issues/494. After activating this option, please select "Change database" in KP2A, tap ,"Open file" and browse to the file on Google Drive again. After that, the message should no longer pop up.
|
||||
|
||||
## I get a message "The name must not be empty: null" when opening from Google Drive
|
||||
Please follow these steps:
|
||||
@@ -118,3 +121,5 @@ One of these has helped all users so far, but unfortunately it's not totally cle
|
||||
# For developers
|
||||
If you are interested in adding new features, you have two options:
|
||||
Either your features can be implemented as a plug-in. Please see [How to create a plug-in?](How-to-create-a-plug-in_.md) for more information. Or you add the features directly in the source code of the projects and create a pull request.
|
||||
|
||||
If you want to build Keepass2Android, check the [build guide](Build.readme.md).
|
||||
|
||||
53
docs/Generating-TOTPs.md
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
## TOTP in brief
|
||||
TOTP stands for [Time-based One-Time Password algorithm](https://en.wikipedia.org/wiki/Time-based_One-time_Password_algorithm) which is one of the most common way proposed by websites to do a [two-factor authentication (2FA)](https://en.wikipedia.org/wiki/Multi-factor_authentication).
|
||||
|
||||
On these websites, this option will often be mentioned in the 2FA configuration menu as things like "_use code generated by an application_", "_use [Google] Authenticator app_".
|
||||
|
||||
You're prompted to scan a QR code with the app, which essentially contains a code called "_seed_", usually with a form like "_AZER TYUI OPQS DFGH JKLM_", used to generate TOTPs. The seed can be also directly copied if there is no scanning option on the app.
|
||||
|
||||
Most common apps:
|
||||
|
||||
- Google Authenticator
|
||||
- Authy
|
||||
- Microsoft Authenticator
|
||||
- FreeOTP
|
||||
- LastPass Authenticator
|
||||
|
||||
## TOTP in KeePass and benefits
|
||||
In KeePass (by Dominik Reichl) there is are several ways to enable this Authenticator app ability:
|
||||
|
||||
- built-in TOTP support: https://keepass.info/help/base/placeholders.html#otp
|
||||
- [KeePassOTP plugin](https://keepass.info/plugins.html#kpotp)
|
||||
- [KeeOtp plugin](https://keepass.info/plugins.html#keeotp)
|
||||
- [KeeTrayTOTP plugin](https://keepass.info/plugins.html#keetraytotp) (note the name "_TrayTOTP_" on this one for later)
|
||||
|
||||
KeePassXC also supports TOTP: https://keepassxc.org/docs/KeePassXC_UserGuide#_adding_totp_to_an_entry
|
||||
|
||||
The greatest benefits are:
|
||||
|
||||
- the seed stays available contrary to the above apps (for which it's more or less hard to backup/restore/switch with another app)
|
||||
- TOTPs are available wherever the KeePass database is available. But conceptually it's not really 2FA anymore (all things are stored in the same place).
|
||||
|
||||
The different implementations use different ways of storing the TOTP seed (or secret, or key) and optional settings (e.g. the length of the TOTP to generate) within an entry inside the kdbx database. Keepass2Android attempts to be able to read the different formats, but can only write one:
|
||||
|
||||
## TOTP in Keepass2Android
|
||||
|
||||
If you use any of the tools mentioned above, you can set up TOTP entries with them. Keepass2Android can read those entries and generate TOTPs if any of the following styles are used:
|
||||
|
||||
* Keepass2 style: used when there are TimeOtp-Secret(-XXX) fields in the entry
|
||||
* KeeOtpPlugin style: used when there is an otp field containing a query string in the form of key=abc&step=X&size=Y (step and size are optional)
|
||||
* KeeWebOtp/Key Uri Format style: used when entry contains a URL starting with otpauth://totp/, e.g. otpauth://totp/?secret=abc (https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
|
||||
* KeeTrayTotp style:
|
||||
* requires a non-empty seed field (default key is "TOTP seed", can be changed in KP2A settings), value is base32 encoded data
|
||||
* requires a non-empty settings field (default key is "TOTP Settings", can be changed as well), value is expected to be a csv-separated array with [Duration];Length(;TimeCorrectionURL). Length is either an integer value or "S" to indicate Steam encoding
|
||||
|
||||
In order to view the generated TOTP code in KP2A, open the corresponding entry. You can then
|
||||
* use a dynamically generated field called "_TOTP_" containing the TOTP or
|
||||
* use the "Copy TOTP" button on the system notification for the selected entry or
|
||||
* switch to the KP2A keyboard and use the TOTP button to insert the TOTP value into the target app or browser
|
||||
|
||||
If you want to configure an entry to contain the TOTP fields, it is suggested to enter edit mode for the entry. Then click the "Configure TOTP" button. You can either enter the data manually or scan a QR code with the information.
|
||||
|
||||
### Spaces in otp field
|
||||
Make sure that the URI doesn't contain spaces, otherwise KeePass2Android will fail to generate TOTPs as a space is an invalid character. If your URIs have spaces, check [this comment](https://github.com/PhilippC/keepass2android/issues/1248#issuecomment-628035961)._
|
||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 87 KiB |
@@ -3,13 +3,13 @@
|
||||
Creating a plug-in for Keepass2Android or enabling your app to query credentials from Keepass2Android is pretty simple. Please follow the steps below to get started. In case you have any questions, please contact me.
|
||||
|
||||
## Preparations
|
||||
First check out the source code and import the Keepass2AndroidPluginSDK from [https://keepass2android.codeplex.com/SourceControl/latest#src/java/Keepass2AndroidPluginSDK/](https://keepass2android.codeplex.com/SourceControl/latest#src/java/Keepass2AndroidPluginSDK/) into your workspace. You should be able to build this library project.
|
||||
First check out the source code and import the Keepass2AndroidPluginSDK from [https://github.com/PhilippC/keepass2android/tree/master/src/java/Keepass2AndroidPluginSDK2](https://github.com/PhilippC/keepass2android/tree/master/src/java/Keepass2AndroidPluginSDK2/) into your workspace. You should be able to build this library project.
|
||||
|
||||
Now add a reference to the PluginSDK library from your existing app or add a new plug-in app and then add the reference.
|
||||
|
||||
## Authorization
|
||||
|
||||
Keepass2Android stores very sensitive user data and therefore implements a plug-in authorization scheme based on broadcasts sent between the plug-in and the host app (=Keepass2Android or Keepass2Android Offline). Before your app/plug-in gets any information from KP2A, the user will have to grant your app/plug-in access to KP2A. As not every app/plug-in requires access to all information, you must specify which scopes are required by your app. The implemented scopes can be found in [https://keepass2android.codeplex.com/SourceControl/latest#src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/Strings.java](https://keepass2android.codeplex.com/SourceControl/latest#src/java/Keepass2AndroidPluginSDK/src/keepass2android/pluginsdk/Strings.java).
|
||||
Keepass2Android stores very sensitive user data and therefore implements a plug-in authorization scheme based on broadcasts sent between the plug-in and the host app (=Keepass2Android or Keepass2Android Offline). Before your app/plug-in gets any information from KP2A, the user will have to grant your app/plug-in access to KP2A. As not every app/plug-in requires access to all information, you must specify which scopes are required by your app. The implemented scopes can be found in [https://github.com/PhilippC/keepass2android/tree/master/src/java/Keepass2AndroidPluginSDK2/src/keepass2android/pluginsdk/Strings.java](https://github.com/PhilippC/keepass2android/tree/master/src/java/Keepass2AndroidPluginSDK2/src/keepass2android/pluginsdk/Strings.java).
|
||||
|
||||
To tell Kp2a that you're a plug-in, you need to add a simple BroadcastReceiver like this:
|
||||
|
||||
@@ -55,8 +55,8 @@ These strings will be displayed to the user when KP2A asks if access should be g
|
||||
|
||||
## Modifying the entry view
|
||||
You can add menu options for the full entry or for individual fields of the entry when displayed to the user. This is done, for example, by the QR plugin ([https://play.google.com/store/apps/details?id=keepass2android.plugin.qr](https://play.google.com/store/apps/details?id=keepass2android.plugin.qr)).
|
||||
In addition, it is even possible to add new fields or modify existing fields. Please see the sample plugin "PluginA" in the KP2A repository for a simple example on how to do this:
|
||||
[https://keepass2android.codeplex.com/SourceControl/latest#src/java/PluginA/src/keepass2android/plugina/PluginAActionReceiver.java](https://keepass2android.codeplex.com/SourceControl/latest#src/java/PluginA/src/keepass2android/plugina/PluginAActionReceiver.java)
|
||||
In addition, it is even possible to add new fields or modify existing fields. Please see the sample plugin "PluginA" for a simple example on how to do this:
|
||||
[https://github.com/PhilippC/keepass2android-sampleplugin/blob/main/src/keepass2android/plugina/PluginAAccessReceiver.java](https://github.com/PhilippC/keepass2android-sampleplugin/blob/main/src/keepass2android/plugina/PluginAAccessReceiver.java)
|
||||
|
||||
## Querying credentials
|
||||
KP2A 0.9.4 adds a great opportunity for third party apps: Instead of prompting the user to enter credentials or a passphrase, the app should try to get the data from KP2A if it is installed: If the user grants (or previously granted) access for the app, KP2A will automatically retrieve the matching entry. User action is only required if the KP2A database is locked (user will usually unlock it with the short QuickUnlock code) or if no matching entry is found (user can then create a new entry or select an existing one. in the latter case KP2A will offer to add entry information so that the entry will be found automatically next time).
|
||||
|
||||
@@ -18,6 +18,8 @@ Keepass2Android does not collect personal identifiable information. For debuggin
|
||||
* **Internet** (Keepass2Android regular only): Required to allow the user to read/store password databases or key files on remote locations, e.g. Dropbox or via WebDav.
|
||||
* **Contacts/Accounts** (Keepass2Android regular only): Required by the Google Drive SDK. If you want to access files on Google Drive, you are prompted to select one of the Google Accounts on your phone to use. The permission is required to query the list of Google accounts on the device. Keepass2Android does not access your personal contacts.
|
||||
* **Storage**: Required to allow the user to read/store password databases or key files on the device locally.
|
||||
* **Fingerprint**: Required if you want to use fingerprint unlock.
|
||||
* **Fingerprint/Biometric**: Required if you want to use biometric unlock.
|
||||
* **Vibrate**: Required by the built-in keyboard (vibrate on key press)
|
||||
* **Camera**: Required for scanning OTP QR Codes
|
||||
* **Foreground service**: Required to keep the app alive for QuickUnlock (so you don't need to enter your full master password repeatedly)
|
||||
|
||||
|
||||
@@ -19,4 +19,9 @@ 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).
|
||||
|
||||
[](https://www.bitrise.io/app/43a23ab54dee9f7e)
|
||||
# How do I build the project?
|
||||
If you want to build Keepass2Android, check the [build guide](Build.readme.md).
|
||||
|
||||
The project homepage is https://philipp.crocoll.net/keepass2android/index.php
|
||||
|
||||
<img src="https://github.com/PhilippC/keepass2android/actions/workflows/build.yml/badge.svg" alt="build status" /> [Build status](https://github.com/PhilippC/keepass2android/actions)
|
||||
|
||||
72
docs/SFTP-Credentials.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# SFTP Open/Create Database Credentials Documentation
|
||||
|
||||
## Basic Settings
|
||||
* **Host** -- the hostname or IP address of the SFTP server to connect to
|
||||
* **Port** -- the listening TCP port of the SFTP server to connect to (default: 22)
|
||||
* **Username** -- the user/account name on the SFTP server that has access to the database
|
||||
* **Initial directory** -- The path on the SFTP server that will be used as a starting point when choosing the remote database file
|
||||
|
||||
### Authentication Modes
|
||||
|
||||
#### Password
|
||||
Authenticate using a password
|
||||
|
||||
* **Password** -- the password associated with **username** used to log into the SFTP server
|
||||
|
||||
#### K2A Private/Public Key
|
||||
Authenticate using a private/public key pair that is generated internally by KP2A
|
||||
|
||||
* **SEND PUBLIC KEY...** -- Opens a standard Android "Share" screen containing the KP2A public key content. This allows for the public key to be sent via email, SMS, etc. This public key will need to be added to the SFTP server's user's "authorized keys" to allow private/public key authentication.
|
||||
|
||||
#### Custom Private Key
|
||||
Authenticate using an existing private/public key pair. Use this option instead of *K2A Private/Public Key* if you wish to use a key pair that is already set up for this **username** on the SFTP server.
|
||||
|
||||
* **Selected private key** -- a combo-box containing a list of custom private keys that KP2A knows about, and a special `[Add new...]` option.
|
||||
##### Add A New Private Key
|
||||
* Select `[Add new...]`
|
||||
* Enter a name for the new key in **New key name**
|
||||
* Enter the private key contents (text) into **New key content**. **TIP:** The easiest way to accomplish this is to open the private key file in a text editor on the device, **Select All**, **Copy** to the clipboard, and paste it into **New key content**.
|
||||
* Tap **SAVE PRIVATE KEY** to add the new key to the known list.
|
||||
|
||||
##### Use An Existing Private Key
|
||||
* To use a private key that has already been imported into KP2A, simply select it from the list of keys.
|
||||
|
||||
##### Remove An Existing Key
|
||||
* To remove a private that has been imported into KP2A, select it from the list and tap **DELETE PRIVATE KEY**.
|
||||
|
||||
A **key passphrase** can be supplied (if the key pair requires it)
|
||||
|
||||
## Advanced Settings
|
||||
* **Connection timeout seconds** -- the number of seconds to wait for a connection to the server before giving up and considering the server as unavailable/unreachable
|
||||
|
||||
### Key Algorithm Manipulation
|
||||
**NOTE: It is very rare that these fields need to be (or should be) specified. Use at your own risk!**
|
||||
|
||||
* **Key Exchange (KEX) Algorithm(s)** -- Explicitly set or modify the ordered list of Key Exchange algorithms that the SSH/SFTP client library will try to use
|
||||
* **Server Host Key Algorithm(s)** -- Explicitly set or modify the ordered list of Server Host Key algorithms that the SSH/SFTP client library will try to use
|
||||
|
||||
#### How It Works
|
||||
The SSH/SFTP client has a pre-defined ordered list of algorithm names that it will use to negotiate with the server to handle key exchange. In rare cases there are compatibility issues where Android OS has not properly implemented full support for algorithms listed. This can result in a connection failure, even if there is a suitable algorithm available (of lesser priority in the list).
|
||||
|
||||
The fields listed above allow these lists to be manipulated in the following ways to overcome/workaround such problems. The value is a comma-separated list of "algorithm spec" entries. Specs can be one of:
|
||||
|
||||
* Direct replacement of values -- Ex: `primary_alg,secondary_alg`
|
||||
* Prepend to values -- Ex: `+try_first_alg`
|
||||
* Append to values -- Ex: `try_last_alg+`
|
||||
* Remove a specific value -- Ex: `-bad_alg`
|
||||
* Remove values matching prefix -- Ex: `-bad_starting_with*`
|
||||
* Remove values matching suffix -- Ex: `-*bad_ending_with`
|
||||
* Remove values matching substring -- Ex: `-*bad_middle*`
|
||||
* Remove values matching prefix and suffix -- Ex: `-alg_begin*end`
|
||||
|
||||
For example, assume the system's KEX algorithm list is:
|
||||
`ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256`
|
||||
|
||||
These are various outcomes (user KEX field -> result):
|
||||
|
||||
* Prefix removal: `-ec*` --> `diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256`
|
||||
* Suffix removal, appending: `-*256,+first_alg,almost_last_alg+,last_alg+` --> `first_alg,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,almost_last_alg,last_alg`
|
||||
* Direct replacement: `first_alg,middle_alg,last_alg` --> `first_alg,middle_alg,last_alg`
|
||||
|
||||
## Selecting A Database
|
||||
Once all applicable fields have been entered and/or options selected, tapping **OK** will attempt to connect to the SFTP server. First time connections may pop up a dialog window asking to accept the host's authenticity (tap **yes** if the host is trusted), as well as potentially creating a new `known_hosts` file (tap **yes** to do so). If the connection is successful, a remote file browser screen will open. Navigate and select the Keepass database to open.
|
||||
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Xamarin.GooglePlayServices" version="27.0.0.0" />
|
||||
</packages>
|
||||
@@ -13,6 +13,7 @@
|
||||
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
|
||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
||||
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
|
||||
<AndroidClassParser>class-parse</AndroidClassParser>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
||||
BIN
src/JavaFileStorageBindings/Jars/dropbox-core-sdk-5.4.6.jar
Normal file
BIN
src/JavaFileStorageBindings/Jars/gdrive/grpc-context-1.22.1.jar
Normal file
BIN
src/JavaFileStorageBindings/Jars/gdrive/jsr305-3.0.2.jar
Normal file
BIN
src/JavaFileStorageBindings/Jars/gson-2.8.6.jar
Normal file
BIN
src/JavaFileStorageBindings/Jars/jackson-core-2.13.5.jar
Normal file
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.microsoft.services.msa"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
android:targetSdkVersion="22" />
|
||||
|
||||
<uses-permission android:name="com.sony.mobile.permission.SYSTEM_UI_VISIBILITY_EXTENSION" />
|
||||
|
||||
</manifest>
|
||||
@@ -1 +0,0 @@
|
||||
int string app_name 0x7f020000
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.microsoft.services.msa"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
android:targetSdkVersion="22" />
|
||||
|
||||
<uses-permission android:name="com.sony.mobile.permission.SYSTEM_UI_VISIBILITY_EXTENSION" />
|
||||
|
||||
</manifest>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- From: file:/C:/Users/pnied/Documents/git/msa-auth-for-android/src/main/res/values/strings.xml -->
|
||||
<eat-comment/>
|
||||
<string name="app_name">msa-auth</string>
|
||||
</resources>
|
||||
BIN
src/JavaFileStorageBindings/Jars/okhttp-4.12.0.jar
Normal file
BIN
src/JavaFileStorageBindings/Jars/okhttp-digest-3.1.0.jar
Normal file
BIN
src/JavaFileStorageBindings/Jars/okio-3.6.0.jar
Normal file
BIN
src/JavaFileStorageBindings/Jars/okio-jvm-3.6.0.jar
Normal file
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.microsoft.onedrivesdk"
|
||||
android:versionCode="10202"
|
||||
android:versionName="1.2.2" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="15"
|
||||
android:targetSdkVersion="23" />
|
||||
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
||||
|
||||
</manifest>
|
||||
@@ -1,22 +0,0 @@
|
||||
int dimen activity_horizontal_margin 0x7f030000
|
||||
int dimen activity_vertical_margin 0x7f030001
|
||||
int id LinearLayout1 0x7f060004
|
||||
int id com_microsoft_aad_adal_editDummyText 0x7f060002
|
||||
int id com_microsoft_aad_adal_progressBar 0x7f060003
|
||||
int id com_microsoft_aad_adal_webView1 0x7f060001
|
||||
int id editPassword 0x7f060006
|
||||
int id editUserName 0x7f060005
|
||||
int id webView1 0x7f060000
|
||||
int layout activity_authentication 0x7f020000
|
||||
int layout dialog_authentication 0x7f020001
|
||||
int layout http_auth_dialog 0x7f020002
|
||||
int string app_loading 0x7f050000
|
||||
int string app_name 0x7f050001
|
||||
int string broker_processing 0x7f050002
|
||||
int string http_auth_dialog_cancel 0x7f050003
|
||||
int string http_auth_dialog_login 0x7f050004
|
||||
int string http_auth_dialog_password 0x7f050005
|
||||
int string http_auth_dialog_title 0x7f050006
|
||||
int string http_auth_dialog_username 0x7f050007
|
||||
int style AppBaseTheme 0x7f040000
|
||||
int style AppTheme 0x7f040001
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.microsoft.onedrivesdk"
|
||||
android:versionCode="10202"
|
||||
android:versionName="1.2.2" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="15"
|
||||
android:targetSdkVersion="23" />
|
||||
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
||||
|
||||
</manifest>
|
||||
@@ -1,5 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.props" Condition="Exists('..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.props')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.props" Condition="Exists('..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@@ -11,9 +13,11 @@
|
||||
<AssemblyName>JavaFileStorageBindings</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
||||
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v13.0</TargetFrameworkVersion>
|
||||
<AndroidClassParser>class-parse</AndroidClassParser>
|
||||
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -46,11 +50,130 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Java.Interop" />
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="GooglePlayServicesLib">
|
||||
<HintPath>..\Components\googleplayservices-19.0.0\lib\android\GooglePlayServicesLib.dll</HintPath>
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Xamarin.AndroidX.Activity, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Activity.1.6.0\lib\monoandroid12.0\Xamarin.AndroidX.Activity.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Annotation, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Annotation.1.5.0\lib\monoandroid12.0\Xamarin.AndroidX.Annotation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Annotation.Experimental, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Annotation.Experimental.1.3.0\lib\monoandroid12.0\Xamarin.AndroidX.Annotation.Experimental.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Arch.Core.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Arch.Core.Common.2.1.0.15\lib\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Arch.Core.Runtime, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Arch.Core.Runtime.2.1.0.15\lib\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Collection, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Collection.1.2.0.4\lib\monoandroid12.0\Xamarin.AndroidX.Collection.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Concurrent.Futures, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Concurrent.Futures.1.1.0.9\lib\monoandroid12.0\Xamarin.AndroidX.Concurrent.Futures.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Core, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Core.1.9.0\lib\monoandroid12.0\Xamarin.AndroidX.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Core.Core.Ktx, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Core.Core.Ktx.1.9.0\lib\monoandroid12.0\Xamarin.AndroidX.Core.Core.Ktx.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.CustomView, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.CustomView.1.1.0.13\lib\monoandroid12.0\Xamarin.AndroidX.CustomView.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Fragment, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Fragment.1.5.3\lib\monoandroid12.0\Xamarin.AndroidX.Fragment.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Lifecycle.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Lifecycle.Common.2.5.1\lib\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Lifecycle.LiveData.Core, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Lifecycle.LiveData.Core.2.5.1\lib\monoandroid12.0\Xamarin.AndroidX.Lifecycle.LiveData.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Lifecycle.Runtime, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Lifecycle.Runtime.2.5.1\lib\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Lifecycle.ViewModel, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Lifecycle.ViewModel.2.5.1\lib\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModel.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Lifecycle.ViewModelSavedState, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.2.5.1\lib\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Loader, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Loader.1.1.0.14\lib\monoandroid12.0\Xamarin.AndroidX.Loader.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.MultiDex, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.MultiDex.2.0.1.13\lib\monoandroid12.0\Xamarin.AndroidX.MultiDex.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.SavedState, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.SavedState.1.2.0\lib\monoandroid12.0\Xamarin.AndroidX.SavedState.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.Tracing.Tracing, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.Tracing.Tracing.1.1.0.1\lib\monoandroid12.0\Xamarin.AndroidX.Tracing.Tracing.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.VersionedParcelable, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.VersionedParcelable.1.1.1.14\lib\monoandroid12.0\Xamarin.AndroidX.VersionedParcelable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.AndroidX.ViewPager, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.AndroidX.ViewPager.1.0.0.14\lib\monoandroid12.0\Xamarin.AndroidX.ViewPager.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Google.Guava, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Google.Guava.28.2.0.1\lib\monoandroid90\Xamarin.Google.Guava.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Google.Guava.FailureAccess, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Google.Guava.FailureAccess.1.0.1.3\lib\monoandroid90\Xamarin.Google.Guava.FailureAccess.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Google.Guava.ListenableFuture, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Google.Guava.ListenableFuture.1.0.0.9\lib\monoandroid12.0\Xamarin.Google.Guava.ListenableFuture.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.GooglePlayServices.Auth, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.GooglePlayServices.Auth.120.4.0\lib\monoandroid12.0\Xamarin.GooglePlayServices.Auth.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.GooglePlayServices.Auth.Api.Phone, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.GooglePlayServices.Auth.Api.Phone.118.0.1.2\lib\monoandroid12.0\Xamarin.GooglePlayServices.Auth.Api.Phone.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.GooglePlayServices.Auth.Base, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.GooglePlayServices.Auth.Base.118.0.6\lib\monoandroid12.0\Xamarin.GooglePlayServices.Auth.Base.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.GooglePlayServices.Base, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.GooglePlayServices.Base.118.1.0\lib\monoandroid12.0\Xamarin.GooglePlayServices.Base.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.GooglePlayServices.Basement, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.GooglePlayServices.Basement.118.1.0.1\lib\monoandroid12.0\Xamarin.GooglePlayServices.Basement.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.GooglePlayServices.Fido, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.GooglePlayServices.Fido.119.0.0\lib\monoandroid12.0\Xamarin.GooglePlayServices.Fido.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.GooglePlayServices.Tasks, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.GooglePlayServices.Tasks.118.0.2\lib\monoandroid12.0\Xamarin.GooglePlayServices.Tasks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Jetbrains.Annotations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Jetbrains.Annotations.23.0.0.4\lib\monoandroid12.0\Xamarin.Jetbrains.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Kotlin.StdLib, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Kotlin.StdLib.1.7.10\lib\monoandroid12.0\Xamarin.Kotlin.StdLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Kotlin.StdLib.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Kotlin.StdLib.Common.1.7.10\lib\monoandroid12.0\Xamarin.Kotlin.StdLib.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Kotlin.StdLib.Jdk7, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Kotlin.StdLib.Jdk7.1.7.10\lib\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk7.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Kotlin.StdLib.Jdk8, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Kotlin.StdLib.Jdk8.1.7.10\lib\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk8.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.KotlinX.Coroutines.Android, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.KotlinX.Coroutines.Android.1.6.4\lib\monoandroid12.0\Xamarin.KotlinX.Coroutines.Android.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.KotlinX.Coroutines.Core.Jvm, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.KotlinX.Coroutines.Core.Jvm.1.6.4\lib\monoandroid12.0\Xamarin.KotlinX.Coroutines.Core.Jvm.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -60,8 +183,10 @@
|
||||
<LibraryProjectZip Include="..\java\JavaFileStorage\app\build\outputs\aar\JavaFileStorage-debug.aar">
|
||||
<Link>Jars\JavaFileStorage-debug.aar</Link>
|
||||
</LibraryProjectZip>
|
||||
<None Include="app.config" />
|
||||
<None Include="Jars\AboutJars.txt" />
|
||||
<None Include="Additions\AboutAdditions.txt" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TransformFile Include="Transforms\Metadata.xml" />
|
||||
@@ -83,41 +208,20 @@
|
||||
</XamarinComponentReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\msa-auth-0.8.6\classes-msa-auth.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\onedrive-sdk-android-1.2.2\classes-onedrive-sdk.jar" />
|
||||
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj">
|
||||
<Project>{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}</Project>
|
||||
<Name>PCloudBindings</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\commons-logging-1.1.1.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-client-1.16.0-rc.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-client-android-1.16.0-rc.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-services-drive-v2-rev102-1.16.0-rc.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-1.16.0-rc.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-android-1.16.0-rc.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-jackson-1.16.0-rc.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-jackson2-1.16.0-rc.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-oauth-client-1.16.0-rc.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\httpclient-4.0.3.jar" />
|
||||
</ItemGroup>
|
||||
@@ -130,28 +234,141 @@
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\json_simple-1.1.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\jsr305-1.3.9.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-gson-1.16.0-rc.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\jackson-core-2.7.4.jar" />
|
||||
<EmbeddedReferenceJar Include="Jars\gson-2.8.6.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedJar Include="Jars\dropbox-core-sdk-3.1.1.jar" />
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-android-1.32.1.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gson-2.8.1.jar" />
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\jsr305-3.0.2.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\okhttp-digest-2.5.jar" />
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-1.32.1.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\okio-2.9.0.jar" />
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-http-client-jackson2-1.32.1.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\okhttp-4.10.0-RC1.jar" />
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-client-1.30.5.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-api-client-android-1.30.5.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\google-oauth-client-1.30.4.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\opencensus-contrib-http-util-0.24.0.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\grpc-context-1.22.1.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\opencensus-api-0.24.0.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\okhttp-4.12.0.jar" />
|
||||
<EmbeddedReferenceJar Include="Jars\okio-3.6.0.jar" />
|
||||
<EmbeddedReferenceJar Include="Jars\okio-jvm-3.6.0.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\okhttp-digest-3.1.0.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\dropbox-core-sdk-5.4.6.jar" />
|
||||
<EmbeddedReferenceJar Include="Jars\jackson-core-2.13.5.jar" />
|
||||
</ItemGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.MultiDex.2.0.1.13\build\monoandroid12.0\Xamarin.AndroidX.MultiDex.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.MultiDex.2.0.1.13\build\monoandroid12.0\Xamarin.AndroidX.MultiDex.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Google.Guava.FailureAccess.1.0.1.3\build\monoandroid90\Xamarin.Google.Guava.FailureAccess.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Google.Guava.FailureAccess.1.0.1.3\build\monoandroid90\Xamarin.Google.Guava.FailureAccess.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Google.Guava.ListenableFuture.1.0.0.9\build\monoandroid12.0\Xamarin.Google.Guava.ListenableFuture.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Google.Guava.ListenableFuture.1.0.0.9\build\monoandroid12.0\Xamarin.Google.Guava.ListenableFuture.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Google.Guava.28.2.0.1\build\monoandroid90\Xamarin.Google.Guava.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Google.Guava.28.2.0.1\build\monoandroid90\Xamarin.Google.Guava.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Jetbrains.Annotations.23.0.0.4\build\monoandroid12.0\Xamarin.Jetbrains.Annotations.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Jetbrains.Annotations.23.0.0.4\build\monoandroid12.0\Xamarin.Jetbrains.Annotations.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Kotlin.StdLib.Common.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Kotlin.StdLib.Common.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Common.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Kotlin.StdLib.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Kotlin.StdLib.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Annotation.1.5.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Annotation.1.5.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Annotation.Experimental.1.3.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.Experimental.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Annotation.Experimental.1.3.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.Experimental.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Arch.Core.Common.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Arch.Core.Common.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Common.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Arch.Core.Runtime.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Arch.Core.Runtime.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Runtime.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Collection.1.2.0.4\build\monoandroid12.0\Xamarin.AndroidX.Collection.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Collection.1.2.0.4\build\monoandroid12.0\Xamarin.AndroidX.Collection.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Concurrent.Futures.1.1.0.9\build\monoandroid12.0\Xamarin.AndroidX.Concurrent.Futures.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Concurrent.Futures.1.1.0.9\build\monoandroid12.0\Xamarin.AndroidX.Concurrent.Futures.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Lifecycle.Common.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Lifecycle.Common.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Common.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Lifecycle.LiveData.Core.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.LiveData.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Lifecycle.LiveData.Core.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.LiveData.Core.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Lifecycle.Runtime.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Lifecycle.Runtime.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Runtime.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Lifecycle.ViewModel.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModel.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Lifecycle.ViewModel.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModel.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.SavedState.1.2.0\build\monoandroid12.0\Xamarin.AndroidX.SavedState.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.SavedState.1.2.0\build\monoandroid12.0\Xamarin.AndroidX.SavedState.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Tracing.Tracing.1.1.0.1\build\monoandroid12.0\Xamarin.AndroidX.Tracing.Tracing.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Tracing.Tracing.1.1.0.1\build\monoandroid12.0\Xamarin.AndroidX.Tracing.Tracing.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.VersionedParcelable.1.1.1.14\build\monoandroid12.0\Xamarin.AndroidX.VersionedParcelable.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.VersionedParcelable.1.1.1.14\build\monoandroid12.0\Xamarin.AndroidX.VersionedParcelable.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Core.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Core.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Core.Core.Ktx.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.Core.Ktx.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Core.Core.Ktx.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.Core.Ktx.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.CustomView.1.1.0.13\build\monoandroid12.0\Xamarin.AndroidX.CustomView.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.CustomView.1.1.0.13\build\monoandroid12.0\Xamarin.AndroidX.CustomView.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Loader.1.1.0.14\build\monoandroid12.0\Xamarin.AndroidX.Loader.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Loader.1.1.0.14\build\monoandroid12.0\Xamarin.AndroidX.Loader.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.ViewPager.1.0.0.14\build\monoandroid12.0\Xamarin.AndroidX.ViewPager.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.ViewPager.1.0.0.14\build\monoandroid12.0\Xamarin.AndroidX.ViewPager.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Kotlin.StdLib.Jdk7.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk7.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Kotlin.StdLib.Jdk7.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk7.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Kotlin.StdLib.Jdk8.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk8.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Kotlin.StdLib.Jdk8.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk8.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.KotlinX.Coroutines.Core.Jvm.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Core.Jvm.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.KotlinX.Coroutines.Core.Jvm.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Core.Jvm.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Activity.1.6.0\build\monoandroid12.0\Xamarin.AndroidX.Activity.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Activity.1.6.0\build\monoandroid12.0\Xamarin.AndroidX.Activity.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.AndroidX.Fragment.1.5.3\build\monoandroid12.0\Xamarin.AndroidX.Fragment.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.AndroidX.Fragment.1.5.3\build\monoandroid12.0\Xamarin.AndroidX.Fragment.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Basement.118.1.0.1\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Basement.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Basement.118.1.0.1\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Basement.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Tasks.118.0.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Tasks.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Tasks.118.0.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Tasks.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Base.118.1.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Base.118.1.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Base.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Auth.Api.Phone.118.0.1.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Api.Phone.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Auth.Api.Phone.118.0.1.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Api.Phone.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Auth.Base.118.0.6\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Auth.Base.118.0.6\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Base.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Fido.119.0.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Fido.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Fido.119.0.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Fido.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.GooglePlayServices.Auth.120.4.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.GooglePlayServices.Auth.120.4.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.KotlinX.Coroutines.Android.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Android.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.KotlinX.Coroutines.Android.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Android.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Migration.1.0.10\build\monoandroid120\Xamarin.AndroidX.Migration.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.targets" Condition="Exists('..\packages\Xamarin.Build.Download.0.11.4\build\Xamarin.Build.Download.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.MultiDex.2.0.1.13\build\monoandroid12.0\Xamarin.AndroidX.MultiDex.targets" Condition="Exists('..\packages\Xamarin.AndroidX.MultiDex.2.0.1.13\build\monoandroid12.0\Xamarin.AndroidX.MultiDex.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Google.Guava.FailureAccess.1.0.1.3\build\monoandroid90\Xamarin.Google.Guava.FailureAccess.targets" Condition="Exists('..\packages\Xamarin.Google.Guava.FailureAccess.1.0.1.3\build\monoandroid90\Xamarin.Google.Guava.FailureAccess.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Google.Guava.ListenableFuture.1.0.0.9\build\monoandroid12.0\Xamarin.Google.Guava.ListenableFuture.targets" Condition="Exists('..\packages\Xamarin.Google.Guava.ListenableFuture.1.0.0.9\build\monoandroid12.0\Xamarin.Google.Guava.ListenableFuture.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Google.Guava.28.2.0.1\build\monoandroid90\Xamarin.Google.Guava.targets" Condition="Exists('..\packages\Xamarin.Google.Guava.28.2.0.1\build\monoandroid90\Xamarin.Google.Guava.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Jetbrains.Annotations.23.0.0.4\build\monoandroid12.0\Xamarin.Jetbrains.Annotations.targets" Condition="Exists('..\packages\Xamarin.Jetbrains.Annotations.23.0.0.4\build\monoandroid12.0\Xamarin.Jetbrains.Annotations.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Kotlin.StdLib.Common.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Common.targets" Condition="Exists('..\packages\Xamarin.Kotlin.StdLib.Common.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Common.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Kotlin.StdLib.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.targets" Condition="Exists('..\packages\Xamarin.Kotlin.StdLib.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Annotation.1.5.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Annotation.1.5.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Annotation.Experimental.1.3.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.Experimental.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Annotation.Experimental.1.3.0\build\monoandroid12.0\Xamarin.AndroidX.Annotation.Experimental.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Arch.Core.Common.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Common.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Arch.Core.Common.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Common.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Arch.Core.Runtime.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Runtime.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Arch.Core.Runtime.2.1.0.15\build\monoandroid12.0\Xamarin.AndroidX.Arch.Core.Runtime.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Collection.1.2.0.4\build\monoandroid12.0\Xamarin.AndroidX.Collection.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Collection.1.2.0.4\build\monoandroid12.0\Xamarin.AndroidX.Collection.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Concurrent.Futures.1.1.0.9\build\monoandroid12.0\Xamarin.AndroidX.Concurrent.Futures.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Concurrent.Futures.1.1.0.9\build\monoandroid12.0\Xamarin.AndroidX.Concurrent.Futures.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Lifecycle.Common.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Common.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Lifecycle.Common.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Common.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Lifecycle.LiveData.Core.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.LiveData.Core.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Lifecycle.LiveData.Core.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.LiveData.Core.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Lifecycle.Runtime.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Runtime.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Lifecycle.Runtime.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.Runtime.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Lifecycle.ViewModel.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModel.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Lifecycle.ViewModel.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModel.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.SavedState.1.2.0\build\monoandroid12.0\Xamarin.AndroidX.SavedState.targets" Condition="Exists('..\packages\Xamarin.AndroidX.SavedState.1.2.0\build\monoandroid12.0\Xamarin.AndroidX.SavedState.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Tracing.Tracing.1.1.0.1\build\monoandroid12.0\Xamarin.AndroidX.Tracing.Tracing.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Tracing.Tracing.1.1.0.1\build\monoandroid12.0\Xamarin.AndroidX.Tracing.Tracing.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.VersionedParcelable.1.1.1.14\build\monoandroid12.0\Xamarin.AndroidX.VersionedParcelable.targets" Condition="Exists('..\packages\Xamarin.AndroidX.VersionedParcelable.1.1.1.14\build\monoandroid12.0\Xamarin.AndroidX.VersionedParcelable.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Core.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Core.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Core.Core.Ktx.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.Core.Ktx.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Core.Core.Ktx.1.9.0\build\monoandroid12.0\Xamarin.AndroidX.Core.Core.Ktx.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.CustomView.1.1.0.13\build\monoandroid12.0\Xamarin.AndroidX.CustomView.targets" Condition="Exists('..\packages\Xamarin.AndroidX.CustomView.1.1.0.13\build\monoandroid12.0\Xamarin.AndroidX.CustomView.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Loader.1.1.0.14\build\monoandroid12.0\Xamarin.AndroidX.Loader.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Loader.1.1.0.14\build\monoandroid12.0\Xamarin.AndroidX.Loader.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.ViewPager.1.0.0.14\build\monoandroid12.0\Xamarin.AndroidX.ViewPager.targets" Condition="Exists('..\packages\Xamarin.AndroidX.ViewPager.1.0.0.14\build\monoandroid12.0\Xamarin.AndroidX.ViewPager.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Kotlin.StdLib.Jdk7.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk7.targets" Condition="Exists('..\packages\Xamarin.Kotlin.StdLib.Jdk7.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk7.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Kotlin.StdLib.Jdk8.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk8.targets" Condition="Exists('..\packages\Xamarin.Kotlin.StdLib.Jdk8.1.7.10\build\monoandroid12.0\Xamarin.Kotlin.StdLib.Jdk8.targets')" />
|
||||
<Import Project="..\packages\Xamarin.KotlinX.Coroutines.Core.Jvm.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Core.Jvm.targets" Condition="Exists('..\packages\Xamarin.KotlinX.Coroutines.Core.Jvm.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Core.Jvm.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.2.5.1\build\monoandroid12.0\Xamarin.AndroidX.Lifecycle.ViewModelSavedState.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Activity.1.6.0\build\monoandroid12.0\Xamarin.AndroidX.Activity.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Activity.1.6.0\build\monoandroid12.0\Xamarin.AndroidX.Activity.targets')" />
|
||||
<Import Project="..\packages\Xamarin.AndroidX.Fragment.1.5.3\build\monoandroid12.0\Xamarin.AndroidX.Fragment.targets" Condition="Exists('..\packages\Xamarin.AndroidX.Fragment.1.5.3\build\monoandroid12.0\Xamarin.AndroidX.Fragment.targets')" />
|
||||
<Import Project="..\packages\Xamarin.GooglePlayServices.Basement.118.1.0.1\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Basement.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Basement.118.1.0.1\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Basement.targets')" />
|
||||
<Import Project="..\packages\Xamarin.GooglePlayServices.Tasks.118.0.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Tasks.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Tasks.118.0.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Tasks.targets')" />
|
||||
<Import Project="..\packages\Xamarin.GooglePlayServices.Base.118.1.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Base.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Base.118.1.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Base.targets')" />
|
||||
<Import Project="..\packages\Xamarin.GooglePlayServices.Auth.Api.Phone.118.0.1.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Api.Phone.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Auth.Api.Phone.118.0.1.2\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Api.Phone.targets')" />
|
||||
<Import Project="..\packages\Xamarin.GooglePlayServices.Auth.Base.118.0.6\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Base.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Auth.Base.118.0.6\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.Base.targets')" />
|
||||
<Import Project="..\packages\Xamarin.GooglePlayServices.Fido.119.0.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Fido.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Fido.119.0.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Fido.targets')" />
|
||||
<Import Project="..\packages\Xamarin.GooglePlayServices.Auth.120.4.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.targets" Condition="Exists('..\packages\Xamarin.GooglePlayServices.Auth.120.4.0\build\MonoAndroid12.0\Xamarin.GooglePlayServices.Auth.targets')" />
|
||||
<Import Project="..\packages\Xamarin.KotlinX.Coroutines.Android.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Android.targets" Condition="Exists('..\packages\Xamarin.KotlinX.Coroutines.Android.1.6.4\build\monoandroid12.0\Xamarin.KotlinX.Coroutines.Android.targets')" />
|
||||
</Project>
|
||||
15
src/JavaFileStorageBindings/app.config
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Google.Apis.Auth" publicKeyToken="4b01fa6e34db77ab" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.67.0.0" newVersion="1.67.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
44
src/JavaFileStorageBindings/packages.config
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Xamarin.AndroidX.Activity" version="1.6.0" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Annotation" version="1.5.0" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Annotation.Experimental" version="1.3.0" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Arch.Core.Common" version="2.1.0.15" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Arch.Core.Runtime" version="2.1.0.15" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Collection" version="1.2.0.4" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Concurrent.Futures" version="1.1.0.9" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Core" version="1.9.0" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Core.Core.Ktx" version="1.9.0" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.CustomView" version="1.1.0.13" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Fragment" version="1.5.3" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Lifecycle.Common" version="2.5.1" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Lifecycle.LiveData.Core" version="2.5.1" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Lifecycle.Runtime" version="2.5.1" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Lifecycle.ViewModel" version="2.5.1" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Lifecycle.ViewModelSavedState" version="2.5.1" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Loader" version="1.1.0.14" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Migration" version="1.0.10" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.MultiDex" version="2.0.1.13" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.SavedState" version="1.2.0" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.Tracing.Tracing" version="1.1.0.1" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.VersionedParcelable" version="1.1.1.14" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.AndroidX.ViewPager" version="1.0.0.14" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.Build.Download" version="0.11.4" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.Google.Guava" version="28.2.0.1" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.Google.Guava.FailureAccess" version="1.0.1.3" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.Google.Guava.ListenableFuture" version="1.0.0.9" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.GooglePlayServices.Auth" version="120.4.0" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.GooglePlayServices.Auth.Api.Phone" version="118.0.1.2" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.GooglePlayServices.Auth.Base" version="118.0.6" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.GooglePlayServices.Base" version="118.1.0" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.GooglePlayServices.Basement" version="118.1.0.1" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.GooglePlayServices.Fido" version="119.0.0" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.GooglePlayServices.Tasks" version="118.0.2" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.Jetbrains.Annotations" version="23.0.0.4" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.Kotlin.StdLib" version="1.7.10" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.Kotlin.StdLib.Common" version="1.7.10" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.Kotlin.StdLib.Jdk7" version="1.7.10" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.Kotlin.StdLib.Jdk8" version="1.7.10" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.KotlinX.Coroutines.Android" version="1.6.4" targetFramework="monoandroid13.0" />
|
||||
<package id="Xamarin.KotlinX.Coroutines.Core.Jvm" version="1.6.4" targetFramework="monoandroid13.0" />
|
||||
</packages>
|
||||
@@ -13,6 +13,7 @@
|
||||
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
|
||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
||||
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
|
||||
<AndroidClassParser>class-parse</AndroidClassParser>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
||||
144
src/KeePass.sln
@@ -1,12 +1,10 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29418.71
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33205.214
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePassLib2Android", "KeePassLib2Android\KeePassLib2Android.csproj", "{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "keepass2android", "keepass2android\keepass2android.csproj", "{A6CF8A86-37C1-4197-80FE-519DE2C842F5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aKeyboardBinding", "Kp2aKeyboardBinding\Kp2aKeyboardBinding.csproj", "{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aBusinessLogic", "Kp2aBusinessLogic\Kp2aBusinessLogic.csproj", "{53A9CB7F-6553-4BC0-B56B-9410BB2E59AA}"
|
||||
@@ -23,10 +21,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginSdkBinding", "PluginS
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZlibAndroid", "ZlibAndroid\ZlibAndroid.csproj", "{6C29A7E7-E016-4FC1-B1A0-DEE26AC711BB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamsungPass", "SamsungPass\Xamarin.SamsungPass\SamsungPass\SamsungPass.csproj", "{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCloudBindings", "PCloudBindings\PCloudBindings.csproj", "{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "keepass2android-app", "keepass2android\keepass2android-app.csproj", "{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kp2aAutofillParser", "Kp2aAutofillParser\Kp2aAutofillParser.csproj", "{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kp2aAutofillParserTest", "Kp2aAutofillParserTest\Kp2aAutofillParserTest.csproj", "{3D1560FF-86BB-4CB4-8367-80BA13B81C38}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -65,34 +67,6 @@ Global
|
||||
{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Any CPU.Deploy.0 = ReleaseNoNet|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Mixed Platforms.Deploy.0 = ReleaseNoNet|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{A6CF8A86-37C1-4197-80FE-519DE2C842F5}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
@@ -253,24 +227,6 @@ Global
|
||||
{6C29A7E7-E016-4FC1-B1A0-DEE26AC711BB}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
|
||||
{6C29A7E7-E016-4FC1-B1A0-DEE26AC711BB}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
{6C29A7E7-E016-4FC1-B1A0-DEE26AC711BB}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
@@ -295,6 +251,90 @@ Global
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|Win32.Build.0 = ReleaseNoNet|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|x64.Build.0 = ReleaseNoNet|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|Win32.Deploy.0 = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Debug|x64.Deploy.0 = Debug|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|Win32.Deploy.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.Release|x64.Deploy.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|Win32.Deploy.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
|
||||
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|x64.Deploy.0 = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|x64.Build.0 = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -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,11 +50,6 @@ namespace KeePassLib.Collections
|
||||
return m_d.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_d.Clear();
|
||||
}
|
||||
|
||||
private int GetFreeID()
|
||||
{
|
||||
int i = m_d.Count;
|
||||
@@ -105,11 +103,9 @@ namespace KeePassLib.Collections
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -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,35 +34,47 @@ 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;
|
||||
}
|
||||
|
||||
@@ -70,14 +82,28 @@ namespace KeePassLib.Collections
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -85,8 +111,19 @@ namespace KeePassLib.Collections
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -94,37 +131,40 @@ namespace KeePassLib.Collections
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +29,28 @@ namespace KeePassLib.Cryptography.Cipher
|
||||
{
|
||||
public sealed class ChaCha20Engine : ICipherEngine2
|
||||
{
|
||||
private PwUuid m_uuid = new PwUuid(new byte[] {
|
||||
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
|
||||
});
|
||||
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
|
||||
|
||||
@@ -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,5 +33,10 @@ namespace KeePassLib.Interfaces
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
PwUuid PreviousParentGroup
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,7 @@
|
||||
<Compile Include="IDatabaseFormat.cs" />
|
||||
<Compile Include="Keys\KcpKeyFile.Xml.cs" />
|
||||
<Compile Include="Kp2aLog.cs" />
|
||||
<Compile Include="PwGroup.Search.cs" />
|
||||
<Compile Include="Resources\Resource.designer.cs" />
|
||||
<Compile Include="Resources\KLRes.Generated.cs" />
|
||||
<Compile Include="Resources\KSRes.Generated.cs" />
|
||||
|
||||
@@ -58,12 +58,12 @@ namespace keepass2android
|
||||
|
||||
}
|
||||
|
||||
private static string LogFilename
|
||||
public static string LogFilename
|
||||
{
|
||||
get { return Application.Context.FilesDir.CanonicalPath +"/keepass2android.log"; }
|
||||
}
|
||||
|
||||
private static bool LogToFile
|
||||
public static bool LogToFile
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -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 pwUuid, byte[] pbImageDataPng)
|
||||
public PwCustomIcon(PwUuid pu, byte[] pbImageDataPng)
|
||||
{
|
||||
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");
|
||||
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"); }
|
||||
|
||||
Debug.Assert(pbImageDataPng != null);
|
||||
if(pbImageDataPng == null) throw new ArgumentNullException("pbImageDataPng");
|
||||
|
||||
m_pwUuid = pwUuid;
|
||||
m_uuid = pu;
|
||||
m_pbImageDataPng = pbImageDataPng;
|
||||
}
|
||||
|
||||
#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
|
||||
private static long GetKey(int w, int h)
|
||||
{
|
||||
return (((long)w << 32) ^ (long)h);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the icon as an <c>Image</c> (original size).
|
||||
/// </summary>
|
||||
public Android.Graphics.Bitmap GetImage()
|
||||
{
|
||||
const long lKey = -1;
|
||||
|
||||
Android.Graphics.Bitmap img;
|
||||
if (m_dImageCache.TryGetValue(lKey, out img)) return img;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -739,6 +739,12 @@ namespace KeePassLib
|
||||
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;
|
||||
@@ -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,16 +1332,18 @@ 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;
|
||||
puPrevParent = PwUuid.Zero;
|
||||
|
||||
IStructureItem ptOrg = ppOrg.GetItemByUuid(t.Uuid);
|
||||
if (ptOrg != null)
|
||||
{
|
||||
dtLoc = ptOrg.LocationChanged;
|
||||
puPrevParent = ptOrg.PreviousParentGroup;
|
||||
p = ppOrg;
|
||||
}
|
||||
|
||||
@@ -1277,6 +1351,7 @@ namespace KeePassLib
|
||||
if ((ptSrc != null) && (ptSrc.LocationChanged > dtLoc))
|
||||
{
|
||||
dtLoc = ptSrc.LocationChanged;
|
||||
puPrevParent = ptSrc.PreviousParentGroup;
|
||||
p = ppSrc;
|
||||
}
|
||||
|
||||
@@ -1313,8 +1388,13 @@ namespace KeePassLib
|
||||
GroupHandler gh = delegate (PwGroup pgSub)
|
||||
{
|
||||
DateTime dt;
|
||||
if(GetBestPool<PwGroup>(pgSub, ppOrg, ppSrc, out dt) != null)
|
||||
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;
|
||||
};
|
||||
@@ -1322,8 +1402,13 @@ namespace KeePassLib
|
||||
EntryHandler eh = delegate (PwEntry pe)
|
||||
{
|
||||
DateTime dt;
|
||||
if(GetBestPool<PwEntry>(pe, ppOrg, ppSrc, out dt) != null)
|
||||
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;
|
||||
};
|
||||
@@ -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,
|
||||
@@ -1585,68 +1676,64 @@ namespace KeePassLib
|
||||
else { Debug.Assert(false); return null; }
|
||||
}
|
||||
|
||||
public bool DeleteCustomIcons(List<PwUuid> vUuidsToDelete)
|
||||
public bool DeleteCustomIcons(List<PwUuid> lUuids)
|
||||
{
|
||||
Debug.Assert(vUuidsToDelete != null);
|
||||
if(vUuidsToDelete == null) throw new ArgumentNullException("vUuidsToDelete");
|
||||
if(vUuidsToDelete.Count <= 0) return true;
|
||||
if (lUuids == null) { Debug.Assert(false); throw new ArgumentNullException("lUuids"); }
|
||||
if (lUuids.Count == 0) return false;
|
||||
|
||||
Dictionary<PwUuid, bool> dToDel = new Dictionary<PwUuid, bool>();
|
||||
foreach (PwUuid pu in lUuids) { dToDel[pu] = true; }
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
Predicate<PwCustomIcon> f = delegate (PwCustomIcon ci) { return (ci == null); };
|
||||
m_vCustomIcons.RemoveAll(f);
|
||||
|
||||
FixCustomIconRefs();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void FixCustomIconRefs()
|
||||
{
|
||||
Dictionary<PwUuid, bool> d = new Dictionary<PwUuid, bool>();
|
||||
foreach (PwCustomIcon ci in m_vCustomIcons) { d[ci.Uuid] = true; }
|
||||
|
||||
GroupHandler gh = delegate (PwGroup pg)
|
||||
{
|
||||
PwUuid uuidThis = pg.CustomIconUuid;
|
||||
if(uuidThis.Equals(PwUuid.Zero)) return true;
|
||||
|
||||
foreach(PwUuid uuidDelete in vUuidsToDelete)
|
||||
{
|
||||
if(uuidThis.Equals(uuidDelete))
|
||||
{
|
||||
pg.CustomIconUuid = PwUuid.Zero;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PwUuid pu = pg.CustomIconUuid;
|
||||
if (pu.Equals(PwUuid.Zero)) return true;
|
||||
if (!d.ContainsKey(pu)) pg.CustomIconUuid = PwUuid.Zero;
|
||||
return true;
|
||||
};
|
||||
|
||||
EntryHandler eh = delegate (PwEntry pe)
|
||||
{
|
||||
RemoveCustomIconUuid(pe, vUuidsToDelete);
|
||||
FixCustomIconRefs(pe, d);
|
||||
return true;
|
||||
};
|
||||
|
||||
gh(m_pgRootGroup);
|
||||
if(!m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return false;
|
||||
m_pgRootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
|
||||
}
|
||||
|
||||
foreach(PwUuid pwUuid in vUuidsToDelete)
|
||||
private void FixCustomIconRefs(PwEntry pe, Dictionary<PwUuid, bool> d)
|
||||
{
|
||||
int nIndex = GetCustomIconIndex(pwUuid);
|
||||
if(nIndex >= 0) m_vCustomIcons.RemoveAt(nIndex);
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void RemoveCustomIconUuid(PwEntry pe, List<PwUuid> vToDelete)
|
||||
{
|
||||
PwUuid uuidThis = pe.CustomIconUuid;
|
||||
if(uuidThis.Equals(PwUuid.Zero)) return;
|
||||
|
||||
foreach(PwUuid uuidDelete in vToDelete)
|
||||
{
|
||||
if(uuidThis.Equals(uuidDelete))
|
||||
{
|
||||
pe.CustomIconUuid = PwUuid.Zero;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -240,12 +252,20 @@ namespace KeePassLib
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,12 +917,10 @@ namespace KeePassLib
|
||||
|
||||
if (bAlsoChangeHistoryUuids)
|
||||
{
|
||||
foreach (PwEntry peHist in m_listHistory)
|
||||
{
|
||||
foreach (PwEntry peHist in m_lHistory)
|
||||
peHist.Uuid = pwNewUuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCreatedNow()
|
||||
{
|
||||
|
||||
@@ -316,4 +316,13 @@ namespace KeePassLib
|
||||
Cinnamon,
|
||||
Pantheon
|
||||
}
|
||||
|
||||
|
||||
public enum PwSearchMode
|
||||
{
|
||||
None = 0,
|
||||
Simple,
|
||||
Regular,
|
||||
XPath
|
||||
}
|
||||
}
|
||||
|
||||
372
src/KeePassLib2Android/PwGroup.Search.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -20,35 +20,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
|
||||
using KeePassLib.Collections;
|
||||
using KeePassLib.Delegates;
|
||||
using KeePassLib.Interfaces;
|
||||
using KeePassLib.Security;
|
||||
using KeePassLib.Resources;
|
||||
using KeePassLib.Utility;
|
||||
|
||||
namespace KeePassLib
|
||||
{
|
||||
/// <summary>
|
||||
/// A group containing several password entries.
|
||||
/// A group containing subgroups and entries.
|
||||
/// </summary>
|
||||
public sealed class PwGroup : ITimeLogger, IStructureItem, IDeepCloneable<PwGroup>
|
||||
public sealed partial class PwGroup : ITimeLogger, IStructureItem, IDeepCloneable<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";
|
||||
|
||||
public const bool DefaultAutoTypeEnabled = true;
|
||||
public const bool DefaultSearchingEnabled = true;
|
||||
|
||||
private PwObjectList<PwGroup> m_listGroups = new PwObjectList<PwGroup>();
|
||||
private PwObjectList<PwEntry> m_listEntries = new PwObjectList<PwEntry>();
|
||||
private PwGroup m_pParentGroup = null;
|
||||
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
|
||||
// In the tree view of Windows 10, the X coordinate is reset
|
||||
// to 0 after 256 nested nodes
|
||||
private const uint MaxDepth = 126; // Depth 126 = level 127 < 256/2
|
||||
|
||||
private PwUuid m_uuid = PwUuid.Zero;
|
||||
private PwGroup m_pParentGroup = null;
|
||||
private DateTime m_tParentGroupLastMod = PwDefs.DtDefaultNow;
|
||||
private PwUuid m_puPrevParentGroup = PwUuid.Zero;
|
||||
|
||||
private PwObjectList<PwGroup> m_listGroups = new PwObjectList<PwGroup>();
|
||||
private PwObjectList<PwEntry> m_listEntries = new PwObjectList<PwEntry>();
|
||||
|
||||
private string m_strName = string.Empty;
|
||||
private string m_strNotes = string.Empty;
|
||||
|
||||
@@ -72,6 +73,8 @@ namespace KeePassLib
|
||||
|
||||
private PwUuid m_pwLastTopVisibleEntry = PwUuid.Zero;
|
||||
|
||||
private List<string> m_lTags = new List<string>();
|
||||
|
||||
private StringDictionaryEx m_dCustomData = new StringDictionaryEx();
|
||||
|
||||
/// <summary>
|
||||
@@ -82,11 +85,42 @@ namespace KeePassLib
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the group to which this group belongs. May be <c>null</c>.
|
||||
/// </summary>
|
||||
public PwGroup ParentGroup
|
||||
{
|
||||
get { return m_pParentGroup; }
|
||||
|
||||
// Plugins: use the PwGroup.AddGroup method instead.
|
||||
// Internal: check depth using CanAddGroup/CheckCanAddGroup.
|
||||
internal set { Debug.Assert(value != this); m_pParentGroup = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when the location of the object was last changed.
|
||||
/// </summary>
|
||||
public DateTime LocationChanged
|
||||
{
|
||||
get { return m_tParentGroupLastMod; }
|
||||
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>
|
||||
/// The name of this group. Cannot be <c>null</c>.
|
||||
/// </summary>
|
||||
@@ -95,7 +129,7 @@ namespace KeePassLib
|
||||
get { return m_strName; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
|
||||
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
|
||||
m_strName = value;
|
||||
}
|
||||
}
|
||||
@@ -108,7 +142,7 @@ namespace KeePassLib
|
||||
get { return m_strNotes; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
|
||||
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
|
||||
m_strNotes = value;
|
||||
}
|
||||
}
|
||||
@@ -132,31 +166,11 @@ namespace KeePassLib
|
||||
get { return m_pwCustomIconID; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
|
||||
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
|
||||
m_pwCustomIconID = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the group to which this group belongs. May be <c>null</c>.
|
||||
/// </summary>
|
||||
public PwGroup ParentGroup
|
||||
{
|
||||
get { return m_pParentGroup; }
|
||||
|
||||
// Plugins: use <c>PwGroup.AddGroup</c> instead.
|
||||
internal set { Debug.Assert(value != this); m_pParentGroup = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date/time when the location of the object was last changed.
|
||||
/// </summary>
|
||||
public DateTime LocationChanged
|
||||
{
|
||||
get { return m_tParentGroupLastMod; }
|
||||
set { m_tParentGroupLastMod = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A flag that specifies if the group is shown as expanded or
|
||||
/// collapsed in the user interface.
|
||||
@@ -261,7 +275,7 @@ namespace KeePassLib
|
||||
get { return m_strDefaultAutoTypeSequence; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
|
||||
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
|
||||
m_strDefaultAutoTypeSequence = value;
|
||||
}
|
||||
}
|
||||
@@ -283,11 +297,21 @@ namespace KeePassLib
|
||||
get { return m_pwLastTopVisibleEntry; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null); if (value == null) throw new ArgumentNullException("value");
|
||||
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
|
||||
m_pwLastTopVisibleEntry = value;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> Tags
|
||||
{
|
||||
get { StrUtil.NormalizeTags(m_lTags); return m_lTags; }
|
||||
set
|
||||
{
|
||||
if (value == null) { Debug.Assert(false); throw new ArgumentNullException("value"); }
|
||||
m_lTags = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom data container that can be used by plugins to store
|
||||
/// own data in KeePass groups.
|
||||
@@ -326,8 +350,11 @@ namespace KeePassLib
|
||||
|
||||
if (bSetTimes)
|
||||
{
|
||||
m_tCreation = m_tLastMod = m_tLastAccess =
|
||||
m_tParentGroupLastMod = DateTime.Now;
|
||||
DateTime dtNow = DateTime.UtcNow;
|
||||
m_tCreation = dtNow;
|
||||
m_tLastMod = dtNow;
|
||||
m_tLastAccess = dtNow;
|
||||
m_tParentGroupLastMod = dtNow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,8 +371,11 @@ namespace KeePassLib
|
||||
|
||||
if (bSetTimes)
|
||||
{
|
||||
m_tCreation = m_tLastMod = m_tLastAccess =
|
||||
m_tParentGroupLastMod = DateTime.Now;
|
||||
DateTime dtNow = DateTime.UtcNow;
|
||||
m_tCreation = dtNow;
|
||||
m_tLastMod = dtNow;
|
||||
m_tLastAccess = dtNow;
|
||||
m_tParentGroupLastMod = dtNow;
|
||||
}
|
||||
|
||||
if (strName != null) m_strName = strName;
|
||||
@@ -374,8 +404,11 @@ namespace KeePassLib
|
||||
|
||||
pg.m_listGroups = m_listGroups.CloneDeep();
|
||||
pg.m_listEntries = m_listEntries.CloneDeep();
|
||||
pg.TakeOwnership(true, true, false);
|
||||
|
||||
pg.m_pParentGroup = m_pParentGroup;
|
||||
pg.m_tParentGroupLastMod = m_tParentGroupLastMod;
|
||||
pg.m_puPrevParentGroup = m_puPrevParentGroup;
|
||||
|
||||
pg.m_strName = m_strName;
|
||||
pg.m_strNotes = m_strNotes;
|
||||
@@ -400,6 +433,8 @@ namespace KeePassLib
|
||||
|
||||
pg.m_pwLastTopVisibleEntry = m_pwLastTopVisibleEntry;
|
||||
|
||||
pg.m_lTags.AddRange(m_lTags);
|
||||
|
||||
pg.m_dCustomData = m_dCustomData.CloneDeep();
|
||||
|
||||
return pg;
|
||||
@@ -438,6 +473,8 @@ namespace KeePassLib
|
||||
if (m_pParentGroup != pg.m_pParentGroup) return false;
|
||||
if (!bIgnoreLastMod && (m_tParentGroupLastMod != pg.m_tParentGroupLastMod))
|
||||
return false;
|
||||
if (!m_puPrevParentGroup.Equals(pg.m_puPrevParentGroup))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_strName != pg.m_strName) return false;
|
||||
@@ -470,6 +507,9 @@ namespace KeePassLib
|
||||
|
||||
if (!m_pwLastTopVisibleEntry.Equals(pg.m_pwLastTopVisibleEntry)) return false;
|
||||
|
||||
// The Tags property normalizes
|
||||
if (!MemUtil.ListsEqual<string>(this.Tags, pg.Tags)) return false;
|
||||
|
||||
if (!m_dCustomData.Equals(pg.m_dCustomData)) return false;
|
||||
|
||||
if ((pwOpt & PwCompareOptions.PropertiesOnly) == PwCompareOptions.None)
|
||||
@@ -516,7 +556,10 @@ namespace KeePassLib
|
||||
m_uuid = pgTemplate.m_uuid;
|
||||
|
||||
if (bAssignLocationChanged)
|
||||
{
|
||||
m_tParentGroupLastMod = pgTemplate.m_tParentGroupLastMod;
|
||||
m_puPrevParentGroup = pgTemplate.m_puPrevParentGroup;
|
||||
}
|
||||
|
||||
m_strName = pgTemplate.m_strName;
|
||||
m_strNotes = pgTemplate.m_strNotes;
|
||||
@@ -538,6 +581,8 @@ namespace KeePassLib
|
||||
|
||||
m_pwLastTopVisibleEntry = pgTemplate.m_pwLastTopVisibleEntry;
|
||||
|
||||
m_lTags = new List<string>(pgTemplate.m_lTags);
|
||||
|
||||
m_dCustomData = pgTemplate.m_dCustomData.CloneDeep();
|
||||
}
|
||||
|
||||
@@ -562,7 +607,7 @@ namespace KeePassLib
|
||||
/// get touched, too.</param>
|
||||
public void Touch(bool bModified, bool bTouchParents)
|
||||
{
|
||||
m_tLastAccess = DateTime.Now;
|
||||
m_tLastAccess = DateTime.UtcNow;
|
||||
++m_uUsageCount;
|
||||
|
||||
if (bModified) m_tLastMod = m_tLastAccess;
|
||||
@@ -667,21 +712,15 @@ namespace KeePassLib
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PwGroup pg in m_listGroups)
|
||||
{
|
||||
if (groupHandler != null)
|
||||
{
|
||||
foreach(PwGroup pg in m_listGroups)
|
||||
{
|
||||
if (!groupHandler(pg)) return false;
|
||||
}
|
||||
|
||||
pg.PreOrderTraverseTree(groupHandler, entryHandler);
|
||||
}
|
||||
}
|
||||
else // groupHandler == null
|
||||
{
|
||||
foreach(PwGroup pg in m_listGroups)
|
||||
{
|
||||
pg.PreOrderTraverseTree(null, entryHandler);
|
||||
}
|
||||
if (!pg.PreOrderTraverseTree(groupHandler, entryHandler))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -768,357 +807,95 @@ namespace KeePassLib
|
||||
return PreOrderTraverseTree(null, eh);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
internal List<string> GetTagsInherited(bool bNormalize)
|
||||
{
|
||||
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
|
||||
List<string> l = new List<string>();
|
||||
|
||||
PwGroup pg = this;
|
||||
for (int iTerm = 0; iTerm < lTerms.Count; ++iTerm)
|
||||
while (pg != null)
|
||||
{
|
||||
// Update counters for a better state guess
|
||||
if (slStatus != null)
|
||||
{
|
||||
ulong uRemRounds = (ulong)(lTerms.Count - iTerm);
|
||||
uTotalEntries = uCurEntries + (uRemRounds *
|
||||
pg.GetEntriesCount(true));
|
||||
l.AddRange(pg.Tags);
|
||||
pg = pg.m_pParentGroup;
|
||||
}
|
||||
|
||||
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 = "<22> " + contextString.Substring(startPos, SearchContextStringMaxLength) + ((startPos + SearchContextStringMaxLength < contextString.Length) ? " <20>" : null);
|
||||
}
|
||||
resultContexts[pe.Uuid] = new KeyValuePair<string, string>(contextFieldName, contextString);
|
||||
}
|
||||
}
|
||||
if (bNormalize) StrUtil.NormalizeTags(l);
|
||||
return l;
|
||||
}
|
||||
|
||||
public List<string> BuildEntryTagsList()
|
||||
{
|
||||
return BuildEntryTagsList(false);
|
||||
return BuildEntryTagsList(false, false);
|
||||
}
|
||||
|
||||
public List<string> BuildEntryTagsList(bool bSort)
|
||||
{
|
||||
List<string> vTags = new List<string>();
|
||||
return BuildEntryTagsList(bSort, false);
|
||||
}
|
||||
|
||||
internal List<string> BuildEntryTagsList(bool bSort, bool bGroupTags)
|
||||
{
|
||||
Dictionary<string, bool> d = new Dictionary<string, bool>();
|
||||
|
||||
GroupHandler gh = null;
|
||||
if (bGroupTags)
|
||||
{
|
||||
gh = delegate (PwGroup pg)
|
||||
{
|
||||
foreach (string strTag in pg.Tags) d[strTag] = true;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
EntryHandler eh = delegate (PwEntry pe)
|
||||
{
|
||||
foreach (string strTag in pe.Tags)
|
||||
{
|
||||
bool bFound = false;
|
||||
for (int i = 0; i < vTags.Count; ++i)
|
||||
{
|
||||
if (vTags[i].Equals(strTag, StrUtil.CaseIgnoreCmp))
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFound) vTags.Add(strTag);
|
||||
}
|
||||
|
||||
foreach (string strTag in pe.Tags) d[strTag] = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
TraverseTree(TraversalMethod.PreOrder, null, eh);
|
||||
if (bSort) vTags.Sort(StrUtil.CaseIgnoreComparer);
|
||||
return vTags;
|
||||
if (gh != null) gh(this);
|
||||
TraverseTree(TraversalMethod.PreOrder, gh, eh);
|
||||
|
||||
List<string> l = new List<string>(d.Keys);
|
||||
if (bSort) l.Sort(StrUtil.CompareNaturally);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
#if !KeePassLibSD
|
||||
public IDictionary<string, uint> BuildEntryTagsDict(bool bSort)
|
||||
{
|
||||
IDictionary<string, uint> d;
|
||||
if (!bSort) d = new Dictionary<string, uint>(StrUtil.CaseIgnoreComparer);
|
||||
else d = new SortedDictionary<string, uint>(StrUtil.CaseIgnoreComparer);
|
||||
Debug.Assert(!bSort); // Obsolete
|
||||
|
||||
EntryHandler eh = delegate(PwEntry pe)
|
||||
IDictionary<string, uint> d;
|
||||
if (!bSort) d = new Dictionary<string, uint>();
|
||||
else d = new SortedDictionary<string, uint>();
|
||||
|
||||
GroupHandler gh = delegate (PwGroup pg)
|
||||
{
|
||||
foreach (string strTag in pe.Tags)
|
||||
foreach (string strTag in pg.Tags)
|
||||
{
|
||||
uint u;
|
||||
if (d.TryGetValue(strTag, out u)) d[strTag] = u + 1;
|
||||
else d[strTag] = 1;
|
||||
// For groups without entries
|
||||
if (!d.ContainsKey(strTag)) d[strTag] = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
TraverseTree(TraversalMethod.PreOrder, null, eh);
|
||||
EntryHandler eh = delegate (PwEntry pe)
|
||||
{
|
||||
foreach (string strTag in pe.GetTagsInherited())
|
||||
{
|
||||
uint u;
|
||||
d.TryGetValue(strTag, out u);
|
||||
d[strTag] = u + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gh(this);
|
||||
TraverseTree(TraversalMethod.PreOrder, gh, eh);
|
||||
|
||||
return d;
|
||||
}
|
||||
#endif
|
||||
@@ -1127,24 +904,29 @@ namespace KeePassLib
|
||||
bool bSearchRecursive)
|
||||
{
|
||||
if (strTag == null) throw new ArgumentNullException("strTag");
|
||||
if (strTag.Length == 0) return;
|
||||
|
||||
foreach (PwEntry pe in m_listEntries)
|
||||
strTag = StrUtil.NormalizeTag(strTag);
|
||||
if (string.IsNullOrEmpty(strTag)) return;
|
||||
|
||||
EntryHandler eh = delegate (PwEntry pe)
|
||||
{
|
||||
foreach (string strEntryTag in pe.Tags)
|
||||
foreach (string strEntryTag in pe.GetTagsInherited())
|
||||
{
|
||||
if (strEntryTag.Equals(strTag, StrUtil.CaseIgnoreCmp))
|
||||
if (strEntryTag == strTag)
|
||||
{
|
||||
listStorage.Add(pe);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (bSearchRecursive)
|
||||
TraverseTree(TraversalMethod.PreOrder, null, eh);
|
||||
else
|
||||
{
|
||||
foreach (PwGroup pg in m_listGroups)
|
||||
pg.FindEntriesByTag(strTag, listStorage, true);
|
||||
foreach (PwEntry pe in m_listEntries) eh(pe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1279,7 +1061,7 @@ namespace KeePassLib
|
||||
PwGroup pg = m_pParentGroup;
|
||||
while (pg != null)
|
||||
{
|
||||
if ((!bIncludeTopMostGroup) && (pg.m_pParentGroup == null))
|
||||
if (!bIncludeTopMostGroup && (pg.m_pParentGroup == null))
|
||||
break;
|
||||
|
||||
strPath = pg.Name + strSeparator + strPath;
|
||||
@@ -1402,21 +1184,34 @@ namespace KeePassLib
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get the level of the group (i.e. the number of parent groups).
|
||||
/// Get the depth of this group (i.e. the number of ancestors).
|
||||
/// </summary>
|
||||
/// <returns>Number of parent groups.</returns>
|
||||
public uint GetLevel()
|
||||
/// <returns>Depth of this group.</returns>
|
||||
public uint GetDepth()
|
||||
{
|
||||
PwGroup pg = m_pParentGroup;
|
||||
uint uLevel = 0;
|
||||
uint d = 0;
|
||||
|
||||
while (pg != null)
|
||||
{
|
||||
pg = pg.ParentGroup;
|
||||
++uLevel;
|
||||
pg = pg.m_pParentGroup;
|
||||
++d;
|
||||
}
|
||||
|
||||
return uLevel;
|
||||
return d;
|
||||
}
|
||||
|
||||
private uint GetHeight()
|
||||
{
|
||||
if (m_listGroups.UCount == 0) return 0;
|
||||
|
||||
uint h = 0;
|
||||
foreach (PwGroup pgSub in m_listGroups)
|
||||
{
|
||||
h = Math.Max(h, pgSub.GetHeight());
|
||||
}
|
||||
|
||||
return (h + 1);
|
||||
}
|
||||
|
||||
public string GetAutoTypeSequenceInherited()
|
||||
@@ -1460,7 +1255,7 @@ namespace KeePassLib
|
||||
/// subgroups.</returns>
|
||||
public PwObjectList<PwGroup> GetGroups(bool bRecursive)
|
||||
{
|
||||
if (bRecursive == false) return m_listGroups;
|
||||
if (!bRecursive) return m_listGroups;
|
||||
|
||||
PwObjectList<PwGroup> list = m_listGroups.CloneShallow();
|
||||
foreach (PwGroup pgSub in m_listGroups)
|
||||
@@ -1473,15 +1268,20 @@ namespace KeePassLib
|
||||
|
||||
public PwObjectList<PwEntry> GetEntries(bool bIncludeSubGroupEntries)
|
||||
{
|
||||
if (bIncludeSubGroupEntries == false) return m_listEntries;
|
||||
PwObjectList<PwEntry> l = new PwObjectList<PwEntry>();
|
||||
|
||||
PwObjectList<PwEntry> list = m_listEntries.CloneShallow();
|
||||
foreach (PwGroup pgSub in m_listGroups)
|
||||
GroupHandler gh = delegate (PwGroup pg)
|
||||
{
|
||||
list.Add(pgSub.GetEntries(true));
|
||||
}
|
||||
l.Add(pg.Entries);
|
||||
return true;
|
||||
};
|
||||
|
||||
return list;
|
||||
gh(this);
|
||||
if (bIncludeSubGroupEntries)
|
||||
PreOrderTraverseTree(gh, null);
|
||||
|
||||
Debug.Assert(l.UCount == GetEntriesCount(bIncludeSubGroupEntries));
|
||||
return l;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1550,11 +1350,29 @@ namespace KeePassLib
|
||||
{
|
||||
if (subGroup == null) throw new ArgumentNullException("subGroup");
|
||||
|
||||
CheckCanAddGroup(subGroup);
|
||||
m_listGroups.Add(subGroup);
|
||||
|
||||
if (bTakeOwnership) subGroup.m_pParentGroup = this;
|
||||
if (bTakeOwnership) subGroup.ParentGroup = this;
|
||||
|
||||
if (bUpdateLocationChangedOfSub) subGroup.LocationChanged = DateTime.Now;
|
||||
if (bUpdateLocationChangedOfSub) subGroup.LocationChanged = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
internal bool CanAddGroup(PwGroup pgSub)
|
||||
{
|
||||
if (pgSub == null) { Debug.Assert(false); return false; }
|
||||
|
||||
uint dCur = GetDepth(), hSub = pgSub.GetHeight();
|
||||
return ((dCur + hSub + 1) <= MaxDepth);
|
||||
}
|
||||
|
||||
internal void CheckCanAddGroup(PwGroup pgSub)
|
||||
{
|
||||
if (!CanAddGroup(pgSub))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new InvalidOperationException(KLRes.StructsTooDeep);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1589,7 +1407,7 @@ namespace KeePassLib
|
||||
// only assign it to the new one
|
||||
if (bTakeOwnership) pe.ParentGroup = this;
|
||||
|
||||
if (bUpdateLocationChangedOfEntry) pe.LocationChanged = DateTime.Now;
|
||||
if (bUpdateLocationChangedOfEntry) pe.LocationChanged = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void SortSubGroups(bool bRecursive)
|
||||
@@ -1605,7 +1423,7 @@ namespace KeePassLib
|
||||
|
||||
public void DeleteAllObjects(PwDatabase pdContext)
|
||||
{
|
||||
DateTime dtNow = DateTime.Now;
|
||||
DateTime dtNow = DateTime.UtcNow;
|
||||
|
||||
foreach (PwEntry pe in m_listEntries)
|
||||
{
|
||||
@@ -1649,7 +1467,7 @@ namespace KeePassLib
|
||||
|
||||
public void SetCreatedNow(bool bRecursive)
|
||||
{
|
||||
DateTime dt = DateTime.Now;
|
||||
DateTime dt = DateTime.UtcNow;
|
||||
|
||||
m_tCreation = dt;
|
||||
m_tLastAccess = dt;
|
||||
@@ -1682,10 +1500,63 @@ namespace KeePassLib
|
||||
|
||||
pg.SetCreatedNow(true);
|
||||
|
||||
pg.TakeOwnership(true, true, true);
|
||||
|
||||
return pg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal string[] GetAutoTypeSequences(bool bWithStd)
|
||||
{
|
||||
try
|
||||
{
|
||||
Dictionary<string, bool> d = new Dictionary<string, bool>();
|
||||
|
||||
Action<string> fAdd = delegate (string str)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(str)) d[str] = true;
|
||||
};
|
||||
|
||||
if (bWithStd)
|
||||
{
|
||||
fAdd(PwDefs.DefaultAutoTypeSequence);
|
||||
fAdd(PwDefs.DefaultAutoTypeSequenceTan);
|
||||
}
|
||||
|
||||
GroupHandler gh = delegate (PwGroup pg)
|
||||
{
|
||||
fAdd(pg.DefaultAutoTypeSequence);
|
||||
return true;
|
||||
};
|
||||
|
||||
EntryHandler eh = delegate (PwEntry pe)
|
||||
{
|
||||
AutoTypeConfig c = pe.AutoType;
|
||||
|
||||
fAdd(c.DefaultSequence);
|
||||
foreach (AutoTypeAssociation a in c.Associations)
|
||||
{
|
||||
fAdd(a.Sequence);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gh(this);
|
||||
TraverseTree(TraversalMethod.PreOrder, gh, eh);
|
||||
|
||||
string[] v = new string[d.Count];
|
||||
if (d.Count != 0)
|
||||
{
|
||||
d.Keys.CopyTo(v, 0);
|
||||
Array.Sort<string>(v, StrUtil.CaseIgnoreComparer);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PwGroupComparer : IComparer<PwGroup>
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace KeePassLib.Resources
|
||||
{
|
||||
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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
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,18 +156,16 @@ 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)
|
||||
@@ -160,7 +174,7 @@ namespace KeePassLib.Security
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -178,8 +192,8 @@ namespace KeePassLib.Security
|
||||
}
|
||||
|
||||
/// <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>
|
||||
@@ -194,46 +208,64 @@ 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;
|
||||
}
|
||||
|
||||
@@ -242,10 +274,34 @@ namespace KeePassLib.Security
|
||||
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)
|
||||
@@ -254,18 +310,14 @@ namespace KeePassLib.Security
|
||||
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)
|
||||
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
|
||||
{
|
||||
@@ -279,21 +331,20 @@ 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;
|
||||
}
|
||||
|
||||
@@ -303,44 +354,81 @@ namespace KeePassLib.Security
|
||||
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)
|
||||
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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 (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)
|
||||
byte[] pbCT = m_pbCT, pbX = m_pbXorPad;
|
||||
if ((pbCT == null) || (pbX == null) || (pbCT.Length != pbX.Length))
|
||||
{
|
||||
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 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];
|
||||
|
||||
public bool EqualsValue(byte[] pb)
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); throw new ArgumentNullException("pb"); }
|
||||
for (int i = 0; i < pbPT.Length; ++i)
|
||||
pbPT[i] = (byte)(pbCT[i] ^ pbX[i]);
|
||||
|
||||
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;
|
||||
} */
|
||||
return pbPT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ namespace KeePassLib.Serialization
|
||||
byte[] pbFile = StrUtil.Utf8.GetBytes(sb.ToString());
|
||||
|
||||
s = IOConnection.OpenWrite(iocLockFile);
|
||||
if(s == null) throw new IOException(iocLockFile.GetDisplayName());
|
||||
if(s == null) throw new IOException(UrlUtil.GetFileName(iocLockFile.Path));
|
||||
s.Write(pbFile, 0, pbFile.Length);
|
||||
}
|
||||
finally { if(s != null) s.Close(); }
|
||||
@@ -205,8 +205,7 @@ namespace KeePassLib.Serialization
|
||||
if(lfiEx != null)
|
||||
{
|
||||
m_iocLockFile = null; // Otherwise Dispose deletes the existing one
|
||||
throw new FileLockException(iocBaseFile.GetDisplayName(),
|
||||
lfiEx.GetOwner());
|
||||
throw new FileLockException(UrlUtil.GetFileName(iocBaseFile.Path), lfiEx.GetOwner());
|
||||
}
|
||||
|
||||
LockFileInfo.Create(m_iocLockFile);
|
||||
|
||||
@@ -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
|
||||
@@ -24,9 +24,6 @@ using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using keepass2android;
|
||||
#if !KeePassUAP
|
||||
using System.Drawing;
|
||||
#endif
|
||||
|
||||
using KeePassLib;
|
||||
using KeePassLib.Collections;
|
||||
@@ -86,8 +83,11 @@ namespace KeePassLib.Serialization
|
||||
private PwDeletedObject m_ctxDeletedObject = null;
|
||||
private PwUuid m_uuidCustomIconID = PwUuid.Zero;
|
||||
private byte[] m_pbCustomIconData = null;
|
||||
private string m_strCustomIconName = null;
|
||||
private DateTime? m_odtCustomIconLastMod = null;
|
||||
private string m_strCustomDataKey = null;
|
||||
private string m_strCustomDataValue = null;
|
||||
private DateTime? m_odtCustomDataLastMod = null;
|
||||
private string m_strGroupCustomDataKey = null;
|
||||
private string m_strGroupCustomDataValue = null;
|
||||
private string m_strEntryCustomDataKey = null;
|
||||
@@ -95,35 +95,10 @@ namespace KeePassLib.Serialization
|
||||
|
||||
private void ReadXmlStreamed(Stream sXml, Stream sParent)
|
||||
{
|
||||
ReadDocumentStreamed(CreateXmlReader(sXml), sParent);
|
||||
}
|
||||
|
||||
internal static XmlReaderSettings CreateStdXmlReaderSettings()
|
||||
using (XmlReader xr = XmlUtilEx.CreateXmlReader(sXml))
|
||||
{
|
||||
XmlReaderSettings xrs = new XmlReaderSettings();
|
||||
|
||||
xrs.CloseInput = true;
|
||||
xrs.IgnoreComments = true;
|
||||
xrs.IgnoreProcessingInstructions = true;
|
||||
xrs.IgnoreWhitespace = true;
|
||||
|
||||
#if KeePassUAP
|
||||
xrs.DtdProcessing = DtdProcessing.Prohibit;
|
||||
#else
|
||||
#if !KeePassLibSD
|
||||
xrs.ProhibitDtd = true; // Obsolete in .NET 4, but still there
|
||||
// xrs.DtdProcessing = DtdProcessing.Prohibit; // .NET 4 only
|
||||
#endif
|
||||
xrs.ValidationType = ValidationType.None;
|
||||
#endif
|
||||
|
||||
return xrs;
|
||||
ReadDocumentStreamed(xr, sParent);
|
||||
}
|
||||
|
||||
private static XmlReader CreateXmlReader(Stream readerStream)
|
||||
{
|
||||
XmlReaderSettings xrs = CreateStdXmlReaderSettings();
|
||||
return XmlReader.Create(readerStream, xrs);
|
||||
}
|
||||
|
||||
private void ReadDocumentStreamed(XmlReader xr, Stream sParentStream)
|
||||
@@ -176,7 +151,7 @@ namespace KeePassLib.Serialization
|
||||
}
|
||||
|
||||
++uTagCounter;
|
||||
if(((uTagCounter % 256) == 0) && bSupportsStatus)
|
||||
if (((uTagCounter & 0xFFU) == 0) && bSupportsStatus)
|
||||
{
|
||||
Debug.Assert(lStreamLength == sParentStream.Length);
|
||||
uint uPct = (uint)((sParentStream.Position * 100) /
|
||||
@@ -186,7 +161,8 @@ namespace KeePassLib.Serialization
|
||||
// position/length values (M120413)
|
||||
if (uPct > 100) { Debug.Assert(false); uPct = 100; }
|
||||
|
||||
m_slLogger.SetProgress(uPct);
|
||||
if (!m_slLogger.SetProgress(uPct))
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,6 +306,10 @@ namespace KeePassLib.Serialization
|
||||
m_pbCustomIconData = Convert.FromBase64String(strData);
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
else if (xr.Name == ElemName)
|
||||
m_strCustomIconName = ReadString(xr);
|
||||
else if (xr.Name == ElemLastModTime)
|
||||
m_odtCustomIconLastMod = ReadTime(xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
@@ -366,6 +346,8 @@ namespace KeePassLib.Serialization
|
||||
m_strCustomDataKey = ReadString(xr);
|
||||
else if (xr.Name == ElemValue)
|
||||
m_strCustomDataValue = ReadString(xr);
|
||||
else if (xr.Name == ElemLastModTime)
|
||||
m_odtCustomDataLastMod = ReadTime(xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
@@ -409,6 +391,10 @@ namespace KeePassLib.Serialization
|
||||
m_ctxGroup.EnableSearching = StrUtil.StringToBoolEx(ReadString(xr));
|
||||
else if (xr.Name == ElemLastTopVisibleEntry)
|
||||
m_ctxGroup.LastTopVisibleEntry = ReadUuid(xr);
|
||||
else if (xr.Name == ElemPreviousParentGroup)
|
||||
m_ctxGroup.PreviousParentGroup = ReadUuid(xr);
|
||||
else if (xr.Name == ElemTags)
|
||||
m_ctxGroup.Tags = StrUtil.StringToTags(ReadString(xr));
|
||||
else if (xr.Name == ElemCustomData)
|
||||
return SwitchContext(ctx, KdbContext.GroupCustomData, xr);
|
||||
else if (xr.Name == ElemGroup)
|
||||
@@ -466,8 +452,12 @@ namespace KeePassLib.Serialization
|
||||
}
|
||||
else if (xr.Name == ElemOverrideUrl)
|
||||
m_ctxEntry.OverrideUrl = ReadString(xr);
|
||||
else if (xr.Name == ElemQualityCheck)
|
||||
m_ctxEntry.QualityCheck = ReadBool(xr, true);
|
||||
else if (xr.Name == ElemTags)
|
||||
m_ctxEntry.Tags = StrUtil.StringToTags(ReadString(xr));
|
||||
else if (xr.Name == ElemPreviousParentGroup)
|
||||
m_ctxEntry.PreviousParentGroup = ReadUuid(xr);
|
||||
else if (xr.Name == ElemTimes)
|
||||
return SwitchContext(ctx, KdbContext.EntryTimes, xr);
|
||||
else if (xr.Name == ElemString)
|
||||
@@ -514,6 +504,7 @@ namespace KeePassLib.Serialization
|
||||
tl.LocationChanged = ReadTime(xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.EntryString:
|
||||
if (xr.Name == ElemKey)
|
||||
m_ctxStringName = ReadString(xr);
|
||||
@@ -622,12 +613,19 @@ namespace KeePassLib.Serialization
|
||||
{
|
||||
if (!m_uuidCustomIconID.Equals(PwUuid.Zero) &&
|
||||
(m_pbCustomIconData != null))
|
||||
m_pwDatabase.CustomIcons.Add(new PwCustomIcon(
|
||||
m_uuidCustomIconID, m_pbCustomIconData));
|
||||
{
|
||||
PwCustomIcon ci = new PwCustomIcon(m_uuidCustomIconID,
|
||||
m_pbCustomIconData);
|
||||
if (m_strCustomIconName != null) ci.Name = m_strCustomIconName;
|
||||
ci.LastModificationTime = m_odtCustomIconLastMod;
|
||||
m_pwDatabase.CustomIcons.Add(ci);
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
|
||||
m_uuidCustomIconID = PwUuid.Zero;
|
||||
m_pbCustomIconData = null;
|
||||
m_strCustomIconName = null;
|
||||
m_odtCustomIconLastMod = null;
|
||||
|
||||
return KdbContext.CustomIcons;
|
||||
}
|
||||
@@ -638,11 +636,13 @@ namespace KeePassLib.Serialization
|
||||
else if ((ctx == KdbContext.CustomDataItem) && (xr.Name == ElemStringDictExItem))
|
||||
{
|
||||
if ((m_strCustomDataKey != null) && (m_strCustomDataValue != null))
|
||||
m_pwDatabase.CustomData.Set(m_strCustomDataKey, m_strCustomDataValue);
|
||||
m_pwDatabase.CustomData.Set(m_strCustomDataKey,
|
||||
m_strCustomDataValue, m_odtCustomDataLastMod);
|
||||
else { Debug.Assert(false); }
|
||||
|
||||
m_strCustomDataKey = null;
|
||||
m_strCustomDataValue = null;
|
||||
m_odtCustomDataLastMod = null;
|
||||
|
||||
return KdbContext.CustomData;
|
||||
}
|
||||
@@ -765,10 +765,16 @@ namespace KeePassLib.Serialization
|
||||
{
|
||||
XorredBuffer xb = ProcessNode(xr);
|
||||
if (xb != null)
|
||||
{
|
||||
Debug.Assert(false); // Protected data is unexpected here
|
||||
try
|
||||
{
|
||||
byte[] pb = xb.ReadPlainText();
|
||||
if (pb.Length == 0) return string.Empty;
|
||||
return StrUtil.Utf8.GetString(pb, 0, pb.Length);
|
||||
try { return StrUtil.Utf8.GetString(pb, 0, pb.Length); }
|
||||
finally { MemUtil.ZeroByteArray(pb); }
|
||||
}
|
||||
finally { xb.Dispose(); }
|
||||
}
|
||||
|
||||
m_bReadNextNode = false; // ReadElementString skips end tag
|
||||
@@ -781,6 +787,55 @@ namespace KeePassLib.Serialization
|
||||
return xr.ReadElementString();
|
||||
}
|
||||
|
||||
private byte[] ReadBase64(XmlReader xr, bool bRaw)
|
||||
{
|
||||
// if(bRaw) return ReadBase64RawInChunks(xr);
|
||||
|
||||
string str = (bRaw ? ReadStringRaw(xr) : ReadString(xr));
|
||||
if (string.IsNullOrEmpty(str)) return MemUtil.EmptyByteArray;
|
||||
|
||||
return Convert.FromBase64String(str);
|
||||
}
|
||||
|
||||
/* private byte[] m_pbBase64ReadBuf = new byte[1024 * 1024 * 3];
|
||||
private byte[] ReadBase64RawInChunks(XmlReader xr)
|
||||
{
|
||||
xr.MoveToContent();
|
||||
|
||||
List<byte[]> lParts = new List<byte[]>();
|
||||
byte[] pbBuf = m_pbBase64ReadBuf;
|
||||
while(true)
|
||||
{
|
||||
int cb = xr.ReadElementContentAsBase64(pbBuf, 0, pbBuf.Length);
|
||||
if(cb == 0) break;
|
||||
|
||||
byte[] pb = new byte[cb];
|
||||
Array.Copy(pbBuf, 0, pb, 0, cb);
|
||||
lParts.Add(pb);
|
||||
|
||||
// No break when cb < pbBuf.Length, because ReadElementContentAsBase64
|
||||
// moves to the next XML node only when returning 0
|
||||
}
|
||||
m_bReadNextNode = false;
|
||||
|
||||
if(lParts.Count == 0) return MemUtil.EmptyByteArray;
|
||||
if(lParts.Count == 1) return lParts[0];
|
||||
|
||||
long cbRes = 0;
|
||||
for(int i = 0; i < lParts.Count; ++i)
|
||||
cbRes += lParts[i].Length;
|
||||
|
||||
byte[] pbRes = new byte[cbRes];
|
||||
int cbCur = 0;
|
||||
for(int i = 0; i < lParts.Count; ++i)
|
||||
{
|
||||
Array.Copy(lParts[i], 0, pbRes, cbCur, lParts[i].Length);
|
||||
cbCur += lParts[i].Length;
|
||||
}
|
||||
|
||||
return pbRes;
|
||||
} */
|
||||
|
||||
private bool ReadBool(XmlReader xr, bool bDefault)
|
||||
{
|
||||
string str = ReadString(xr);
|
||||
@@ -793,9 +848,9 @@ namespace KeePassLib.Serialization
|
||||
|
||||
private PwUuid ReadUuid(XmlReader xr)
|
||||
{
|
||||
string str = ReadString(xr);
|
||||
if(string.IsNullOrEmpty(str)) return PwUuid.Zero;
|
||||
return new PwUuid(Convert.FromBase64String(str));
|
||||
byte[] pb = ReadBase64(xr, false);
|
||||
if (pb.Length == 0) return PwUuid.Zero;
|
||||
return new PwUuid(pb);
|
||||
}
|
||||
|
||||
private int ReadInt(XmlReader xr, int nDefault)
|
||||
@@ -862,8 +917,7 @@ namespace KeePassLib.Serialization
|
||||
// long l = ReadLong(xr, -1);
|
||||
// if(l != -1) return DateTime.FromBinary(l);
|
||||
|
||||
string str = ReadString(xr);
|
||||
byte[] pb = Convert.FromBase64String(str);
|
||||
byte[] pb = ReadBase64(xr, false);
|
||||
if (pb.Length != 8)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
@@ -907,7 +961,11 @@ namespace KeePassLib.Serialization
|
||||
private ProtectedString ReadProtectedString(XmlReader xr)
|
||||
{
|
||||
XorredBuffer xb = ProcessNode(xr);
|
||||
if(xb != null) return new ProtectedString(true, xb);
|
||||
if (xb != null)
|
||||
{
|
||||
try { return new ProtectedString(true, xb); }
|
||||
finally { xb.Dispose(); }
|
||||
}
|
||||
|
||||
bool bProtect = false;
|
||||
if (m_format == KdbxFormat.PlainXml)
|
||||
@@ -919,8 +977,7 @@ namespace KeePassLib.Serialization
|
||||
}
|
||||
}
|
||||
|
||||
ProtectedString ps = new ProtectedString(bProtect, ReadString(xr));
|
||||
return ps;
|
||||
return new ProtectedString(bProtect, ReadString(xr));
|
||||
}
|
||||
|
||||
private ProtectedBinary ReadProtectedBinary(XmlReader xr)
|
||||
@@ -962,13 +1019,13 @@ namespace KeePassLib.Serialization
|
||||
if (xb != null)
|
||||
{
|
||||
Debug.Assert(!bCompressed); // See SubWriteValue(ProtectedBinary value)
|
||||
return new ProtectedBinary(true, xb);
|
||||
try { return new ProtectedBinary(true, xb); }
|
||||
finally { xb.Dispose(); }
|
||||
}
|
||||
|
||||
string strValue = ReadString(xr);
|
||||
if(strValue.Length == 0) return new ProtectedBinary();
|
||||
byte[] pbData = ReadBase64(xr, true);
|
||||
if (pbData.Length == 0) return new ProtectedBinary();
|
||||
|
||||
byte[] pbData = Convert.FromBase64String(strValue);
|
||||
if (bCompressed) pbData = MemUtil.Decompress(pbData);
|
||||
return new ProtectedBinary(false, pbData);
|
||||
}
|
||||
@@ -976,28 +1033,37 @@ namespace KeePassLib.Serialization
|
||||
private void ReadUnknown(XmlReader xr)
|
||||
{
|
||||
Debug.Assert(false); // Unknown node!
|
||||
Debug.Assert(xr.NodeType == XmlNodeType.Element);
|
||||
|
||||
if(xr.IsEmptyElement) return;
|
||||
bool bRead = false;
|
||||
int cOpen = 0;
|
||||
|
||||
string strUnknownName = xr.Name;
|
||||
ProcessNode(xr);
|
||||
|
||||
while(xr.Read())
|
||||
do
|
||||
{
|
||||
if(xr.NodeType == XmlNodeType.EndElement) break;
|
||||
if(xr.NodeType != XmlNodeType.Element) continue;
|
||||
if (bRead) xr.Read();
|
||||
bRead = true;
|
||||
|
||||
ReadUnknown(xr);
|
||||
if (xr.NodeType == XmlNodeType.EndElement) --cOpen;
|
||||
else if (xr.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
if (!xr.IsEmptyElement)
|
||||
{
|
||||
XorredBuffer xb = ProcessNode(xr);
|
||||
if (xb != null) { xb.Dispose(); bRead = m_bReadNextNode; continue; }
|
||||
|
||||
++cOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (cOpen > 0);
|
||||
|
||||
Debug.Assert(xr.Name == strUnknownName);
|
||||
m_bReadNextNode = bRead;
|
||||
}
|
||||
|
||||
private XorredBuffer ProcessNode(XmlReader xr)
|
||||
{
|
||||
// Debug.Assert(xr.NodeType == XmlNodeType.Element);
|
||||
|
||||
XorredBuffer xb = null;
|
||||
if (xr.HasAttributes)
|
||||
{
|
||||
if (xr.MoveToAttribute(AttrProtected))
|
||||
@@ -1005,21 +1071,16 @@ namespace KeePassLib.Serialization
|
||||
if (xr.Value == ValTrue)
|
||||
{
|
||||
xr.MoveToElement();
|
||||
string strEncrypted = ReadStringRaw(xr);
|
||||
|
||||
byte[] pbEncrypted;
|
||||
if(strEncrypted.Length > 0)
|
||||
pbEncrypted = Convert.FromBase64String(strEncrypted);
|
||||
else pbEncrypted = MemUtil.EmptyByteArray;
|
||||
byte[] pbCT = ReadBase64(xr, true);
|
||||
byte[] pbPad = m_randomStream.GetRandomBytes((uint)pbCT.Length);
|
||||
|
||||
byte[] pbPad = m_randomStream.GetRandomBytes((uint)pbEncrypted.Length);
|
||||
|
||||
xb = new XorredBuffer(pbEncrypted, pbPad);
|
||||
return new XorredBuffer(pbCT, pbPad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return xb;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static KdbContext SwitchContext(KdbContext ctxCurrent,
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -149,7 +150,6 @@ namespace KeePassLib.Serialization
|
||||
if (m_slLogger != null)
|
||||
m_slLogger.SetText("KP2AKEY_DecodingDatabase", LogStatusType.AdditionalInfo);
|
||||
|
||||
|
||||
sPlain = new HashedBlockStream(sDecrypted, false, 0, !m_bRepairMode);
|
||||
}
|
||||
else // KDBX >= 4
|
||||
@@ -176,6 +176,7 @@ namespace KeePassLib.Serialization
|
||||
if ((sPlain == null) || (sPlain == sBlocks))
|
||||
throw new SecurityException(KLRes.CryptoStreamFailed);
|
||||
}
|
||||
|
||||
lStreams.Add(sPlain);
|
||||
|
||||
if (m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
|
||||
@@ -190,7 +191,11 @@ namespace KeePassLib.Serialization
|
||||
}
|
||||
else if (fmt == KdbxFormat.PlainXml)
|
||||
sXml = sHashing;
|
||||
else { Debug.Assert(false); throw new ArgumentOutOfRangeException("fmt"); }
|
||||
else
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new ArgumentOutOfRangeException("fmt");
|
||||
}
|
||||
|
||||
if (fmt == KdbxFormat.Default)
|
||||
{
|
||||
@@ -203,23 +208,22 @@ namespace KeePassLib.Serialization
|
||||
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();
|
||||
|
||||
@@ -237,8 +241,8 @@ namespace KeePassLib.Serialization
|
||||
|
||||
Kp2aLog.Log(String.Format("ReadXmlStreamed: {0}ms", stopWatch.ElapsedMilliseconds));
|
||||
}
|
||||
// ReadXmlDom(sXml);
|
||||
}
|
||||
// ReadXmlDom(sXml);
|
||||
catch (CryptographicException) // Thrown on invalid padding
|
||||
{
|
||||
throw new CryptographicException(KLRes.FileCorrupted);
|
||||
@@ -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
|
||||
@@ -430,7 +434,7 @@ namespace KeePassLib.Serialization
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
if (m_slLogger != null)
|
||||
m_slLogger.SetText(KLRes.UnknownHeaderId + @": " +
|
||||
m_slLogger.SetText(KLRes.UnknownHeaderId + ": " +
|
||||
kdbID.ToString() + "!", LogStatusType.Warning);
|
||||
break;
|
||||
}
|
||||
@@ -487,6 +491,7 @@ namespace KeePassLib.Serialization
|
||||
|
||||
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);
|
||||
@@ -526,6 +531,29 @@ namespace KeePassLib.Serialization
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -89,6 +87,7 @@ namespace KeePassLib.Serialization
|
||||
|
||||
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;
|
||||
@@ -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,7 +264,6 @@ namespace KeePassLib.Serialization
|
||||
|
||||
CleanUpInnerRandomStream();
|
||||
|
||||
m_xmlWriter = null;
|
||||
m_pbHashOfHeader = null;
|
||||
}
|
||||
|
||||
@@ -449,14 +453,16 @@ namespace KeePassLib.Serialization
|
||||
|
||||
++uCurEntry;
|
||||
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();
|
||||
throw new OperationCanceledException();
|
||||
|
||||
while (groupStack.Count > 1)
|
||||
{
|
||||
@@ -541,6 +547,16 @@ namespace KeePassLib.Serialization
|
||||
WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false);
|
||||
WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -565,8 +581,16 @@ namespace KeePassLib.Serialization
|
||||
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);
|
||||
@@ -616,7 +640,7 @@ namespace KeePassLib.Serialization
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -636,7 +660,7 @@ 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)
|
||||
@@ -690,7 +714,13 @@ namespace KeePassLib.Serialization
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
|
||||
foreach (KeyValuePair<string, string> kvp in value)
|
||||
WriteObject(ElemStringDictExItem, ElemKey, ElemValue, kvp);
|
||||
{
|
||||
DateTime? odtLastMod = null;
|
||||
if (m_uFileVersion >= FileVersion32_4_1)
|
||||
odtLastMod = value.GetLastModificationTime(kvp.Key);
|
||||
|
||||
WriteObject(ElemStringDictExItem, ElemKey, ElemValue, kvp, odtLastMod);
|
||||
}
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
@@ -701,15 +731,23 @@ namespace KeePassLib.Serialization
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -809,18 +847,22 @@ namespace KeePassLib.Serialization
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -857,9 +899,9 @@ namespace KeePassLib.Serialization
|
||||
{
|
||||
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,7 +911,7 @@ 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)
|
||||
@@ -880,8 +922,7 @@ namespace KeePassLib.Serialization
|
||||
// page area
|
||||
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);
|
||||
}
|
||||
@@ -949,9 +990,9 @@ namespace KeePassLib.Serialization
|
||||
{
|
||||
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
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
@@ -1020,60 +1080,15 @@ namespace KeePassLib.Serialization
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,6 +67,9 @@ namespace KeePassLib.Serialization
|
||||
/// </summary>
|
||||
public sealed partial class KdbxFile
|
||||
{
|
||||
/// <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)
|
||||
@@ -108,6 +111,7 @@ namespace KeePassLib.Serialization
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
@@ -356,8 +364,8 @@ 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
|
||||
{
|
||||
@@ -365,7 +373,7 @@ namespace KeePassLib.Serialization
|
||||
foreach (char ch in PwDatabase.LocalizedAppName)
|
||||
uTest = uTest * 5 + ch;
|
||||
|
||||
m_bLocalizedNames = (uTest != NeutralLanguageID);
|
||||
g_bLocalizedNames = (uTest != NeutralLanguageID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,40 +381,75 @@ namespace KeePassLib.Serialization
|
||||
{
|
||||
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();
|
||||
|
||||
uint minRequiredVersion = Math.Max(minVersionForKeys, m_uFileVersion); //don't save a version lower than what we read
|
||||
|
||||
return Math.Max(minRequiredVersion, GetMinKdbxVersionOrig());
|
||||
}
|
||||
|
||||
AesKdf kdfAes = new AesKdf();
|
||||
if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
|
||||
return Math.Max(FileVersion32, minRequiredVersion);
|
||||
private uint GetMinKdbxVersionOrig()
|
||||
{
|
||||
if (m_uForceVersion != 0) return m_uForceVersion;
|
||||
|
||||
if(m_pwDatabase.PublicCustomData.Count > 0)
|
||||
return Math.Max(FileVersion32, minRequiredVersion);
|
||||
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
|
||||
|
||||
uint uMin = 0;
|
||||
|
||||
|
||||
bool bCustomData = false;
|
||||
GroupHandler gh = delegate (PwGroup pg)
|
||||
{
|
||||
if (pg == null) { Debug.Assert(false); return true; }
|
||||
if(pg.CustomData.Count > 0) { bCustomData = true; return false; }
|
||||
|
||||
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)
|
||||
{
|
||||
if (pe == null) { Debug.Assert(false); return true; }
|
||||
if(pe.CustomData.Count > 0) { bCustomData = true; return false; }
|
||||
|
||||
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,
|
||||
@@ -421,7 +464,7 @@ namespace KeePassLib.Serialization
|
||||
Debug.Assert(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);
|
||||
@@ -542,7 +585,7 @@ namespace KeePassLib.Serialization
|
||||
{
|
||||
if (pb == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(string.IsNullOrEmpty(strName)) strName = "File.bin";
|
||||
strName = UrlUtil.GetSafeFileName(strName);
|
||||
|
||||
string strPath;
|
||||
int iTry = 1;
|
||||
@@ -550,8 +593,8 @@ 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)
|
||||
@@ -564,17 +607,9 @@ namespace KeePassLib.Serialization
|
||||
}
|
||||
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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ using System.Diagnostics;
|
||||
|
||||
using KeePassLib.Resources;
|
||||
using KeePassLib.Serialization;
|
||||
using Android.Webkit;
|
||||
|
||||
namespace KeePassLib.Utility
|
||||
{
|
||||
@@ -411,7 +412,7 @@ Clipboard.SetText(ObjectsToMessage(vLines, true));*/
|
||||
public static void ShowLoadWarning(IOConnectionInfo ioConnection, Exception ex)
|
||||
{
|
||||
if (ioConnection != null)
|
||||
ShowLoadWarning(ioConnection.GetDisplayName(), ex, false);
|
||||
ShowLoadWarning(UrlUtil.GetFileName(ioConnection.Path), ex, false);
|
||||
else ShowWarning(ex);
|
||||
}
|
||||
|
||||
@@ -429,7 +430,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;
|
||||
@@ -444,7 +445,7 @@ Clipboard.SetText(ObjectsToMessage(vLines, true));*/
|
||||
bool bCorruptionWarning)
|
||||
{
|
||||
if (ioConnection != null)
|
||||
ShowSaveWarning(ioConnection.GetDisplayName(), ex, bCorruptionWarning);
|
||||
ShowSaveWarning(UrlUtil.GetFileName(ioConnection.Path), ex, bCorruptionWarning);
|
||||
else ShowWarning(ex);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -24,7 +24,6 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#if !KeePassUAP
|
||||
using System.Drawing;
|
||||
@@ -32,6 +31,7 @@ using System.Security.Cryptography;
|
||||
#endif
|
||||
|
||||
using KeePassLib.Collections;
|
||||
using KeePassLib.Cryptography;
|
||||
using KeePassLib.Cryptography.PasswordGenerator;
|
||||
using KeePassLib.Native;
|
||||
using KeePassLib.Security;
|
||||
@@ -43,45 +43,41 @@ namespace KeePassLib.Utility
|
||||
/// </summary>
|
||||
public sealed class CharStream
|
||||
{
|
||||
private string m_strString = string.Empty;
|
||||
private int m_nPos = 0;
|
||||
private readonly string m_str;
|
||||
private int m_iPos = 0;
|
||||
|
||||
public long Position
|
||||
{
|
||||
get { return m_iPos; }
|
||||
set
|
||||
{
|
||||
if ((value < 0) || (value > int.MaxValue))
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
m_iPos = (int)value;
|
||||
}
|
||||
}
|
||||
|
||||
public CharStream(string str)
|
||||
{
|
||||
Debug.Assert(str != null);
|
||||
if(str == null) throw new ArgumentNullException("str");
|
||||
if (str == null) { Debug.Assert(false); throw new ArgumentNullException("str"); }
|
||||
|
||||
m_strString = str;
|
||||
}
|
||||
|
||||
public void Seek(SeekOrigin org, int nSeek)
|
||||
{
|
||||
if(org == SeekOrigin.Begin)
|
||||
m_nPos = nSeek;
|
||||
else if(org == SeekOrigin.Current)
|
||||
m_nPos += nSeek;
|
||||
else if(org == SeekOrigin.End)
|
||||
m_nPos = m_strString.Length + nSeek;
|
||||
m_str = str;
|
||||
}
|
||||
|
||||
public char ReadChar()
|
||||
{
|
||||
if(m_nPos < 0) return char.MinValue;
|
||||
if(m_nPos >= m_strString.Length) return char.MinValue;
|
||||
if (m_iPos >= m_str.Length) return char.MinValue;
|
||||
|
||||
char chRet = m_strString[m_nPos];
|
||||
++m_nPos;
|
||||
return chRet;
|
||||
return m_str[m_iPos++];
|
||||
}
|
||||
|
||||
public char ReadChar(bool bSkipWhiteSpace)
|
||||
{
|
||||
if(bSkipWhiteSpace == false) return ReadChar();
|
||||
if (!bSkipWhiteSpace) return ReadChar();
|
||||
|
||||
while (true)
|
||||
{
|
||||
char ch = ReadChar();
|
||||
|
||||
if ((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
|
||||
return ch;
|
||||
}
|
||||
@@ -89,29 +85,25 @@ namespace KeePassLib.Utility
|
||||
|
||||
public char PeekChar()
|
||||
{
|
||||
if(m_nPos < 0) return char.MinValue;
|
||||
if(m_nPos >= m_strString.Length) return char.MinValue;
|
||||
if (m_iPos >= m_str.Length) return char.MinValue;
|
||||
|
||||
return m_strString[m_nPos];
|
||||
return m_str[m_iPos];
|
||||
}
|
||||
|
||||
public char PeekChar(bool bSkipWhiteSpace)
|
||||
{
|
||||
if(bSkipWhiteSpace == false) return PeekChar();
|
||||
if (!bSkipWhiteSpace) return PeekChar();
|
||||
|
||||
int iIndex = m_nPos;
|
||||
while(true)
|
||||
int i = m_iPos;
|
||||
while (i < m_str.Length)
|
||||
{
|
||||
if(iIndex < 0) return char.MinValue;
|
||||
if(iIndex >= m_strString.Length) return char.MinValue;
|
||||
|
||||
char ch = m_strString[iIndex];
|
||||
|
||||
char ch = m_str[i];
|
||||
if ((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
|
||||
return ch;
|
||||
|
||||
++iIndex;
|
||||
++i;
|
||||
}
|
||||
|
||||
return char.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +179,7 @@ namespace KeePassLib.Utility
|
||||
/// </summary>
|
||||
public static class StrUtil
|
||||
{
|
||||
public const StringComparison CaseIgnoreCmp = StringComparison.OrdinalIgnoreCase;
|
||||
public static readonly StringComparison CaseIgnoreCmp = StringComparison.OrdinalIgnoreCase;
|
||||
|
||||
public static StringComparer CaseIgnoreComparer
|
||||
{
|
||||
@@ -299,23 +291,82 @@ namespace KeePassLib.Utility
|
||||
return ("\\u" + sh.ToString(NumberFormatInfo.InvariantInfo) + "?");
|
||||
}
|
||||
|
||||
internal static bool RtfIsURtf(string str)
|
||||
{
|
||||
if (str == null) { Debug.Assert(false); return false; }
|
||||
|
||||
const string p = "{\\urtf"; // Typically "{\\urtf1\\ansi\\ansicpg65001"
|
||||
return (str.StartsWith(p) && (str.Length > p.Length) &&
|
||||
char.IsDigit(str[p.Length]));
|
||||
}
|
||||
|
||||
public static string RtfFix(string strRtf)
|
||||
{
|
||||
if (strRtf == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
string str = strRtf;
|
||||
|
||||
// Workaround for .NET bug: the Rtf property of a RichTextBox
|
||||
// can return an RTF text starting with "{\\urtf", but
|
||||
// setting such an RTF text throws an exception (the setter
|
||||
// checks for the RTF text to start with "{\\rtf");
|
||||
// https://sourceforge.net/p/keepass/discussion/329221/thread/7788872f/
|
||||
// https://www.microsoft.com/en-us/download/details.aspx?id=10725
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb774284.aspx
|
||||
// https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/RichTextBox.cs
|
||||
if (RtfIsURtf(str)) str = str.Remove(2, 1); // Remove the 'u'
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
internal static string RtfFilterText(string strText)
|
||||
{
|
||||
if (strText == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
// A U+FFFC character causes the rest of the text to be lost.
|
||||
// With '?', the string length, substring indices and
|
||||
// character visibility remain the same.
|
||||
// More special characters (unproblematic) in rich text boxes:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/richedit/ns-richedit-gettextex
|
||||
return strText.Replace('\uFFFC', '?');
|
||||
}
|
||||
|
||||
internal static bool ContainsHighChar(string str)
|
||||
{
|
||||
if (str == null) { Debug.Assert(false); return false; }
|
||||
|
||||
for (int i = 0; i < str.Length; ++i)
|
||||
{
|
||||
if (str[i] > '\u00FF') return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a string into a valid HTML sequence representing that string.
|
||||
/// Convert a string to a HTML sequence representing that string.
|
||||
/// </summary>
|
||||
/// <param name="str">String to convert.</param>
|
||||
/// <returns>String, HTML-encoded.</returns>
|
||||
public static string StringToHtml(string str)
|
||||
{
|
||||
return StringToHtml(str, false);
|
||||
}
|
||||
|
||||
internal static string StringToHtml(string str, bool bNbsp)
|
||||
{
|
||||
Debug.Assert(str != null); if (str == null) throw new ArgumentNullException("str");
|
||||
|
||||
str = str.Replace(@"&", @"&");
|
||||
str = str.Replace(@"&", @"&"); // Must be first
|
||||
str = str.Replace(@"<", @"<");
|
||||
str = str.Replace(@">", @">");
|
||||
str = str.Replace("\"", @""");
|
||||
str = str.Replace("\'", @"'");
|
||||
|
||||
if (bNbsp) str = str.Replace(" ", @" "); // Before <br />
|
||||
|
||||
str = NormalizeNewLines(str, false);
|
||||
str = str.Replace("\n", @"<br />\n" );
|
||||
str = str.Replace("\n", @"<br />" + MessageService.NewLine);
|
||||
|
||||
return str;
|
||||
}
|
||||
@@ -357,46 +408,6 @@ namespace KeePassLib.Utility
|
||||
return str;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Split up a command line into application and argument.
|
||||
/// </summary>
|
||||
/// <param name="strCmdLine">Command line to split.</param>
|
||||
/// <param name="strApp">Application path.</param>
|
||||
/// <param name="strArgs">Arguments.</param>
|
||||
public static void SplitCommandLine(string strCmdLine, out string strApp, out string strArgs)
|
||||
{
|
||||
Debug.Assert(strCmdLine != null); if(strCmdLine == null) throw new ArgumentNullException("strCmdLine");
|
||||
|
||||
string str = strCmdLine.Trim();
|
||||
|
||||
strApp = null; strArgs = null;
|
||||
|
||||
if(str.StartsWith("\""))
|
||||
{
|
||||
int nSecond = str.IndexOf('\"', 1);
|
||||
if(nSecond >= 1)
|
||||
{
|
||||
strApp = str.Substring(1, nSecond - 1).Trim();
|
||||
strArgs = str.Remove(0, nSecond + 1).Trim();
|
||||
}
|
||||
}
|
||||
|
||||
if(strApp == null)
|
||||
{
|
||||
int nSpace = str.IndexOf(' ');
|
||||
|
||||
if(nSpace >= 0)
|
||||
{
|
||||
strApp = str.Substring(0, nSpace);
|
||||
strArgs = str.Remove(0, nSpace).Trim();
|
||||
}
|
||||
else strApp = strCmdLine;
|
||||
}
|
||||
|
||||
if(strApp == null) strApp = string.Empty;
|
||||
if(strArgs == null) strArgs = string.Empty;
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// Initialize an RTF document based on given font face and size.
|
||||
// /// </summary>
|
||||
@@ -487,41 +498,41 @@ namespace KeePassLib.Utility
|
||||
public static string FormatException(Exception excp)
|
||||
{
|
||||
string strText = string.Empty;
|
||||
string NewLine = "\n";
|
||||
if(excp.Message != null)
|
||||
strText += excp.Message + NewLine;
|
||||
|
||||
if (!string.IsNullOrEmpty(excp.Message))
|
||||
strText += excp.Message + MessageService.NewLine;
|
||||
#if !KeePassLibSD
|
||||
if(excp.Source != null)
|
||||
strText += excp.Source + NewLine;
|
||||
if (!string.IsNullOrEmpty(excp.Source))
|
||||
strText += excp.Source + MessageService.NewLine;
|
||||
#endif
|
||||
if(excp.StackTrace != null)
|
||||
strText += excp.StackTrace + NewLine;
|
||||
if (!string.IsNullOrEmpty(excp.StackTrace))
|
||||
strText += excp.StackTrace + MessageService.NewLine;
|
||||
#if !KeePassLibSD
|
||||
#if !KeePassUAP
|
||||
if (excp.TargetSite != null)
|
||||
strText += excp.TargetSite.ToString() + NewLine;
|
||||
strText += excp.TargetSite.ToString() + MessageService.NewLine;
|
||||
#endif
|
||||
|
||||
if (excp.Data != null)
|
||||
{
|
||||
strText += NewLine;
|
||||
strText += MessageService.NewLine;
|
||||
foreach (DictionaryEntry de in excp.Data)
|
||||
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
|
||||
NewLine;
|
||||
MessageService.NewLine;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (excp.InnerException != null)
|
||||
{
|
||||
strText += NewLine + "Inner:" + NewLine;
|
||||
if(excp.InnerException.Message != null)
|
||||
strText += excp.InnerException.Message + NewLine;
|
||||
strText += MessageService.NewLine + "Inner:" + MessageService.NewLine;
|
||||
if (!string.IsNullOrEmpty(excp.InnerException.Message))
|
||||
strText += excp.InnerException.Message + MessageService.NewLine;
|
||||
#if !KeePassLibSD
|
||||
if(excp.InnerException.Source != null)
|
||||
strText += excp.InnerException.Source + NewLine;
|
||||
if (!string.IsNullOrEmpty(excp.InnerException.Source))
|
||||
strText += excp.InnerException.Source + MessageService.NewLine;
|
||||
#endif
|
||||
if(excp.InnerException.StackTrace != null)
|
||||
strText += excp.InnerException.StackTrace + NewLine;
|
||||
if (!string.IsNullOrEmpty(excp.InnerException.StackTrace))
|
||||
strText += excp.InnerException.StackTrace + MessageService.NewLine;
|
||||
#if !KeePassLibSD
|
||||
#if !KeePassUAP
|
||||
if (excp.InnerException.TargetSite != null)
|
||||
@@ -530,10 +541,10 @@ namespace KeePassLib.Utility
|
||||
|
||||
if (excp.InnerException.Data != null)
|
||||
{
|
||||
strText += NewLine;
|
||||
strText += MessageService.NewLine;
|
||||
foreach (DictionaryEntry de in excp.InnerException.Data)
|
||||
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
|
||||
NewLine;
|
||||
MessageService.NewLine;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -674,19 +685,29 @@ namespace KeePassLib.Utility
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string CompactString3Dots(string strText, int nMaxChars)
|
||||
public static string CompactString3Dots(string strText, int cchMax)
|
||||
{
|
||||
Debug.Assert(strText != null);
|
||||
if (strText == null) throw new ArgumentNullException("strText");
|
||||
Debug.Assert(nMaxChars >= 0);
|
||||
if(nMaxChars < 0) throw new ArgumentOutOfRangeException("nMaxChars");
|
||||
Debug.Assert(cchMax >= 0);
|
||||
if (cchMax < 0) throw new ArgumentOutOfRangeException("cchMax");
|
||||
|
||||
if(nMaxChars == 0) return string.Empty;
|
||||
if(strText.Length <= nMaxChars) return strText;
|
||||
if (strText.Length <= cchMax) return strText;
|
||||
|
||||
if(nMaxChars <= 3) return strText.Substring(0, nMaxChars);
|
||||
if (cchMax == 0) return string.Empty;
|
||||
if (cchMax <= 3) return new string('.', cchMax);
|
||||
|
||||
return strText.Substring(0, nMaxChars - 3) + "...";
|
||||
return (strText.Substring(0, cchMax - 3) + "...");
|
||||
}
|
||||
|
||||
private static readonly char[] g_vDots = new char[] { '.', '\u2026' };
|
||||
private static readonly char[] g_vDotsWS = new char[] { '.', '\u2026',
|
||||
' ', '\t', '\r', '\n' };
|
||||
internal static string TrimDots(string strText, bool bTrimWhiteSpace)
|
||||
{
|
||||
if (strText == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
return strText.Trim(bTrimWhiteSpace ? g_vDotsWS : g_vDots);
|
||||
}
|
||||
|
||||
public static string GetStringBetween(string strText, int nStartIndex,
|
||||
@@ -719,7 +740,7 @@ namespace KeePassLib.Utility
|
||||
|
||||
/// <summary>
|
||||
/// Removes all characters that are not valid XML characters,
|
||||
/// according to http://www.w3.org/TR/xml/#charsets .
|
||||
/// according to https://www.w3.org/TR/xml/#charsets .
|
||||
/// </summary>
|
||||
/// <param name="strText">Source text.</param>
|
||||
/// <returns>Text containing only valid XML characters.</returns>
|
||||
@@ -915,10 +936,10 @@ namespace KeePassLib.Utility
|
||||
|
||||
for (char ch = 'A'; ch <= 'Z'; ++ch)
|
||||
{
|
||||
string strEnhAcc = @"(&" + ch.ToString() + @")";
|
||||
string strEnhAcc = @"(&" + ch.ToString() + ")";
|
||||
if (str.IndexOf(strEnhAcc) >= 0)
|
||||
{
|
||||
str = str.Replace(@" " + strEnhAcc, string.Empty);
|
||||
str = str.Replace(" " + strEnhAcc, string.Empty);
|
||||
str = str.Replace(strEnhAcc, string.Empty);
|
||||
}
|
||||
}
|
||||
@@ -931,35 +952,24 @@ namespace KeePassLib.Utility
|
||||
public static string AddAccelerator(string strMenuText,
|
||||
List<char> lAvailKeys)
|
||||
{
|
||||
if(strMenuText == null) { Debug.Assert(false); return null; }
|
||||
if (strMenuText == null) { Debug.Assert(false); return string.Empty; }
|
||||
if (lAvailKeys == null) { Debug.Assert(false); return strMenuText; }
|
||||
|
||||
int xa = -1, xs = 0;
|
||||
for (int i = 0; i < strMenuText.Length; ++i)
|
||||
{
|
||||
char ch = strMenuText[i];
|
||||
char ch = char.ToLowerInvariant(strMenuText[i]);
|
||||
|
||||
#if KeePassLibSD
|
||||
char chUpper = char.ToUpper(ch);
|
||||
#else
|
||||
char chUpper = char.ToUpperInvariant(ch);
|
||||
#endif
|
||||
xa = lAvailKeys.IndexOf(chUpper);
|
||||
if(xa >= 0) { xs = i; break; }
|
||||
|
||||
#if KeePassLibSD
|
||||
char chLower = char.ToLower(ch);
|
||||
#else
|
||||
char chLower = char.ToLowerInvariant(ch);
|
||||
#endif
|
||||
xa = lAvailKeys.IndexOf(chLower);
|
||||
if(xa >= 0) { xs = i; break; }
|
||||
for (int j = 0; j < lAvailKeys.Count; ++j)
|
||||
{
|
||||
if (char.ToLowerInvariant(lAvailKeys[j]) == ch)
|
||||
{
|
||||
lAvailKeys.RemoveAt(j);
|
||||
return strMenuText.Insert(i, @"&");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(xa < 0) return strMenuText;
|
||||
|
||||
lAvailKeys.RemoveAt(xa);
|
||||
return strMenuText.Insert(xs, @"&");
|
||||
return strMenuText;
|
||||
}
|
||||
|
||||
public static string EncodeMenuText(string strText)
|
||||
@@ -1021,7 +1031,7 @@ namespace KeePassLib.Utility
|
||||
}
|
||||
|
||||
#if !KeePassLibSD
|
||||
private static readonly char[] m_vPatternPartsSep = new char[] { '*' };
|
||||
private static readonly char[] g_vPatternPartsSep = new char[] { '*' };
|
||||
public static bool SimplePatternMatch(string strPattern, string strText,
|
||||
StringComparison sc)
|
||||
{
|
||||
@@ -1030,23 +1040,18 @@ namespace KeePassLib.Utility
|
||||
|
||||
if (strPattern.IndexOf('*') < 0) return strText.Equals(strPattern, sc);
|
||||
|
||||
string[] vPatternParts = strPattern.Split(m_vPatternPartsSep,
|
||||
string[] vPatternParts = strPattern.Split(g_vPatternPartsSep,
|
||||
StringSplitOptions.RemoveEmptyEntries);
|
||||
if (vPatternParts == null) { Debug.Assert(false); return true; }
|
||||
if (vPatternParts.Length == 0) return true;
|
||||
|
||||
if (strText.Length == 0) return false;
|
||||
|
||||
if(!strPattern.StartsWith(@"*") && !strText.StartsWith(vPatternParts[0], sc))
|
||||
{
|
||||
if ((strPattern[0] != '*') && !strText.StartsWith(vPatternParts[0], sc))
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!strPattern.EndsWith(@"*") && !strText.EndsWith(vPatternParts[
|
||||
vPatternParts.Length - 1], sc))
|
||||
{
|
||||
if ((strPattern[strPattern.Length - 1] != '*') && !strText.EndsWith(
|
||||
vPatternParts[vPatternParts.Length - 1], sc))
|
||||
return false;
|
||||
}
|
||||
|
||||
int iOffset = 0;
|
||||
for (int i = 0; i < vPatternParts.Length; ++i)
|
||||
@@ -1123,32 +1128,58 @@ namespace KeePassLib.Utility
|
||||
return str;
|
||||
}
|
||||
|
||||
private static char[] m_vNewLineChars = null;
|
||||
public static void NormalizeNewLines(ProtectedStringDictionary dict,
|
||||
bool bWindows)
|
||||
{
|
||||
if (dict == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(m_vNewLineChars == null)
|
||||
m_vNewLineChars = new char[]{ '\r', '\n' };
|
||||
|
||||
List<string> vKeys = dict.GetKeys();
|
||||
foreach(string strKey in vKeys)
|
||||
List<string> lKeys = dict.GetKeys();
|
||||
foreach (string strKey in lKeys)
|
||||
{
|
||||
ProtectedString ps = dict.Get(strKey);
|
||||
if (ps == null) { Debug.Assert(false); continue; }
|
||||
|
||||
string strValue = ps.ReadString();
|
||||
if(strValue.IndexOfAny(m_vNewLineChars) < 0) continue;
|
||||
|
||||
char[] v = ps.ReadChars();
|
||||
if (!IsNewLineNormalized(v, bWindows))
|
||||
dict.Set(strKey, new ProtectedString(ps.IsProtected,
|
||||
NormalizeNewLines(strValue, bWindows)));
|
||||
NormalizeNewLines(ps.ReadString(), bWindows)));
|
||||
MemUtil.ZeroArray<char>(v);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsNewLineNormalized(char[] v, bool bWindows)
|
||||
{
|
||||
if (v == null) { Debug.Assert(false); return true; }
|
||||
|
||||
if (bWindows)
|
||||
{
|
||||
int iFreeCr = -2; // Must be < -1 (for test "!= (i - 1)")
|
||||
|
||||
for (int i = 0; i < v.Length; ++i)
|
||||
{
|
||||
char ch = v[i];
|
||||
|
||||
if (ch == '\r')
|
||||
{
|
||||
if (iFreeCr >= 0) return false;
|
||||
iFreeCr = i;
|
||||
}
|
||||
else if (ch == '\n')
|
||||
{
|
||||
if (iFreeCr != (i - 1)) return false;
|
||||
iFreeCr = -2; // Consume \r
|
||||
}
|
||||
}
|
||||
|
||||
return (iFreeCr < 0); // Ensure no \r at end
|
||||
}
|
||||
|
||||
return (Array.IndexOf<char>(v, '\r') < 0);
|
||||
}
|
||||
|
||||
public static string GetNewLineSeq(string str)
|
||||
{
|
||||
if(str == null) { Debug.Assert(false); return "\n"; }
|
||||
if (str == null) { Debug.Assert(false); return MessageService.NewLine; }
|
||||
|
||||
int n = str.Length, nLf = 0, nCr = 0, nCrLf = 0;
|
||||
char chLast = char.MinValue;
|
||||
@@ -1170,7 +1201,7 @@ namespace KeePassLib.Utility
|
||||
nLf -= nCrLf;
|
||||
|
||||
int nMax = Math.Max(nCrLf, Math.Max(nCr, nLf));
|
||||
if(nMax == 0) return "\n";
|
||||
if (nMax == 0) return MessageService.NewLine;
|
||||
|
||||
if (nCrLf == nMax) return "\r\n";
|
||||
return ((nLf == nMax) ? "\n" : "\r");
|
||||
@@ -1301,7 +1332,15 @@ namespace KeePassLib.Utility
|
||||
|
||||
try
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
byte[] pbPlain = StrUtil.Utf8.GetBytes(strPlainText);
|
||||
byte[] pbEnc = CryptoUtil.ProtectData(pbPlain, m_pbOptEnt,
|
||||
DataProtectionScope.CurrentUser);
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
return Convert.ToBase64String(pbEnc, Base64FormattingOptions.None);
|
||||
#else
|
||||
return Convert.ToBase64String(pbEnc);
|
||||
#endif
|
||||
}
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
|
||||
@@ -1315,8 +1354,10 @@ namespace KeePassLib.Utility
|
||||
try
|
||||
{
|
||||
byte[] pbEnc = Convert.FromBase64String(strCipherText);
|
||||
throw new NotSupportedException();
|
||||
byte[] pbPlain = CryptoUtil.UnprotectData(pbEnc, m_pbOptEnt,
|
||||
DataProtectionScope.CurrentUser);
|
||||
|
||||
return StrUtil.Utf8.GetString(pbPlain, 0, pbPlain.Length);
|
||||
}
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
|
||||
@@ -1355,27 +1396,98 @@ namespace KeePassLib.Utility
|
||||
return v;
|
||||
}
|
||||
|
||||
private static readonly char[] m_vTagSep = new char[] { ',', ';', ':' };
|
||||
public static string TagsToString(List<string> vTags, bool bForDisplay)
|
||||
private static readonly char[] g_vTagSep = new char[] { ',', ';' };
|
||||
internal static string NormalizeTag(string strTag)
|
||||
{
|
||||
if(vTags == null) throw new ArgumentNullException("vTags");
|
||||
if (strTag == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
strTag = strTag.Trim();
|
||||
|
||||
for (int i = g_vTagSep.Length - 1; i >= 0; --i)
|
||||
strTag = strTag.Replace(g_vTagSep[i], '.');
|
||||
|
||||
return strTag;
|
||||
}
|
||||
|
||||
internal static void NormalizeTags(List<string> lTags)
|
||||
{
|
||||
if (lTags == null) { Debug.Assert(false); return; }
|
||||
|
||||
bool bRemoveNulls = false;
|
||||
for (int i = lTags.Count - 1; i >= 0; --i)
|
||||
{
|
||||
string str = NormalizeTag(lTags[i]);
|
||||
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
lTags[i] = null;
|
||||
bRemoveNulls = true;
|
||||
}
|
||||
else lTags[i] = str;
|
||||
}
|
||||
|
||||
if (bRemoveNulls)
|
||||
{
|
||||
Predicate<string> f = delegate (string str) { return (str == null); };
|
||||
lTags.RemoveAll(f);
|
||||
}
|
||||
|
||||
if (lTags.Count >= 2)
|
||||
{
|
||||
// Deduplicate
|
||||
Dictionary<string, bool> d = new Dictionary<string, bool>();
|
||||
for (int i = lTags.Count - 1; i >= 0; --i)
|
||||
d[lTags[i]] = true;
|
||||
if (d.Count != lTags.Count)
|
||||
{
|
||||
lTags.Clear();
|
||||
lTags.AddRange(d.Keys);
|
||||
}
|
||||
|
||||
lTags.Sort(StrUtil.CompareNaturally);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void AddTags(List<string> lTags, IEnumerable<string> eNewTags)
|
||||
{
|
||||
if (lTags == null) { Debug.Assert(false); return; }
|
||||
if (eNewTags == null) { Debug.Assert(false); return; }
|
||||
|
||||
lTags.AddRange(eNewTags);
|
||||
NormalizeTags(lTags);
|
||||
}
|
||||
|
||||
public static string TagsToString(List<string> lTags, bool bForDisplay)
|
||||
{
|
||||
if (lTags == null) throw new ArgumentNullException("lTags");
|
||||
|
||||
#if DEBUG
|
||||
// The input should be normalized
|
||||
foreach (string str in lTags) { Debug.Assert(NormalizeTag(str) == str); }
|
||||
List<string> l = new List<string>(lTags);
|
||||
NormalizeTags(l);
|
||||
Debug.Assert(l.Count == lTags.Count);
|
||||
#endif
|
||||
|
||||
int n = lTags.Count;
|
||||
if (n == 0) return string.Empty;
|
||||
if (n == 1) return (lTags[0] ?? string.Empty);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
bool bFirst = true;
|
||||
|
||||
foreach(string strTag in vTags)
|
||||
foreach (string strTag in lTags)
|
||||
{
|
||||
if (string.IsNullOrEmpty(strTag)) { Debug.Assert(false); continue; }
|
||||
Debug.Assert(strTag.IndexOfAny(m_vTagSep) < 0);
|
||||
|
||||
if(!bFirst)
|
||||
if (bFirst) bFirst = false;
|
||||
else
|
||||
{
|
||||
if (bForDisplay) sb.Append(", ");
|
||||
else sb.Append(';');
|
||||
}
|
||||
sb.Append(strTag);
|
||||
|
||||
bFirst = false;
|
||||
sb.Append(strTag);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
@@ -1388,13 +1500,9 @@ namespace KeePassLib.Utility
|
||||
List<string> lTags = new List<string>();
|
||||
if (strTags.Length == 0) return lTags;
|
||||
|
||||
string[] vTags = strTags.Split(m_vTagSep);
|
||||
foreach(string strTag in vTags)
|
||||
{
|
||||
string strFlt = strTag.Trim();
|
||||
if(strFlt.Length > 0) lTags.Add(strFlt);
|
||||
}
|
||||
lTags.AddRange(strTags.Split(g_vTagSep));
|
||||
|
||||
NormalizeTags(lTags);
|
||||
return lTags;
|
||||
}
|
||||
|
||||
@@ -1485,8 +1593,8 @@ namespace KeePassLib.Utility
|
||||
|
||||
string str = strMulti;
|
||||
str = str.Replace("\r\n", " ");
|
||||
str = str.Replace("\r", " ");
|
||||
str = str.Replace("\n", " ");
|
||||
str = str.Replace('\r', ' ');
|
||||
str = str.Replace('\n', ' ');
|
||||
|
||||
return str;
|
||||
}
|
||||
@@ -1506,14 +1614,16 @@ namespace KeePassLib.Utility
|
||||
if (((ch == ' ') || (ch == '\t') || (ch == '\r') ||
|
||||
(ch == '\n')) && !bQuoted)
|
||||
{
|
||||
if(sbTerm.Length > 0) l.Add(sbTerm.ToString());
|
||||
|
||||
if (sbTerm.Length != 0)
|
||||
{
|
||||
l.Add(sbTerm.ToString());
|
||||
sbTerm.Remove(0, sbTerm.Length);
|
||||
}
|
||||
}
|
||||
else if (ch == '\"') bQuoted = !bQuoted;
|
||||
else sbTerm.Append(ch);
|
||||
}
|
||||
if(sbTerm.Length > 0) l.Add(sbTerm.ToString());
|
||||
if (sbTerm.Length != 0) l.Add(sbTerm.ToString());
|
||||
|
||||
return l;
|
||||
}
|
||||
@@ -1529,10 +1639,10 @@ namespace KeePassLib.Utility
|
||||
return IsDataUri(strUri, null);
|
||||
}
|
||||
|
||||
public static bool IsDataUri(string strUri, string strReqMimeType)
|
||||
public static bool IsDataUri(string strUri, string strReqMediaType)
|
||||
{
|
||||
if (strUri == null) { Debug.Assert(false); return false; }
|
||||
// strReqMimeType may be null
|
||||
// strReqMediaType may be null
|
||||
|
||||
const string strPrefix = "data:";
|
||||
if (!strUri.StartsWith(strPrefix, StrUtil.CaseIgnoreCmp))
|
||||
@@ -1541,14 +1651,14 @@ namespace KeePassLib.Utility
|
||||
int iC = strUri.IndexOf(',');
|
||||
if (iC < 0) return false;
|
||||
|
||||
if(!string.IsNullOrEmpty(strReqMimeType))
|
||||
if (!string.IsNullOrEmpty(strReqMediaType))
|
||||
{
|
||||
int iS = strUri.IndexOf(';', 0, iC);
|
||||
int iTerm = ((iS >= 0) ? iS : iC);
|
||||
|
||||
string strMime = strUri.Substring(strPrefix.Length,
|
||||
string strMedia = strUri.Substring(strPrefix.Length,
|
||||
iTerm - strPrefix.Length);
|
||||
if(!strMime.Equals(strReqMimeType, StrUtil.CaseIgnoreCmp))
|
||||
if (!strMedia.Equals(strReqMediaType, StrUtil.CaseIgnoreCmp))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1559,20 +1669,20 @@ namespace KeePassLib.Utility
|
||||
/// Create a data URI (according to RFC 2397).
|
||||
/// </summary>
|
||||
/// <param name="pbData">Data to encode.</param>
|
||||
/// <param name="strMimeType">Optional MIME type. If <c>null</c>,
|
||||
/// <param name="strMediaType">Optional MIME type. If <c>null</c>,
|
||||
/// an appropriate type is used.</param>
|
||||
/// <returns>Data URI.</returns>
|
||||
public static string DataToDataUri(byte[] pbData, string strMimeType)
|
||||
public static string DataToDataUri(byte[] pbData, string strMediaType)
|
||||
{
|
||||
if (pbData == null) throw new ArgumentNullException("pbData");
|
||||
|
||||
if(strMimeType == null) strMimeType = "application/octet-stream";
|
||||
if (strMediaType == null) strMediaType = "application/octet-stream";
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
return ("data:" + strMimeType + ";base64," + Convert.ToBase64String(
|
||||
return ("data:" + strMediaType + ";base64," + Convert.ToBase64String(
|
||||
pbData, Base64FormattingOptions.None));
|
||||
#else
|
||||
return ("data:" + strMimeType + ";base64," + Convert.ToBase64String(
|
||||
return ("data:" + strMediaType + ";base64," + Convert.ToBase64String(
|
||||
pbData));
|
||||
#endif
|
||||
}
|
||||
@@ -1615,6 +1725,54 @@ namespace KeePassLib.Utility
|
||||
return pb;
|
||||
}
|
||||
|
||||
// https://www.iana.org/assignments/media-types/media-types.xhtml
|
||||
private static readonly string[] g_vMediaTypePfx = new string[] {
|
||||
"application/", "audio/", "example/", "font/", "image/",
|
||||
"message/", "model/", "multipart/", "text/", "video/"
|
||||
};
|
||||
internal static bool IsMediaType(string str)
|
||||
{
|
||||
if (str == null) { Debug.Assert(false); return false; }
|
||||
if (str.Length == 0) return false;
|
||||
|
||||
foreach (string strPfx in g_vMediaTypePfx)
|
||||
{
|
||||
if (str.StartsWith(strPfx, StrUtil.CaseIgnoreCmp))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static string GetCustomMediaType(string strFormat)
|
||||
{
|
||||
if (strFormat == null)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
if (IsMediaType(strFormat)) return strFormat;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < strFormat.Length; ++i)
|
||||
{
|
||||
char ch = strFormat[i];
|
||||
|
||||
if (((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z')) ||
|
||||
((ch >= '0') && (ch <= '9')))
|
||||
sb.Append(ch);
|
||||
else if ((sb.Length != 0) && ((ch == '-') || (ch == '_')))
|
||||
sb.Append(ch);
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
if (sb.Length == 0) return "application/octet-stream";
|
||||
|
||||
return ("application/vnd." + PwDefs.ShortProductName +
|
||||
"." + sb.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove placeholders from a string (wrapped in '{' and '}').
|
||||
/// This doesn't remove environment variables (wrapped in '%').
|
||||
@@ -1735,6 +1893,50 @@ namespace KeePassLib.Utility
|
||||
return str.Replace('\0', ' ');
|
||||
}
|
||||
|
||||
// https://sourceforge.net/p/keepass/discussion/329220/thread/f98dece5/
|
||||
internal static string EnsureLtrPath(string strPath)
|
||||
{
|
||||
if (strPath == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
string str = strPath;
|
||||
|
||||
// U+200E = left-to-right mark
|
||||
str = str.Replace("\\", "\\\u200E");
|
||||
str = str.Replace("/", "/\u200E");
|
||||
str = str.Replace("\u200E\u200E", "\u200E"); // Remove duplicates
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
internal static bool IsValid(string str)
|
||||
{
|
||||
if (str == null) { Debug.Assert(false); return false; }
|
||||
|
||||
int cc = str.Length;
|
||||
for (int i = 0; i < cc; ++i)
|
||||
{
|
||||
char ch = str[i];
|
||||
if (ch == '\0') return false;
|
||||
|
||||
if (char.IsLowSurrogate(ch)) return false;
|
||||
if (char.IsHighSurrogate(ch))
|
||||
{
|
||||
if (++i >= cc) return false; // High surrogate at end
|
||||
if (!char.IsLowSurrogate(str[i])) return false;
|
||||
|
||||
UnicodeCategory uc2 = char.GetUnicodeCategory(str, i - 1);
|
||||
if (uc2 == UnicodeCategory.OtherNotAssigned) return false;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
UnicodeCategory uc = char.GetUnicodeCategory(ch);
|
||||
if (uc == UnicodeCategory.OtherNotAssigned) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static string RemoveWhiteSpace(string str)
|
||||
{
|
||||
if (str == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
@@ -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:
|
||||
@@ -65,7 +91,7 @@ namespace KeePassLib.Utility
|
||||
Debug.Assert(strFile != null);
|
||||
if (strFile == null) throw new ArgumentNullException("strFile");
|
||||
|
||||
int nLastSep = strFile.LastIndexOfAny(m_vDirSeps);
|
||||
int nLastSep = strFile.LastIndexOfAny(UrlUtil.DirSepChars);
|
||||
if (nLastSep < 0) return string.Empty; // No directory
|
||||
|
||||
if (bEnsureValidDirSpec && (nLastSep == 2) && (strFile[1] == ':') &&
|
||||
@@ -89,7 +115,7 @@ namespace KeePassLib.Utility
|
||||
{
|
||||
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;
|
||||
@@ -106,7 +132,7 @@ namespace KeePassLib.Utility
|
||||
{
|
||||
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;
|
||||
@@ -123,7 +149,7 @@ namespace KeePassLib.Utility
|
||||
{
|
||||
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;
|
||||
@@ -148,11 +174,8 @@ namespace KeePassLib.Utility
|
||||
if (nLength <= 0) return string.Empty;
|
||||
|
||||
char chLast = strPath[nLength - 1];
|
||||
|
||||
for(int i = 0; i < m_vDirSeps.Length; ++i)
|
||||
{
|
||||
if(chLast == m_vDirSeps[i]) return strPath;
|
||||
}
|
||||
if (Array.IndexOf<char>(UrlUtil.DirSepChars, chLast) >= 0)
|
||||
return strPath;
|
||||
|
||||
if (bUrl) return (strPath + '/');
|
||||
return (strPath + UrlUtil.LocalDirSepChar);
|
||||
@@ -216,21 +239,35 @@ 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;
|
||||
|
||||
string str = strPath.Trim();
|
||||
if (str.Length <= 1) return str;
|
||||
if (str[0] != '\"') return str;
|
||||
|
||||
int iSecond = str.IndexOf('\"', 1);
|
||||
int iSecond = IndexOfSecondEnclQuote(str);
|
||||
if (iSecond <= 0) return str;
|
||||
|
||||
return str.Substring(1, iSecond - 1);
|
||||
@@ -238,21 +275,30 @@ namespace KeePassLib.Utility
|
||||
|
||||
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");
|
||||
@@ -272,7 +318,7 @@ 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");
|
||||
@@ -315,9 +361,8 @@ namespace KeePassLib.Utility
|
||||
|
||||
#if (!KeePassLibSD && !KeePassUAP)
|
||||
if (NativeLib.IsUnix())
|
||||
#endif
|
||||
{
|
||||
|
||||
#endif
|
||||
bool bBaseUnc = IsUncPath(strBaseFile);
|
||||
bool bTargetUnc = IsUncPath(strTargetFile);
|
||||
if ((!bBaseUnc && bTargetUnc) || (bBaseUnc && !bTargetUnc))
|
||||
@@ -325,8 +370,8 @@ namespace KeePassLib.Utility
|
||||
|
||||
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)) &&
|
||||
@@ -352,8 +397,8 @@ 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();
|
||||
@@ -403,13 +448,14 @@ namespace KeePassLib.Utility
|
||||
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));
|
||||
@@ -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
|
||||
}
|
||||
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);
|
||||
@@ -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;
|
||||
|
||||
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)
|
||||
@@ -525,26 +568,54 @@ namespace KeePassLib.Utility
|
||||
|
||||
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.
|
||||
@@ -659,13 +730,11 @@ namespace KeePassLib.Utility
|
||||
foreach (string strPathRaw in v)
|
||||
{
|
||||
if (strPathRaw == null) { Debug.Assert(false); continue; }
|
||||
string strPath = strPathRaw.Trim(m_vPathTrimCharsWs);
|
||||
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;
|
||||
|
||||
if (strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
|
||||
l.Add(strPathRaw);
|
||||
}
|
||||
}
|
||||
@@ -698,13 +767,11 @@ namespace KeePassLib.Utility
|
||||
if (fi == null) { Debug.Assert(false); continue; }
|
||||
string strPathRaw = fi.FullName;
|
||||
if (strPathRaw == null) { Debug.Assert(false); continue; }
|
||||
string strPath = strPathRaw.Trim(m_vPathTrimCharsWs);
|
||||
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;
|
||||
|
||||
if (strPath.EndsWith(strExt, StrUtil.CaseIgnoreCmp))
|
||||
l.Add(fi);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||