Compare commits
819 Commits
1.06f-none
...
1.07-r1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0ce77a2729 | ||
![]() |
b0f61038e3 | ||
![]() |
d0749324c9 | ||
![]() |
a43f72d18a | ||
![]() |
3d691088be | ||
![]() |
bca3becadb | ||
![]() |
d937fbe7ad | ||
![]() |
c13e00871d | ||
![]() |
b5b716da4c | ||
![]() |
45d2a8e0b2 | ||
![]() |
d47c948699 | ||
![]() |
fdf36babba | ||
![]() |
d47411264e | ||
![]() |
b9ebad7942 | ||
![]() |
2878cc1cb5 | ||
![]() |
9277d76f37 | ||
![]() |
b7b1301334 | ||
![]() |
954dd73ffa | ||
![]() |
7aeab98c5b | ||
![]() |
51123d60d3 | ||
![]() |
395d4b5a97 | ||
![]() |
654f025f64 | ||
![]() |
8a9ccea5d3 | ||
![]() |
92e00075c6 | ||
![]() |
624e78e734 | ||
![]() |
76bb8822fa | ||
![]() |
e3cba097fa | ||
![]() |
c812fca8c1 | ||
![]() |
f86f4ee18c | ||
![]() |
dafed9786e | ||
![]() |
cd3a119053 | ||
![]() |
f143a171aa | ||
![]() |
ee93d7df82 | ||
![]() |
61a3bcc53d | ||
![]() |
17a46eb92c | ||
![]() |
14b121a8ba | ||
![]() |
41b57417e5 | ||
![]() |
bb492c3043 | ||
![]() |
b683dca67c | ||
![]() |
b6e6024bda | ||
![]() |
2b3a95e21d | ||
![]() |
de9c27f1f5 | ||
![]() |
85c9cb26b1 | ||
![]() |
8ffa27e93d | ||
![]() |
ea743fc2c5 | ||
![]() |
7fab9aab00 | ||
![]() |
444e224d41 | ||
![]() |
9fde19f1ce | ||
![]() |
4e085b5dca | ||
![]() |
06ab58cc07 | ||
![]() |
9dae1746ec | ||
![]() |
db87191f1f | ||
![]() |
76abe79b6d | ||
![]() |
970bc29e01 | ||
![]() |
79a3262659 | ||
![]() |
3add6d64ef | ||
![]() |
43168b18d1 | ||
![]() |
6970b42456 | ||
![]() |
a545b96601 | ||
![]() |
59b66409e8 | ||
![]() |
25c50bbcaf | ||
![]() |
a9a5df00d2 | ||
![]() |
5f4d9c29dc | ||
![]() |
31e158e39b | ||
![]() |
25af5844be | ||
![]() |
f79bb49af1 | ||
![]() |
2756bae656 | ||
![]() |
b574b55f84 | ||
![]() |
76ef1c4033 | ||
![]() |
409deaee9d | ||
![]() |
8396e18283 | ||
![]() |
5e952e06ff | ||
![]() |
7b6dc3d773 | ||
![]() |
1759b67921 | ||
![]() |
1307b0e5d1 | ||
![]() |
c1af6994bf | ||
![]() |
d09c15e5b8 | ||
![]() |
ea98b170d9 | ||
![]() |
c67f4e45c7 | ||
![]() |
c21a5afe94 | ||
![]() |
49de16f0f3 | ||
![]() |
6d60afadf4 | ||
![]() |
c6751af075 | ||
![]() |
d41aa389a7 | ||
![]() |
f95986fd7a | ||
![]() |
ac522bce43 | ||
![]() |
6e0729c3b3 | ||
![]() |
9ce674da2b | ||
![]() |
da8a8e7e77 | ||
![]() |
566a1088bd | ||
![]() |
11f1f07653 | ||
![]() |
45194bb26f | ||
![]() |
1f6440189f | ||
![]() |
60e98d87d8 | ||
![]() |
f560124088 | ||
![]() |
7521d0b787 | ||
![]() |
c45e52f571 | ||
![]() |
275920e5be | ||
![]() |
7045c6ee8d | ||
![]() |
7a7b65e8c6 | ||
![]() |
261526b4d1 | ||
![]() |
bc74c91a2c | ||
![]() |
ab47543df0 | ||
![]() |
3a601468a9 | ||
![]() |
f2002e2b90 | ||
![]() |
29031167d6 | ||
![]() |
7305ac47c3 | ||
![]() |
2aec6c9e4b | ||
![]() |
5a2cdf3cda | ||
![]() |
a943df63fc | ||
![]() |
ca771abebe | ||
![]() |
010db6148d | ||
![]() |
8f065f480c | ||
![]() |
f163f9382f | ||
![]() |
e752244b5f | ||
![]() |
4bbb958a05 | ||
![]() |
d1ddfed8dd | ||
![]() |
bdb39f203a | ||
![]() |
1bcb6aabe7 | ||
![]() |
90b09b0f7e | ||
![]() |
4e6e0a0e73 | ||
![]() |
ad4c764adb | ||
![]() |
2265432ec4 | ||
![]() |
bb45e60a3f | ||
![]() |
b1ff92d63e | ||
![]() |
016864edfc | ||
![]() |
dc5133e676 | ||
![]() |
1fbd10c918 | ||
![]() |
4cd219ab32 | ||
![]() |
871e84fec9 | ||
![]() |
08ce132587 | ||
![]() |
4c58d8df72 | ||
![]() |
bf554d2417 | ||
![]() |
631cd8e35f | ||
![]() |
f46c4d8054 | ||
![]() |
0010713b78 | ||
![]() |
f5bfd4f8f2 | ||
![]() |
6d04d2f579 | ||
![]() |
0d3cef1626 | ||
![]() |
4d251becb0 | ||
![]() |
372d4c4bf3 | ||
![]() |
2dbda89973 | ||
![]() |
2f55bbddd8 | ||
![]() |
928773b183 | ||
![]() |
bc9803cbc4 | ||
![]() |
fca8f5efbc | ||
![]() |
f09fe4f7e2 | ||
![]() |
de3bc0ff6c | ||
![]() |
c4a1b6a82e | ||
![]() |
bd08dd54e2 | ||
![]() |
b1f93fdb75 | ||
![]() |
51599b3f1a | ||
![]() |
1b753877a9 | ||
![]() |
d33ffa9d22 | ||
![]() |
df710e523a | ||
![]() |
7d218e0706 | ||
![]() |
88127a4858 | ||
![]() |
63f2a6f902 | ||
![]() |
d8286eb639 | ||
![]() |
668fa46a98 | ||
![]() |
e16a671a26 | ||
![]() |
7f89ef358c | ||
![]() |
e0df319279 | ||
![]() |
61c7a260b4 | ||
![]() |
f26fce63ad | ||
![]() |
74c3c96b43 | ||
![]() |
765b020152 | ||
![]() |
59c05235f6 | ||
![]() |
92a4131c88 | ||
![]() |
540e14bbe7 | ||
![]() |
0ebd355edd | ||
![]() |
68d4dfed04 | ||
![]() |
37a482d2e4 | ||
![]() |
b79cc65728 | ||
![]() |
78977c7e31 | ||
![]() |
1957fc5345 | ||
![]() |
49cefff75d | ||
![]() |
2ebb145b7b | ||
![]() |
0a6f1394e4 | ||
![]() |
1310913281 | ||
![]() |
c245c5ade3 | ||
![]() |
9e6f45a59b | ||
![]() |
f6a78c8890 | ||
![]() |
76717fa3cb | ||
![]() |
a19230393d | ||
![]() |
0b89969a33 | ||
![]() |
bbe97230d2 | ||
![]() |
ee0161737c | ||
![]() |
e7e0b91703 | ||
![]() |
dbcdf71bbe | ||
![]() |
779615c09a | ||
![]() |
1c3a1ce53c | ||
![]() |
981c524e3f | ||
![]() |
44a7d68f31 | ||
![]() |
ae858e3dc4 | ||
![]() |
22fd23a31d | ||
![]() |
2babe87d6d | ||
![]() |
95a8b3fe34 | ||
![]() |
32c526a26a | ||
![]() |
b2adfad2ee | ||
![]() |
ee5a8534a7 | ||
![]() |
80ba3b969d | ||
![]() |
83bd6911c8 | ||
![]() |
6c66f6199c | ||
![]() |
c11731541c | ||
![]() |
609593ebd0 | ||
![]() |
85b867a5ca | ||
![]() |
a3690051eb | ||
![]() |
ecb44a2611 | ||
![]() |
7898ea9ba7 | ||
![]() |
90a2029f3d | ||
![]() |
1d9a9ea658 | ||
![]() |
e88f80eda3 | ||
![]() |
025e2f415d | ||
![]() |
e113ca79de | ||
![]() |
f988c44440 | ||
![]() |
37cda26e27 | ||
![]() |
65c2da9afa | ||
![]() |
434b7b756b | ||
![]() |
9dfcab6c1c | ||
![]() |
9f39f4e377 | ||
![]() |
58da4284ac | ||
![]() |
55787ff6cf | ||
![]() |
3b93610e43 | ||
![]() |
40f3066ee0 | ||
![]() |
2108aac41c | ||
![]() |
68f8300395 | ||
![]() |
325a61912e | ||
![]() |
7363c88fce | ||
![]() |
0b0f95c65c | ||
![]() |
354675f1e5 | ||
![]() |
7a769f283b | ||
![]() |
3dc67d5978 | ||
![]() |
d3a0d71857 | ||
![]() |
72f7534a5f | ||
![]() |
27a733cbed | ||
![]() |
92a0ce98bb | ||
![]() |
84c9632508 | ||
![]() |
22c2b406db | ||
![]() |
bc374a1317 | ||
![]() |
0bf82a999a | ||
![]() |
453a8459ac | ||
![]() |
53f6ddad22 | ||
![]() |
14bd128931 | ||
![]() |
b0b8daa03d | ||
![]() |
940cd70f64 | ||
![]() |
d8cdef0622 | ||
![]() |
44a7cfe32e | ||
![]() |
8584352e21 | ||
![]() |
1c39d027e0 | ||
![]() |
ce82e2cdcf | ||
![]() |
a8492f75cd | ||
![]() |
03afb38aa3 | ||
![]() |
acc2acce2c | ||
![]() |
3a612864f8 | ||
![]() |
7e1789a3f4 | ||
![]() |
69dea794e1 | ||
![]() |
4e024bd8b1 | ||
![]() |
6078e511aa | ||
![]() |
58574cd824 | ||
![]() |
635fa99cf2 | ||
![]() |
074d71cef7 | ||
![]() |
027075b1b2 | ||
![]() |
898ad42f96 | ||
![]() |
c87ffeff71 | ||
![]() |
40f0744adc | ||
![]() |
eba555f260 | ||
![]() |
e7db208a40 | ||
![]() |
69399bb328 | ||
![]() |
749e928d66 | ||
![]() |
a3b8690609 | ||
![]() |
340e6794ca | ||
![]() |
7a23152cee | ||
![]() |
d00f71884a | ||
![]() |
587014159d | ||
![]() |
e2cb3691e4 | ||
![]() |
a40be3b658 | ||
![]() |
598d7c699a | ||
![]() |
23c9b9f2d9 | ||
![]() |
22aeff708a | ||
![]() |
e9b7b935ee | ||
![]() |
eb0d9ec482 | ||
![]() |
cfd0d72100 | ||
![]() |
f615dc265a | ||
![]() |
53c4971c4e | ||
![]() |
3a98fa9f53 | ||
![]() |
6ed5386c12 | ||
![]() |
a998a7f92b | ||
![]() |
3218989b13 | ||
![]() |
3aff954063 | ||
![]() |
11e824c133 | ||
![]() |
a26662a813 | ||
![]() |
007838197b | ||
![]() |
0ad82d5b91 | ||
![]() |
52e47a62d2 | ||
![]() |
cbaaf3ee54 | ||
![]() |
26f26ee35d | ||
![]() |
0102ac77e3 | ||
![]() |
d24101bee2 | ||
![]() |
36cced5ce7 | ||
![]() |
87eb537ebe | ||
![]() |
86fdc17d2a | ||
![]() |
bd8763c62f | ||
![]() |
bc917d41ad | ||
![]() |
68b60b427a | ||
![]() |
c9e64c53ec | ||
![]() |
6ba6c6bac7 | ||
![]() |
f2967e1d8f | ||
![]() |
60783ca88e | ||
![]() |
8d7c1371df | ||
![]() |
090bfe9091 | ||
![]() |
3ad2ba9f33 | ||
![]() |
1036def599 | ||
![]() |
2acad69940 | ||
![]() |
1f18011a5b | ||
![]() |
3da63b378c | ||
![]() |
0852aa7429 | ||
![]() |
8a863ca636 | ||
![]() |
83db5c205d | ||
![]() |
760e73959c | ||
![]() |
e516e06a53 | ||
![]() |
a954461be3 | ||
![]() |
5998369af6 | ||
![]() |
f41a2604c4 | ||
![]() |
6fc578d286 | ||
![]() |
f2604a5fec | ||
![]() |
642026156a | ||
![]() |
708c511a98 | ||
![]() |
2c75b5886f | ||
![]() |
0ef7f5a955 | ||
![]() |
28856c01cd | ||
![]() |
a47bd03e94 | ||
![]() |
c5551ddeb5 | ||
![]() |
020f274ef3 | ||
![]() |
e2f8f04ace | ||
![]() |
6d61d885c1 | ||
![]() |
e98cd16032 | ||
![]() |
4ec8bdda2f | ||
![]() |
16df1624f8 | ||
![]() |
cb978e012b | ||
![]() |
b7ea02696b | ||
![]() |
49cb8874ca | ||
![]() |
1384256d87 | ||
![]() |
705b2fcd9a | ||
![]() |
906ca77973 | ||
![]() |
7dc5c734d0 | ||
![]() |
a3c1e311f7 | ||
![]() |
4d246a250d | ||
![]() |
8d2c41f558 | ||
![]() |
3b06c4c14a | ||
![]() |
f98dcb813d | ||
![]() |
17cf31e8cf | ||
![]() |
6771c378bf | ||
![]() |
013aa4d071 | ||
![]() |
b887e16628 | ||
![]() |
7774ed65bd | ||
![]() |
c85f996aa3 | ||
![]() |
64d9cc00d1 | ||
![]() |
a0efbecee4 | ||
![]() |
cceb4c6677 | ||
![]() |
66e448a648 | ||
![]() |
eb134fd41a | ||
![]() |
857d6f9008 | ||
![]() |
32396c4534 | ||
![]() |
2df46b6647 | ||
![]() |
9f6ad37547 | ||
![]() |
65955843e5 | ||
![]() |
9e9b5fb9dd | ||
![]() |
f96fc67bc2 | ||
![]() |
07ebd0b4ca | ||
![]() |
64e955c512 | ||
![]() |
161753b195 | ||
![]() |
f92adb847a | ||
![]() |
98f780c277 | ||
![]() |
3c512e74db | ||
![]() |
9b752dd28f | ||
![]() |
3cf2719c29 | ||
![]() |
be420c0394 | ||
![]() |
1b1d995b9d | ||
![]() |
6410f41fa2 | ||
![]() |
f90a45b1e6 | ||
![]() |
9ca5569122 | ||
![]() |
a6b4a35973 | ||
![]() |
06cb6bac3e | ||
![]() |
5241aeea9f | ||
![]() |
6b69ad20ad | ||
![]() |
a79d2ec323 | ||
![]() |
209a0fe2da | ||
![]() |
277f439f70 | ||
![]() |
d9f29fdcaa | ||
![]() |
a9141d3fe2 | ||
![]() |
4ce55e88fa | ||
![]() |
c930d388dc | ||
![]() |
12c7911cf4 | ||
![]() |
06377ff497 | ||
![]() |
7a1938e515 | ||
![]() |
445166d220 | ||
![]() |
3963501d0c | ||
![]() |
acb51d7859 | ||
![]() |
4dbfb76ced | ||
![]() |
4188464652 | ||
![]() |
fb552a8d62 | ||
![]() |
c998e4e34f | ||
![]() |
ab46015a5a | ||
![]() |
dd10740bf2 | ||
![]() |
44f26685e4 | ||
![]() |
3caa56ae8c | ||
![]() |
1ca4fbb078 | ||
![]() |
bdf5270fa0 | ||
![]() |
e3e865b108 | ||
![]() |
33f3736798 | ||
![]() |
a86fbcee44 | ||
![]() |
cdbf8a2811 | ||
![]() |
da4fae4e04 | ||
![]() |
ddeefc4ab6 | ||
![]() |
33702064a5 | ||
![]() |
b72995c8bf | ||
![]() |
868eb1c4fb | ||
![]() |
d93140efc6 | ||
![]() |
0f59d46ede | ||
![]() |
812b5f22f9 | ||
![]() |
d1b5feae0d | ||
![]() |
0861fa9e03 | ||
![]() |
c3fe9f513d | ||
![]() |
b513669f4d | ||
![]() |
1ad74fdacc | ||
![]() |
9a24ede989 | ||
![]() |
7ea8dfa8ce | ||
![]() |
670068eeba | ||
![]() |
70310f1970 | ||
![]() |
4463c148a5 | ||
![]() |
c982b45134 | ||
![]() |
d209a2c7f8 | ||
![]() |
75c73193a2 | ||
![]() |
fc99f6774d | ||
![]() |
d40fab86c9 | ||
![]() |
5c0520e093 | ||
![]() |
1bb9ead202 | ||
![]() |
3afd8915f0 | ||
![]() |
ba3f2d2823 | ||
![]() |
24989d80ac | ||
![]() |
d2c16158cc | ||
![]() |
8514fd6266 | ||
![]() |
30d1534c01 | ||
![]() |
4f0800d4fc | ||
![]() |
41fa396318 | ||
![]() |
d94af84002 | ||
![]() |
63b92cc35a | ||
![]() |
3238d9fbc5 | ||
![]() |
780d4c95fe | ||
![]() |
c3b27c75bf | ||
![]() |
23c7031672 | ||
![]() |
9e98d09a93 | ||
![]() |
06e64f7347 | ||
![]() |
92594db78d | ||
![]() |
c574f2d6bd | ||
![]() |
ee156cbfd6 | ||
![]() |
73d12daf04 | ||
![]() |
e4179a934b | ||
![]() |
afd309d9a8 | ||
![]() |
1a24b00c5e | ||
![]() |
445e4d8b31 | ||
![]() |
224ca98d0e | ||
![]() |
13419c9fb7 | ||
![]() |
00646bc84b | ||
![]() |
cb4b686335 | ||
![]() |
5e900b62c8 | ||
![]() |
74c94d37e5 | ||
![]() |
588814d1a6 | ||
![]() |
572b858f81 | ||
![]() |
1a9fc48490 | ||
![]() |
2dd131e6c3 | ||
![]() |
d1ad2a681f | ||
![]() |
e2e7666c4f | ||
![]() |
665639fdca | ||
![]() |
8bb930c1fc | ||
![]() |
b3b9d8d105 | ||
![]() |
82c409252b | ||
![]() |
14c63a03c4 | ||
![]() |
0c7ad8b16c | ||
![]() |
1bf2d8219a | ||
![]() |
a72264ef36 | ||
![]() |
1acc399568 | ||
![]() |
160960437c | ||
![]() |
61992adcca | ||
![]() |
10e0ae8200 | ||
![]() |
eb58b8576e | ||
![]() |
9b4d2d6ed7 | ||
![]() |
6417e9dec5 | ||
![]() |
f5ac217f13 | ||
![]() |
a3d3b14dd1 | ||
![]() |
8fccce8684 | ||
![]() |
4a847cd74a | ||
![]() |
f4c1db39ad | ||
![]() |
d9ff177427 | ||
![]() |
95e3566c5f | ||
![]() |
76e02ff514 | ||
![]() |
8aca9a2d82 | ||
![]() |
3abab2072a | ||
![]() |
ca9e479dc5 | ||
![]() |
4d230a7b3e | ||
![]() |
974cc59e6c | ||
![]() |
9f09b144f0 | ||
![]() |
9d4dad5fee | ||
![]() |
46ffa93d93 | ||
![]() |
8b709bc070 | ||
![]() |
c1284c760a | ||
![]() |
6529a94d3a | ||
![]() |
f5e91a0524 | ||
![]() |
f63bd3016b | ||
![]() |
09cd590796 | ||
![]() |
da728c4039 | ||
![]() |
14b7b8cec1 | ||
![]() |
3695c04933 | ||
![]() |
f0f3bb6ede | ||
![]() |
7824326c5b | ||
![]() |
a65d263ae2 | ||
![]() |
f3e628be6b | ||
![]() |
62e8dc7767 | ||
![]() |
8e8cfa704a | ||
![]() |
8b2eff0194 | ||
![]() |
d4d063b97a | ||
![]() |
d5ec33e1bd | ||
![]() |
0726f22ec7 | ||
![]() |
531ab93388 | ||
![]() |
a7ad00d402 | ||
![]() |
1f18848b4e | ||
![]() |
38179d9ce3 | ||
![]() |
b86c493cf8 | ||
![]() |
0cc2df85da | ||
![]() |
3157bac5f6 | ||
![]() |
5cb1709f5d | ||
![]() |
e6834b13a5 | ||
![]() |
8a17daa002 | ||
![]() |
1b09189195 | ||
![]() |
7f21fa2c40 | ||
![]() |
e0e4fc85f7 | ||
![]() |
b3e57db9f8 | ||
![]() |
f8d93a5de7 | ||
![]() |
c5423ac650 | ||
![]() |
6e93d17d14 | ||
![]() |
127bc1be5c | ||
![]() |
8136b09992 | ||
![]() |
2ffac3268e | ||
![]() |
c7056e9d40 | ||
![]() |
3fb8b4e7ff | ||
![]() |
c728a7802d | ||
![]() |
e7d3049456 | ||
![]() |
316675c77e | ||
![]() |
c836d3de71 | ||
![]() |
230429135a | ||
![]() |
0be8092382 | ||
![]() |
eb2019e568 | ||
![]() |
bffbb17271 | ||
![]() |
16b997096d | ||
![]() |
3b23f2a4ed | ||
![]() |
87f86fd2e8 | ||
![]() |
396509579a | ||
![]() |
91175f19c4 | ||
![]() |
7c22ca0f91 | ||
![]() |
5eb416d8d9 | ||
![]() |
12e374bd2f | ||
![]() |
9b50f0e77c | ||
![]() |
6aa89287b1 | ||
![]() |
4b9ebdc87c | ||
![]() |
336b45667e | ||
![]() |
3c33a1126b | ||
![]() |
836cdfb16c | ||
![]() |
8512bae997 | ||
![]() |
6fbe8c5dee | ||
![]() |
2132ea416b | ||
![]() |
24081b9223 | ||
![]() |
220cc98559 | ||
![]() |
ef8b9b0685 | ||
![]() |
e8dd47369c | ||
![]() |
537eaddecd | ||
![]() |
183c171da6 | ||
![]() |
58c6114840 | ||
![]() |
df5162ce56 | ||
![]() |
ae61fe892d | ||
![]() |
a78de3b4b9 | ||
![]() |
f26a864e83 | ||
![]() |
0e0b03e6f2 | ||
![]() |
e5595f13eb | ||
![]() |
1b12c16685 | ||
![]() |
048055daac | ||
![]() |
f2590f1fed | ||
![]() |
2eb7b22100 | ||
![]() |
f169750d1a | ||
![]() |
fbb361fa7a | ||
![]() |
902faca13c | ||
![]() |
7e476a5785 | ||
![]() |
c78c5aad02 | ||
![]() |
42c49645da | ||
![]() |
9427fb9ecc | ||
![]() |
61878f293f | ||
![]() |
2bd1727076 | ||
![]() |
f0fbf7bf8b | ||
![]() |
4a1fccc395 | ||
![]() |
54c0497933 | ||
![]() |
49a99e9f15 | ||
![]() |
134fd5b2c2 | ||
![]() |
696f9b3550 | ||
![]() |
aeda42cb71 | ||
![]() |
7ba8400c38 | ||
![]() |
02f10f07e8 | ||
![]() |
83cc32afaa | ||
![]() |
5b8382643a | ||
![]() |
003cb8719a | ||
![]() |
c625ebc128 | ||
![]() |
89c0b02327 | ||
![]() |
94bb17a301 | ||
![]() |
4f29b921e5 | ||
![]() |
8d5dbb0f3d | ||
![]() |
db321e45da | ||
![]() |
8e254a341c | ||
![]() |
847a52ab08 | ||
![]() |
94e69e4e84 | ||
![]() |
8834526cac | ||
![]() |
788365687e | ||
![]() |
4c3d328140 | ||
![]() |
2588f74f7c | ||
![]() |
b70a4ef428 | ||
![]() |
026a903251 | ||
![]() |
3557113ed8 | ||
![]() |
acd8c27a13 | ||
![]() |
3c186eb113 | ||
![]() |
841eb34224 | ||
![]() |
ef583ba7ec | ||
![]() |
fa0e06df75 | ||
![]() |
c26ee7271b | ||
![]() |
f37c0a9124 | ||
![]() |
80fbd656a7 | ||
![]() |
3fb5749c86 | ||
![]() |
1692130559 | ||
![]() |
cf77a9eae2 | ||
![]() |
26151af48a | ||
![]() |
c3b858f0fd | ||
![]() |
914e788ad8 | ||
![]() |
074178621c | ||
![]() |
25d1b6b695 | ||
![]() |
f121c73c4b | ||
![]() |
3f79e7677c | ||
![]() |
ca573f27be | ||
![]() |
056a3342bf | ||
![]() |
eaa661cf09 | ||
![]() |
b2d791d6ea | ||
![]() |
f1ea1935c5 | ||
![]() |
d9c101debe | ||
![]() |
e3a720a69d | ||
![]() |
4f3f18a0ad | ||
![]() |
10a3a8324b | ||
![]() |
a2dab72b25 | ||
![]() |
ca0a381d8c | ||
![]() |
a503e85a57 | ||
![]() |
0d668561b4 | ||
![]() |
c8d39a2c15 | ||
![]() |
6146ac90c1 | ||
![]() |
2bb00c948d | ||
![]() |
01eadd986c | ||
![]() |
b482ea4ecc | ||
![]() |
19bb98c857 | ||
![]() |
225afb85e4 | ||
![]() |
2873ffdff7 | ||
![]() |
52ba506138 | ||
![]() |
798f70a706 | ||
![]() |
d731d55a7a | ||
![]() |
1bfcea0227 | ||
![]() |
71a307bfef | ||
![]() |
7a16a8eaff | ||
![]() |
c1c2ccd940 | ||
![]() |
8a993b7dcb | ||
![]() |
977393a9aa | ||
![]() |
4f36de9900 | ||
![]() |
ef658eb4cd | ||
![]() |
6f77577482 | ||
![]() |
bfdb1a8f62 | ||
![]() |
0935d70ae4 | ||
![]() |
24e4e2b960 | ||
![]() |
e608d7463e | ||
![]() |
47453f1471 | ||
![]() |
362780f59a | ||
![]() |
a2a511b3c0 | ||
![]() |
78cde8ab62 | ||
![]() |
46b779c9d5 | ||
![]() |
23129bf95a | ||
![]() |
fba137bb3f | ||
![]() |
e50a73b4bf | ||
![]() |
31afe11d0e | ||
![]() |
5dc1d047ac | ||
![]() |
19e0cf406c | ||
![]() |
0bc1eece0a | ||
![]() |
f2e93e915b | ||
![]() |
8db6a99194 | ||
![]() |
1490ebce03 | ||
![]() |
cd189e01dc | ||
![]() |
15467248ee | ||
![]() |
23008ab5b8 | ||
![]() |
041fbb10a1 | ||
![]() |
9ba0e59df5 | ||
![]() |
0bb9f57919 | ||
![]() |
106da869c1 | ||
![]() |
d34a3352f2 | ||
![]() |
4b5c922f11 | ||
![]() |
aacb415364 | ||
![]() |
bdc8bcfe5c | ||
![]() |
154238edf0 | ||
![]() |
eba789c200 | ||
![]() |
63cb67a03c | ||
![]() |
1c299c6d5d | ||
![]() |
486c5310d7 | ||
![]() |
5ef4a97d5f | ||
![]() |
03adb8f730 | ||
![]() |
abdcd0b3f0 | ||
![]() |
6e225808a9 | ||
![]() |
382c96f587 | ||
![]() |
80cb0119c7 | ||
![]() |
e7bc5072c0 | ||
![]() |
a65057679c | ||
![]() |
025186d22f | ||
![]() |
e17217206f | ||
![]() |
c64ca44457 | ||
![]() |
c6e9a16274 | ||
![]() |
2dc125e0ce | ||
![]() |
9c88ce213b | ||
![]() |
f945380a87 | ||
![]() |
bb9664eafc | ||
![]() |
eaca802ecc | ||
![]() |
2e6ca574b6 | ||
![]() |
026de5dd4e | ||
![]() |
262897fbf6 | ||
![]() |
304a9d90d0 | ||
![]() |
91e1674833 | ||
![]() |
d4c61ce932 | ||
![]() |
7387ebe182 | ||
![]() |
d6f305cfa6 | ||
![]() |
73b9fbb435 | ||
![]() |
51c2f313d4 | ||
![]() |
0a390107a3 | ||
![]() |
077bf997c6 | ||
![]() |
48cb473ecb | ||
![]() |
40dc542302 | ||
![]() |
28bb4894b2 | ||
![]() |
774d17dd25 | ||
![]() |
790637f1bd | ||
![]() |
99fb5d3b8e | ||
![]() |
6676e62579 | ||
![]() |
d735992f5c | ||
![]() |
8a32cfc185 | ||
![]() |
285fafc300 | ||
![]() |
0c90433a34 | ||
![]() |
d6070368b7 | ||
![]() |
b42389500f | ||
![]() |
daca972fc2 | ||
![]() |
8447e59d1f | ||
![]() |
07ff8cd662 | ||
![]() |
dbc734eee5 | ||
![]() |
eaa95fdba4 | ||
![]() |
3ba1169e9c | ||
![]() |
0117b53ea2 | ||
![]() |
a2ec04b641 | ||
![]() |
8fdcef9c42 | ||
![]() |
d311de764d | ||
![]() |
e9b16b2f70 | ||
![]() |
21a81b977b | ||
![]() |
9ce1c2b075 | ||
![]() |
b974cbd14c | ||
![]() |
1d96282fd9 | ||
![]() |
48efda6bd9 | ||
![]() |
aba3e0033f | ||
![]() |
5bf61a63cc | ||
![]() |
48095c0433 | ||
![]() |
3e1599119a | ||
![]() |
f488e8a7ab | ||
![]() |
b5c17bf7bc | ||
![]() |
77e994e9fd | ||
![]() |
bbd3fc4d1d | ||
![]() |
ee63946be2 | ||
![]() |
b1bcfacf35 | ||
![]() |
d4936e8eec | ||
![]() |
dbba098555 | ||
![]() |
b368e93061 | ||
![]() |
10b333ae48 | ||
![]() |
87c0345a6a | ||
![]() |
381510d213 | ||
![]() |
f5f99f5a04 | ||
![]() |
fcf1214f80 | ||
![]() |
7e122529d2 | ||
![]() |
600c3d21dc | ||
![]() |
2a57b94e08 | ||
![]() |
366a37b765 | ||
![]() |
a3806f7a81 | ||
![]() |
cf28e373f7 | ||
![]() |
8d80295e07 | ||
![]() |
1f7ffe6ea0 | ||
![]() |
a740753175 | ||
![]() |
c70f996915 | ||
![]() |
707dfc2a0b | ||
![]() |
77db3362fc | ||
![]() |
c8e0687288 | ||
![]() |
f61a6191ec | ||
![]() |
2648236bb4 | ||
![]() |
382e23f545 | ||
![]() |
2eddbe3669 | ||
![]() |
267d710c13 | ||
![]() |
5e41517178 | ||
![]() |
5c9cf0a655 | ||
![]() |
6301b48b5d | ||
![]() |
5dea97cce2 | ||
![]() |
69b63bcb15 | ||
![]() |
cefad66475 | ||
![]() |
726f4438d6 | ||
![]() |
880e75a91c | ||
![]() |
0f1fd0e367 | ||
![]() |
7a44cf6e26 | ||
![]() |
177c709e0a | ||
![]() |
699eb824a1 | ||
![]() |
6b19e305f3 | ||
![]() |
8b1b4044b3 |
@@ -4,3 +4,7 @@ files:
|
||||
/src/keepass2android/Resources/values-%two_letters_code%/%original_file_name%
|
||||
translate_attributes: '0'
|
||||
content_segmentation: '0'
|
||||
languages_mapping:
|
||||
two_letters_code:
|
||||
zh-CN: zh
|
||||
zh-TW: zh-rTW
|
||||
|
@@ -2,7 +2,7 @@ Keepass2Android's apk is pretty big, e.g. when comparing to Keepassdroid. The ma
|
||||
|
||||
Here's a list of what is contained in the Keepass2Android 0.9.1 application package:
|
||||
|
||||
{{
|
||||
```
|
||||
Mono for Android
|
||||
.net dlls 5.0 MB
|
||||
Runtime 2.5 MB
|
||||
@@ -22,4 +22,4 @@ Java/Mono bindings 0.5 MB
|
||||
rest 0.3 MB
|
||||
|
||||
TOTAL 13 MB
|
||||
}}
|
||||
```
|
||||
|
@@ -1,3 +1,6 @@
|
||||
<h1 align="center"><img src="/src/keepass2android/Resources/mipmap-xxxhdpi/ic_launcher_online.png" align="center" width="100" alt="Keepass2Android Logo">Keepass2Android</h1>
|
||||
|
||||
|
||||
# What is Keepass2Android?
|
||||
Keepass2Android is a password manager app. It allows to store and retrieve passwords and other sensitive information in a file called "database". This database is secured with a so-called master password. The master password typically is a strong password and can be complemented with a second factor for additional security.
|
||||
The password database file can be synchronized across different devices. This works best using one of the built-in cloud storage options, but can also be performed with third-party apps. Keepass2Android is compatible with Keepass 1 and Keepass 2 on Windows and KeepassX on Linux.
|
||||
@@ -15,4 +18,4 @@ Beta-releases can be obtained by opting in to the [Beta testing channel](https:/
|
||||
# How do I learn more?
|
||||
Please see the [documentation](Documentation.md).
|
||||
|
||||
[](https://www.bitrise.io/app/43a23ab54dee9f7e)
|
||||
[](https://www.bitrise.io/app/43a23ab54dee9f7e)
|
||||
|
BIN
graphics/launcher_icon/Logo-green-96.png
Normal file
BIN
graphics/launcher_icon/Logo-green-96.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
64
src/AdalBindings/AdalBindings.csproj
Normal file
64
src/AdalBindings/AdalBindings.csproj
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{0B109C0E-0514-4340-8779-5BD6A0DDE84E}</ProjectGuid>
|
||||
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>AdalBindings</RootNamespace>
|
||||
<AssemblyName>AdalBindings</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
|
||||
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Jars\AboutJars.txt" />
|
||||
<None Include="Additions\AboutAdditions.txt" />
|
||||
<LibraryProjectZip Include="Jars\adal-1.14.0.aar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TransformFile Include="Transforms\Metadata.xml" />
|
||||
<TransformFile Include="Transforms\EnumFields.xml" />
|
||||
<TransformFile Include="Transforms\EnumMethods.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gson-2.3.1.jar" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Bindings.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
48
src/AdalBindings/Additions/AboutAdditions.txt
Normal file
48
src/AdalBindings/Additions/AboutAdditions.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
Additions allow you to add arbitrary C# to the generated classes
|
||||
before they are compiled. This can be helpful for providing convenience
|
||||
methods or adding pure C# classes.
|
||||
|
||||
== Adding Methods to Generated Classes ==
|
||||
|
||||
Let's say the library being bound has a Rectangle class with a constructor
|
||||
that takes an x and y position, and a width and length size. It will look like
|
||||
this:
|
||||
|
||||
public partial class Rectangle
|
||||
{
|
||||
public Rectangle (int x, int y, int width, int height)
|
||||
{
|
||||
// JNI bindings
|
||||
}
|
||||
}
|
||||
|
||||
Imagine we want to add a constructor to this class that takes a Point and
|
||||
Size structure instead of 4 ints. We can add a new file called Rectangle.cs
|
||||
with a partial class containing our new method:
|
||||
|
||||
public partial class Rectangle
|
||||
{
|
||||
public Rectangle (Point location, Size size) :
|
||||
this (location.X, location.Y, size.Width, size.Height)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
At compile time, the additions class will be added to the generated class
|
||||
and the final assembly will a Rectangle class with both constructors.
|
||||
|
||||
|
||||
== Adding C# Classes ==
|
||||
|
||||
Another thing that can be done is adding fully C# managed classes to the
|
||||
generated library. In the above example, let's assume that there isn't a
|
||||
Point class available in Java or our library. The one we create doesn't need
|
||||
to interact with Java, so we'll create it like a normal class in C#.
|
||||
|
||||
By adding a Point.cs file with this class, it will end up in the binding library:
|
||||
|
||||
public class Point
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
}
|
24
src/AdalBindings/Jars/AboutJars.txt
Normal file
24
src/AdalBindings/Jars/AboutJars.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
This directory is for Android .jars.
|
||||
|
||||
There are 2 types of jars that are supported:
|
||||
|
||||
== Input Jar ==
|
||||
|
||||
This is the jar that bindings should be generated for.
|
||||
|
||||
For example, if you were binding the Google Maps library, this would
|
||||
be Google's "maps.jar".
|
||||
|
||||
Set the build action for these jars in the properties page to "InputJar".
|
||||
|
||||
|
||||
== Reference Jars ==
|
||||
|
||||
These are jars that are referenced by the input jar. C# bindings will
|
||||
not be created for these jars. These jars will be used to resolve
|
||||
types used by the input jar.
|
||||
|
||||
NOTE: Do not add "android.jar" as a reference jar. It will be added automatically
|
||||
based on the Target Framework selected.
|
||||
|
||||
Set the build action for these jars in the properties page to "ReferenceJar".
|
BIN
src/AdalBindings/Jars/gson-2.3.1.jar
Normal file
BIN
src/AdalBindings/Jars/gson-2.3.1.jar
Normal file
Binary file not shown.
30
src/AdalBindings/Properties/AssemblyInfo.cs
Normal file
30
src/AdalBindings/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Android.App;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("AdalBindings")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("AdalBindings")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
14
src/AdalBindings/Transforms/EnumFields.xml
Normal file
14
src/AdalBindings/Transforms/EnumFields.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<enum-field-mappings>
|
||||
<!--
|
||||
This example converts the constants Fragment_id, Fragment_name,
|
||||
and Fragment_tag from android.support.v4.app.FragmentActivity.FragmentTag
|
||||
to an enum called Android.Support.V4.App.FragmentTagType with values
|
||||
Id, Name, and Tag.
|
||||
|
||||
<mapping jni-class="android/support/v4/app/FragmentActivity$FragmentTag" clr-enum-type="Android.Support.V4.App.FragmentTagType">
|
||||
<field jni-name="Fragment_name" clr-name="Name" value="0" />
|
||||
<field jni-name="Fragment_id" clr-name="Id" value="1" />
|
||||
<field jni-name="Fragment_tag" clr-name="Tag" value="2" />
|
||||
</mapping>
|
||||
-->
|
||||
</enum-field-mappings>
|
13
src/AdalBindings/Transforms/EnumMethods.xml
Normal file
13
src/AdalBindings/Transforms/EnumMethods.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<enum-method-mappings>
|
||||
<!--
|
||||
This example changes the Java method:
|
||||
android.support.v4.app.Fragment.SavedState.writeToParcel (int flags)
|
||||
to be:
|
||||
android.support.v4.app.Fragment.SavedState.writeToParcel (Android.OS.ParcelableWriteFlags flags)
|
||||
when bound in C#.
|
||||
|
||||
<mapping jni-class="android/support/v4/app/Fragment.SavedState">
|
||||
<method jni-name="writeToParcel" parameter="flags" clr-enum-type="Android.OS.ParcelableWriteFlags" />
|
||||
</mapping>
|
||||
-->
|
||||
</enum-method-mappings>
|
13
src/AdalBindings/Transforms/Metadata.xml
Normal file
13
src/AdalBindings/Transforms/Metadata.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<metadata>
|
||||
<!--
|
||||
This sample removes the class: android.support.v4.content.AsyncTaskLoader.LoadTask:
|
||||
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='AsyncTaskLoader.LoadTask']" />
|
||||
|
||||
This sample removes the method: android.support.v4.content.CursorLoader.loadInBackground:
|
||||
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='CursorLoader']/method[@name='loadInBackground']" />
|
||||
-->
|
||||
<remove-node path="/api/package[@name='com.microsoft.aad.adal']/class[@name='AuthenticationActivity']" />
|
||||
<remove-node path="/api/package[@name='com.microsoft.aad.adal']/class[@name='DateTimeAdapter']" />
|
||||
<remove-node path="/api/package[@name='com.microsoft.aad.adal']" />
|
||||
|
||||
</metadata>
|
@@ -60,7 +60,6 @@
|
||||
</LibraryProjectZip>
|
||||
<None Include="Jars\AboutJars.txt" />
|
||||
<None Include="Additions\AboutAdditions.txt" />
|
||||
<LibraryProjectZip Include="Jars\adal-1.14.0.aar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TransformFile Include="Transforms\Metadata.xml" />
|
||||
@@ -81,6 +80,9 @@
|
||||
<Visible>False</Visible>
|
||||
</XamarinComponentReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PCloudBindings\PCloudBindings.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\okhttp-digest-1.7.jar" />
|
||||
</ItemGroup>
|
||||
|
@@ -11,6 +11,8 @@
|
||||
<remove-node path="/api/package[@name='com.jcraft.jsch.jcraft']" />
|
||||
<remove-node path="/api/package[@name='com.jcraft.jzlib']" />
|
||||
|
||||
<remove-node path="/api/package[@name='com.pcloud.sdk']" />
|
||||
|
||||
<remove-node path="/api/package[@name='com.dropbox.core']" />
|
||||
<remove-node path="/api/package[@name='com.dropbox.core.util']" />
|
||||
<remove-node path="/api/package[@name='com.dropbox.core.http']" />
|
||||
|
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2010
|
||||
VisualStudioVersion = 15.0.27004.2009
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePassLib2Android", "KeePassLib2Android\KeePassLib2Android.csproj", "{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}"
|
||||
EndProject
|
||||
@@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aBusinessLogic", "Kp2aBu
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwofishCipher", "TwofishCipher\TwofishCipher.csproj", "{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaFileStorageBindings", "JavaFileStorageBindings\JavaFileStorageBindings.csproj", "{48574278-4779-4B3A-A9E4-9CF1BC285D0B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidFileChooserBinding", "AndroidFileChooserBinding\AndroidFileChooserBinding.csproj", "{3C0F7FE5-639F-4422-A087-8B26CF862D1B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KP2AKdbLibraryBinding", "KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj", "{70D3844A-D9FA-4A64-B205-A84C6A822196}"
|
||||
@@ -21,8 +23,12 @@ 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}") = "System.Net.FtpClient.Android", "netftpandroid\System.Net.FtpClient\System.Net.FtpClient.Android.csproj", "{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}"
|
||||
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
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -105,8 +111,8 @@ Global
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{A8779D4D-7C49-4C2F-82BD-2CDC448391DA}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
@@ -147,6 +153,30 @@ Global
|
||||
{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|Win32.Build.0 = ReleaseNoNet|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{48574278-4779-4B3A-A9E4-9CF1BC285D0B}.ReleaseNoNet|x64.Build.0 = ReleaseNoNet|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
@@ -159,8 +189,8 @@ Global
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
@@ -177,8 +207,8 @@ Global
|
||||
{70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
|
||||
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
@@ -195,8 +225,8 @@ Global
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{3DA3911E-36DE-465E-8F15-F1991B6437E5}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
@@ -225,6 +255,24 @@ 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
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
|
||||
{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}.ReleaseNoNet|x64.ActiveCfg = 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
|
||||
@@ -243,6 +291,30 @@ Global
|
||||
{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
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU
|
||||
{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU
|
||||
{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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@@ -21,7 +21,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using keepass2android;
|
||||
#if KeePassUAP
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Engines;
|
||||
@@ -144,6 +144,7 @@ namespace KeePassLib.Cryptography.KeyDerivation
|
||||
public static bool TransformKeyManaged(byte[] pbNewKey32, byte[] pbKeySeed32,
|
||||
ulong uNumRounds)
|
||||
{
|
||||
Kp2aLog.Log("Warning: transforming key managed. Expect this to be slow!");
|
||||
#if KeePassUAP
|
||||
KeyParameter kp = new KeyParameter(pbKeySeed32);
|
||||
AesEngine aes = new AesEngine();
|
||||
|
@@ -38,9 +38,11 @@ namespace KeePassLib.Keys
|
||||
get;
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// Clear the key and securely erase all security-critical information.
|
||||
// /// </summary>
|
||||
// void Clear();
|
||||
// /// <summary>
|
||||
// /// Clear the key and securely erase all security-critical information.
|
||||
// /// </summary>
|
||||
// void Clear();
|
||||
|
||||
uint GetMinKdbxVersion();
|
||||
}
|
||||
}
|
||||
|
@@ -45,7 +45,12 @@ namespace KeePassLib.Keys
|
||||
get { return m_pbKey; }
|
||||
}
|
||||
|
||||
public KcpCustomKey(string strName, byte[] pbKeyData, bool bPerformHash)
|
||||
public uint GetMinKdbxVersion()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public KcpCustomKey(string strName, byte[] pbKeyData, bool bPerformHash)
|
||||
{
|
||||
Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName");
|
||||
Debug.Assert(pbKeyData != null); if(pbKeyData == null) throw new ArgumentNullException("pbKeyData");
|
||||
|
@@ -64,7 +64,12 @@ namespace KeePassLib.Keys
|
||||
get { return m_pbKeyData; }
|
||||
}
|
||||
|
||||
public IOConnectionInfo Ioc
|
||||
public uint GetMinKdbxVersion()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public IOConnectionInfo Ioc
|
||||
{
|
||||
get { return m_ioc; }
|
||||
}
|
||||
|
@@ -53,7 +53,12 @@ namespace KeePassLib.Keys
|
||||
get { return m_pbKeyData; }
|
||||
}
|
||||
|
||||
public KcpPassword(byte[] pbPasswordUtf8)
|
||||
public uint GetMinKdbxVersion()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public KcpPassword(byte[] pbPasswordUtf8)
|
||||
{
|
||||
SetKey(pbPasswordUtf8);
|
||||
}
|
||||
|
@@ -60,7 +60,12 @@ namespace KeePassLib.Keys
|
||||
get { return m_pbKeyData; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
public uint GetMinKdbxVersion()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a user account key.
|
||||
/// </summary>
|
||||
public KcpUserAccount()
|
||||
|
@@ -85,8 +85,8 @@ namespace keepass2android
|
||||
{
|
||||
if (!File.Exists(LogFilename))
|
||||
{
|
||||
File.Create(LogFilename);
|
||||
_logToFile = true;
|
||||
File.Create(LogFilename).Dispose();
|
||||
_logToFile = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,8 +100,7 @@ namespace keepass2android
|
||||
int count = 0;
|
||||
while (File.Exists(LogFilename + "." + count))
|
||||
count++;
|
||||
if (count > 0)
|
||||
File.Move(LogFilename, LogFilename + "." + count);
|
||||
File.Move(LogFilename, LogFilename + "." + count);
|
||||
|
||||
}
|
||||
|
||||
|
@@ -302,7 +302,7 @@ namespace KeePassLib.Native
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Kp2aLog.Log(e.Message);
|
||||
Kp2aLog.Log(e.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -485,12 +485,6 @@ namespace KeePassLib
|
||||
set { m_pbHashOfLastIO = value; }
|
||||
}
|
||||
|
||||
public bool UseFileTransactions
|
||||
{
|
||||
get { return m_bUseFileTransactions; }
|
||||
set { m_bUseFileTransactions = value; }
|
||||
}
|
||||
|
||||
public bool UseFileLocks
|
||||
{
|
||||
get { return m_bUseFileLocks; }
|
||||
|
@@ -360,5 +360,12 @@ namespace KeePassLib.Serialization
|
||||
m_ioCredProtMode = IOCredProtMode.None;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSameFileAs(IOConnectionInfo other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
return Path == other.Path && UserName == other.UserName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
@@ -126,8 +127,8 @@ namespace KeePassLib.Serialization
|
||||
/// </summary>
|
||||
private const uint FileVersion32 = 0x00040000;
|
||||
|
||||
internal const uint FileVersion32_4 = 0x00040000; // First of 4.x series
|
||||
internal const uint FileVersion32_3 = 0x00030001; // Old format 3.1
|
||||
public const uint FileVersion32_4 = 0x00040000; // First of 4.x series
|
||||
public const uint FileVersion32_3 = 0x00030001; // Old format 3.1
|
||||
|
||||
private const uint FileVersionCriticalMask = 0xFFFF0000;
|
||||
|
||||
@@ -372,16 +373,19 @@ namespace KeePassLib.Serialization
|
||||
{
|
||||
if(m_uForceVersion != 0) return m_uForceVersion;
|
||||
|
||||
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
|
||||
|
||||
AesKdf kdfAes = new AesKdf();
|
||||
// See also KeePassKdb2x3.Export (KDBX 3.1 export module)
|
||||
uint minVersionForKeys = m_pwDatabase.MasterKey.UserKeys.Select(key => key.GetMinKdbxVersion()).Max();
|
||||
|
||||
AesKdf kdfAes = new AesKdf();
|
||||
if(!kdfAes.Uuid.Equals(m_pwDatabase.KdfParameters.KdfUuid))
|
||||
return FileVersion32;
|
||||
return Math.Max(FileVersion32, minVersionForKeys);
|
||||
|
||||
if(m_pwDatabase.PublicCustomData.Count > 0)
|
||||
return FileVersion32;
|
||||
return Math.Max(FileVersion32, minVersionForKeys);
|
||||
|
||||
bool bCustomData = false;
|
||||
|
||||
|
||||
bool bCustomData = false;
|
||||
GroupHandler gh = delegate(PwGroup pg)
|
||||
{
|
||||
if(pg == null) { Debug.Assert(false); return true; }
|
||||
@@ -396,9 +400,10 @@ namespace KeePassLib.Serialization
|
||||
};
|
||||
gh(m_pwDatabase.RootGroup);
|
||||
m_pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, gh, eh);
|
||||
if(bCustomData) return FileVersion32;
|
||||
if(bCustomData)
|
||||
return Math.Max(FileVersion32, minVersionForKeys);
|
||||
|
||||
return FileVersion32_3; // KDBX 3.1 is sufficient
|
||||
return Math.Max(FileVersion32_3, minVersionForKeys); ; // KDBX 3.1 is sufficient
|
||||
}
|
||||
|
||||
private void ComputeKeys(out byte[] pbCipherKey, int cbCipherKey,
|
||||
|
47
src/Kp2aBusinessLogic/ElementAndDatabaseId.cs
Normal file
47
src/Kp2aBusinessLogic/ElementAndDatabaseId.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using keepass2android.Io;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Interfaces;
|
||||
using KeePassLib.Utility;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
public class ElementAndDatabaseId
|
||||
{
|
||||
private const char Separator = '+';
|
||||
|
||||
public ElementAndDatabaseId(Database db, IStructureItem element)
|
||||
{
|
||||
DatabaseId = db.IocAsHexString();
|
||||
ElementIdString = element.Uuid.ToHexString();
|
||||
}
|
||||
|
||||
public ElementAndDatabaseId(string fullId)
|
||||
{
|
||||
string[] parts = fullId.Split(Separator);
|
||||
if (parts.Length != 2)
|
||||
throw new Exception("Invalid full id " + fullId);
|
||||
DatabaseId = parts[0];
|
||||
ElementIdString = parts[1];
|
||||
}
|
||||
|
||||
public string DatabaseId { get; set; }
|
||||
public string ElementIdString { get; set; }
|
||||
public PwUuid ElementId { get { return new PwUuid(MemUtil.HexStringToByteArray(ElementIdString));} }
|
||||
|
||||
public string FullId
|
||||
{
|
||||
get { return DatabaseId + Separator + ElementIdString; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Android.App;
|
||||
@@ -9,6 +10,7 @@ using KeePassLib;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
using keepass2android.Io;
|
||||
using KeePassLib.Interfaces;
|
||||
#if !NoNet
|
||||
using Keepass2android.Javafilestorage;
|
||||
#endif
|
||||
@@ -33,31 +35,41 @@ namespace keepass2android
|
||||
/// This also contains methods which are UI specific and should be replacable for testing.
|
||||
public interface IKp2aApp : ICertificateValidationHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Locks the currently open database, quicklocking if available (unless false is passed for allowQuickUnlock)
|
||||
/// </summary>
|
||||
void LockDatabase(bool allowQuickUnlock = true);
|
||||
/// <summary>
|
||||
/// Locks all currently open databases, quicklocking if available (unless false is passed for allowQuickUnlock)
|
||||
/// </summary>
|
||||
void Lock(bool allowQuickUnlock);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the specified data as the currently open database, as unlocked.
|
||||
/// </summary>
|
||||
void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey,
|
||||
ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current database
|
||||
/// </summary>
|
||||
Database GetDb();
|
||||
/// <summary>
|
||||
/// Loads the specified data as the currently open database, as unlocked.
|
||||
/// </summary>
|
||||
Database LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey, ProgressDialogStatusLogger statusLogger, IDatabaseFormat databaseFormat, bool makeCurrent);
|
||||
|
||||
/// <summary>
|
||||
/// Tell the app that the file from ioc was opened with keyfile.
|
||||
/// </summary>
|
||||
void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, string displayName = "");
|
||||
|
||||
HashSet<PwGroup> DirtyGroups { get; }
|
||||
|
||||
void MarkAllGroupsAsDirty();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current database
|
||||
/// </summary>
|
||||
Database CurrentDb { get; }
|
||||
|
||||
IEnumerable<Database> OpenDatabases { get; }
|
||||
void CloseDatabase(Database db);
|
||||
|
||||
Database FindDatabaseForElement(IStructureItem element);
|
||||
|
||||
/// <summary>
|
||||
/// Tell the app that the file from ioc was opened with keyfile.
|
||||
/// </summary>
|
||||
void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, bool updateTimestamp, string displayName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new database and returns it
|
||||
/// </summary>
|
||||
Database CreateNewDatabase();
|
||||
Database CreateNewDatabase(bool makeCurrent);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the user-displayable string identified by stringKey
|
||||
@@ -76,7 +88,8 @@ namespace keepass2android
|
||||
EventHandler<DialogClickEventArgs> yesHandler,
|
||||
EventHandler<DialogClickEventArgs> noHandler,
|
||||
EventHandler<DialogClickEventArgs> cancelHandler,
|
||||
Context ctx);
|
||||
Context ctx,
|
||||
string messageSuffix = "");
|
||||
|
||||
/// <summary>
|
||||
/// Asks the user the question "messageKey" with the options Yes/No/Cancel, but the yes/no strings can be selected freely, calls the handler corresponding to the answer.
|
||||
@@ -86,7 +99,8 @@ namespace keepass2android
|
||||
EventHandler<DialogClickEventArgs> yesHandler,
|
||||
EventHandler<DialogClickEventArgs> noHandler,
|
||||
EventHandler<DialogClickEventArgs> cancelHandler,
|
||||
Context ctx);
|
||||
Context ctx,
|
||||
string messageSuffix = "");
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Handler object which can run tasks on the UI thread
|
||||
@@ -111,6 +125,10 @@ namespace keepass2android
|
||||
bool CheckForDuplicateUuids { get; }
|
||||
#if !NoNet
|
||||
ICertificateErrorHandler CertificateErrorHandler { get; }
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
@@ -207,8 +207,7 @@ namespace keepass2android.Io
|
||||
|
||||
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||
{
|
||||
//TODO: required for OTP Aux file retrieval
|
||||
throw new NotImplementedException();
|
||||
return IoUtil.GetParentPath(ioc);
|
||||
}
|
||||
|
||||
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
||||
|
@@ -255,7 +255,8 @@ namespace keepass2android.Io
|
||||
if (ioc.IsLocalFile())
|
||||
{
|
||||
bool requiresPermission = !(ioc.Path.StartsWith(activity.Activity.FilesDir.CanonicalPath)
|
||||
|| ioc.Path.StartsWith(IoUtil.GetInternalDirectory(activity.Activity).CanonicalPath));
|
||||
|| ioc.Path.StartsWith(IoUtil.GetInternalDirectory(activity.Activity).CanonicalPath)
|
||||
|| ioc.Path.StartsWith(IoUtil.GetInternalDirectory(activity.Activity).CanonicalPath));
|
||||
|
||||
var extDirectory = activity.Activity.GetExternalFilesDir(null);
|
||||
if ((extDirectory != null) && (ioc.Path.StartsWith(extDirectory.CanonicalPath)))
|
||||
|
@@ -65,22 +65,28 @@ namespace keepass2android.Io
|
||||
|
||||
protected readonly OfflineSwitchableFileStorage _cachedStorage;
|
||||
private readonly ICacheSupervisor _cacheSupervisor;
|
||||
private readonly string _streamCacheDir;
|
||||
private readonly string _legacyCacheDir;
|
||||
private readonly string _cacheDir;
|
||||
|
||||
public CachingFileStorage(IFileStorage cachedStorage, string cacheDir, ICacheSupervisor cacheSupervisor)
|
||||
public CachingFileStorage(IFileStorage cachedStorage, Context cacheDirContext, ICacheSupervisor cacheSupervisor)
|
||||
{
|
||||
_cachedStorage = new OfflineSwitchableFileStorage(cachedStorage);
|
||||
_cacheSupervisor = cacheSupervisor;
|
||||
_streamCacheDir = cacheDir + Java.IO.File.Separator + "OfflineCache" + Java.IO.File.Separator;
|
||||
if (!Directory.Exists(_streamCacheDir))
|
||||
Directory.CreateDirectory(_streamCacheDir);
|
||||
|
||||
}
|
||||
_legacyCacheDir = cacheDirContext.CacheDir.Path + Java.IO.File.Separator + "OfflineCache" + Java.IO.File.Separator;
|
||||
if (!Directory.Exists(_legacyCacheDir))
|
||||
Directory.CreateDirectory(_legacyCacheDir);
|
||||
|
||||
_cacheDir = IoUtil.GetInternalDirectory(cacheDirContext).Path + Java.IO.File.Separator + "OfflineCache" + Java.IO.File.Separator;
|
||||
if (!Directory.Exists(_cacheDir))
|
||||
Directory.CreateDirectory(_cacheDir);
|
||||
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
{
|
||||
IoUtil.DeleteDir(new Java.IO.File(_streamCacheDir), true);
|
||||
}
|
||||
IoUtil.DeleteDir(new Java.IO.File(_legacyCacheDir), true);
|
||||
IoUtil.DeleteDir(new Java.IO.File(_cacheDir), true);
|
||||
}
|
||||
|
||||
public IEnumerable<string> SupportedProtocols { get { return _cachedStorage.SupportedProtocols; } }
|
||||
|
||||
@@ -105,7 +111,11 @@ namespace keepass2android.Io
|
||||
{
|
||||
SHA256Managed sha256 = new SHA256Managed();
|
||||
string iocAsHexString = MemUtil.ByteArrayToHexString(sha256.ComputeHash(Encoding.Unicode.GetBytes(ioc.Path.ToCharArray())))+".cache";
|
||||
return _streamCacheDir + iocAsHexString;
|
||||
if (File.Exists(_legacyCacheDir + iocAsHexString))
|
||||
return _legacyCacheDir + iocAsHexString;
|
||||
|
||||
return _cacheDir + iocAsHexString;
|
||||
|
||||
}
|
||||
|
||||
public bool IsCached(IOConnectionInfo ioc)
|
||||
@@ -168,7 +178,9 @@ namespace keepass2android.Io
|
||||
if (!IsCached(ioc))
|
||||
throw;
|
||||
|
||||
Kp2aLog.Log("couldn't open from remote " + ioc.Path);
|
||||
#if DEBUG
|
||||
Kp2aLog.Log("couldn't open from remote " + ioc.Path);
|
||||
#endif
|
||||
Kp2aLog.Log(ex.ToString());
|
||||
|
||||
_cacheSupervisor.CouldntOpenFromRemote(ioc, ex);
|
||||
|
@@ -2,12 +2,12 @@ namespace keepass2android.Io
|
||||
{
|
||||
public partial class DropboxFileStorage
|
||||
{
|
||||
private const string AppKey = "";
|
||||
private const string AppSecret = "";
|
||||
private const string AppKey = "dummy";
|
||||
private const string AppSecret = "dummy";
|
||||
}
|
||||
public partial class DropboxAppFolderFileStorage
|
||||
{
|
||||
private const string AppKey = "";
|
||||
private const string AppSecret = "";
|
||||
private const string AppKey = "dummy";
|
||||
private const string AppSecret = "dummy";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -233,7 +233,6 @@ namespace keepass2android.Io
|
||||
|
||||
public FileDescription GetFileDescription(IOConnectionInfo ioc)
|
||||
{
|
||||
Kp2aLog.Log("GetFileDescription "+ioc.Path);
|
||||
try
|
||||
{
|
||||
return ConvertToFileDescription(Jfs.GetFileEntry(IocToPath(ioc)));
|
||||
@@ -302,7 +301,9 @@ namespace keepass2android.Io
|
||||
|
||||
public void OnResume(IFileStorageSetupActivity activity)
|
||||
{
|
||||
#if DEBUG
|
||||
Kp2aLog.Log("JFS/OnResume Ioc.Path=" +activity.Ioc.Path+". Path="+((IJavaFileStorageFileStorageSetupActivity)activity).Path);
|
||||
#endif
|
||||
_jfs.OnResume(((IJavaFileStorageFileStorageSetupActivity) activity));
|
||||
}
|
||||
|
||||
@@ -366,4 +367,4 @@ namespace keepass2android.Io
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
23
src/Kp2aBusinessLogic/Io/PCloudFileStorage.cs
Normal file
23
src/Kp2aBusinessLogic/Io/PCloudFileStorage.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Android.Content;
|
||||
#if !EXCLUDE_JAVAFILESTORAGE
|
||||
|
||||
namespace keepass2android.Io
|
||||
{
|
||||
public partial class PCloudFileStorage: JavaFileStorage
|
||||
{
|
||||
private const string ClientId = "CkRWTQXY6Lm";
|
||||
|
||||
public PCloudFileStorage(Context ctx, IKp2aApp app) :
|
||||
base(new Keepass2android.Javafilestorage.PCloudFileStorage(ctx, ClientId), app)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override bool UserShouldBackup
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
@@ -5,8 +5,8 @@ namespace keepass2android.Io
|
||||
{
|
||||
public class SftpFileStorage: JavaFileStorage
|
||||
{
|
||||
public SftpFileStorage(IKp2aApp app) :
|
||||
base(new Keepass2android.Javafilestorage.SftpStorage(), app)
|
||||
public SftpFileStorage(Context ctx, IKp2aApp app) :
|
||||
base(new Keepass2android.Javafilestorage.SftpStorage(ctx.ApplicationContext), app)
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,8 @@ namespace keepass2android.Io
|
||||
yield return "http";
|
||||
yield return "https";
|
||||
yield return "owncloud";
|
||||
}
|
||||
yield return "nextcloud";
|
||||
}
|
||||
}
|
||||
|
||||
public override bool UserShouldBackup
|
||||
@@ -38,12 +39,15 @@ namespace keepass2android.Io
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public static string Owncloud2Webdav(string owncloudUrl)
|
||||
public static string owncloudPrefix = "owncloud://";
|
||||
public static string nextcloudPrefix = "nextcloud://";
|
||||
|
||||
public static string Owncloud2Webdav(string owncloudUrl, string prefix)
|
||||
{
|
||||
string owncloudPrefix = "owncloud://";
|
||||
if (owncloudUrl.StartsWith(owncloudPrefix))
|
||||
|
||||
if (owncloudUrl.StartsWith(prefix))
|
||||
{
|
||||
owncloudUrl = owncloudUrl.Substring(owncloudPrefix.Length);
|
||||
owncloudUrl = owncloudUrl.Substring(prefix.Length);
|
||||
}
|
||||
if (!owncloudUrl.Contains("://"))
|
||||
owncloudUrl = "https://" + owncloudUrl;
|
||||
|
@@ -31,7 +31,7 @@
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NoNet;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
@@ -53,6 +53,7 @@
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Xamarin.Android.Arch.Core.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
@@ -90,7 +91,7 @@
|
||||
<Compile Include="database\CheckDatabaseForChanges.cs" />
|
||||
<Compile Include="database\edit\AddTemplateEntries.cs" />
|
||||
<Compile Include="database\edit\CopyEntry.cs" />
|
||||
<Compile Include="database\edit\DeleteMultipleItems.cs" />
|
||||
<Compile Include="database\edit\DeleteMultipleItemsFromOneDatabase.cs" />
|
||||
<Compile Include="database\edit\EditGroup.cs" />
|
||||
<Compile Include="database\edit\MoveElements.cs" />
|
||||
<Compile Include="database\KdbDatabaseFormat.cs" />
|
||||
@@ -102,15 +103,25 @@
|
||||
<Compile Include="DataExchange\Formats\KeePassKdb2x.cs" />
|
||||
<Compile Include="DataExchange\Formats\KeePassXml2x.cs" />
|
||||
<Compile Include="DataExchange\PwExportInfo.cs" />
|
||||
<Compile Include="ElementAndDatabaseId.cs" />
|
||||
<Compile Include="Io\AndroidContentStorage.cs" />
|
||||
<Compile Include="Io\BuiltInFileStorage.cs" />
|
||||
<Compile Include="Io\CachingFileStorage.cs" />
|
||||
<Compile Include="Io\DropboxFileStorage.cs" />
|
||||
<Compile Include="Io\DropboxFileStorageKeys.cs" />
|
||||
<Compile Include="Io\FileDescription.cs" />
|
||||
<Compile Include="Io\FileStorageSetupActivity.cs" />
|
||||
<Compile Include="Io\FileStorageSetupInitiatorActivity.cs" />
|
||||
<Compile Include="Io\GDriveFileStorage.cs" />
|
||||
<Compile Include="Io\IFileStorage.cs" />
|
||||
<Compile Include="Io\IoUtil.cs" />
|
||||
<Compile Include="Io\JavaFileStorage.cs" />
|
||||
<Compile Include="Io\NetFtpFileStorage.cs" />
|
||||
<Compile Include="Io\OfflineSwitchableFileStorage.cs" />
|
||||
<Compile Include="Io\PCloudFileStorage.cs" />
|
||||
<Compile Include="Io\SftpFileStorage.cs" />
|
||||
<Compile Include="Io\OneDriveFileStorage.cs" />
|
||||
<Compile Include="Io\WebDavFileStorage.cs" />
|
||||
<Compile Include="IProgressDialog.cs" />
|
||||
<Compile Include="PreferenceKey.cs" />
|
||||
<Compile Include="SelectStorageLocationActivityBase.cs" />
|
||||
@@ -119,15 +130,15 @@
|
||||
<Compile Include="database\edit\ActionOnFinish.cs" />
|
||||
<Compile Include="database\edit\AddEntry.cs" />
|
||||
<Compile Include="database\edit\AddGroup.cs" />
|
||||
<Compile Include="database\edit\CreateDb.cs" />
|
||||
<Compile Include="database\edit\CreateDB.cs" />
|
||||
<Compile Include="database\edit\DeleteEntry.cs" />
|
||||
<Compile Include="database\edit\DeleteGroup.cs" />
|
||||
<Compile Include="database\edit\DeleteRunnable.cs" />
|
||||
<Compile Include="database\edit\FileOnFinish.cs" />
|
||||
<Compile Include="database\edit\LoadDb.cs" />
|
||||
<Compile Include="database\edit\LoadDB.cs" />
|
||||
<Compile Include="database\edit\OnFinish.cs" />
|
||||
<Compile Include="database\edit\RunnableOnFinish.cs" />
|
||||
<Compile Include="database\edit\SaveDb.cs" />
|
||||
<Compile Include="database\edit\SaveDB.cs" />
|
||||
<Compile Include="database\edit\SetPassword.cs" />
|
||||
<Compile Include="database\edit\UpdateEntry.cs" />
|
||||
<Compile Include="IKp2aApp.cs" />
|
||||
@@ -146,6 +157,10 @@
|
||||
<Compile Include="Utils\Spr\SprEngine.PickChars.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\JavaFileStorageBindings\JavaFileStorageBindings.csproj">
|
||||
<Project>{48574278-4779-4b3a-a9e4-9cf1bc285d0b}</Project>
|
||||
<Name>JavaFileStorageBindings</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj">
|
||||
<Project>{545B4A6B-8BBA-4FBE-92FC-4AC060122A54}</Project>
|
||||
<Name>KeePassLib2Android</Name>
|
||||
@@ -154,6 +169,10 @@
|
||||
<Project>{70D3844A-D9FA-4A64-B205-A84C6A822196}</Project>
|
||||
<Name>KP2AKdbLibraryBinding</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\netftpandroid\System.Net.FtpClient\System.Net.FtpClient.Android.csproj">
|
||||
<Project>{146FD497-BA03-4740-B6C5-5C84EA8FCDE2}</Project>
|
||||
<Name>System.Net.FtpClient.Android</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\TwofishCipher\TwofishCipher.csproj">
|
||||
<Project>{5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9}</Project>
|
||||
<Name>TwofishCipher</Name>
|
||||
|
@@ -52,7 +52,8 @@ namespace keepass2android
|
||||
|
||||
public void UpdateMessage (String message)
|
||||
{
|
||||
_message = message;
|
||||
Kp2aLog.Log("status message: " + message);
|
||||
_message = message;
|
||||
if ( _app!= null && _progressDialog != null && _handler != null ) {
|
||||
_handler.Post(() => {_progressDialog.SetMessage(message); } );
|
||||
}
|
||||
@@ -60,6 +61,7 @@ namespace keepass2android
|
||||
|
||||
public void UpdateSubMessage(String submessage)
|
||||
{
|
||||
Kp2aLog.Log("status submessage: " + submessage);
|
||||
_submessage = submessage;
|
||||
if (_app != null && _progressDialog != null && _handler != null)
|
||||
{
|
||||
|
@@ -23,7 +23,7 @@ namespace keepass2android
|
||||
/// <summary>
|
||||
/// EqualityComparer implementation to compare PwGroups based on their Id
|
||||
/// </summary>
|
||||
class PwGroupEqualityFromIdComparer: IEqualityComparer<PwGroup>
|
||||
public class PwGroupEqualityFromIdComparer: IEqualityComparer<PwGroup>
|
||||
{
|
||||
#region IEqualityComparer implementation
|
||||
public bool Equals (PwGroup x, PwGroup y)
|
||||
|
@@ -103,7 +103,7 @@ namespace keepass2android
|
||||
PwGroup pgResults = new PwGroup(true, true, strGroupName, PwIcon.EMailSearch) {IsVirtual = true};
|
||||
if (String.IsNullOrWhiteSpace(host))
|
||||
return pgResults;
|
||||
foreach (PwEntry entry in database.Entries.Values)
|
||||
foreach (PwEntry entry in database.EntriesById.Values)
|
||||
{
|
||||
string otherUrl = entry.Strings.ReadSafe(PwDefs.UrlField);
|
||||
otherUrl = SprEngine.Compile(otherUrl, new SprContext(entry, database.KpDatabase, SprCompileFlags.References));
|
||||
|
@@ -86,6 +86,8 @@ namespace keepass2android
|
||||
ReadOnlyReason_PreKitKat,
|
||||
ReadOnlyReason_ReadOnlyFlag,
|
||||
ReadOnlyReason_ReadOnlyKitKat,
|
||||
ReadOnlyReason_LocalBackup
|
||||
ReadOnlyReason_LocalBackup,
|
||||
Ok,
|
||||
cancel
|
||||
}
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ namespace keepass2android
|
||||
{
|
||||
try
|
||||
{
|
||||
IOConnectionInfo ioc = _app.GetDb().Ioc;
|
||||
IOConnectionInfo ioc = _app.CurrentDb.Ioc;
|
||||
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
||||
if (fileStorage is CachingFileStorage)
|
||||
{
|
||||
@@ -49,7 +49,7 @@ namespace keepass2android
|
||||
hashingRemoteStream.CopyTo(remoteData);
|
||||
hashingRemoteStream.Close();
|
||||
|
||||
if (!MemUtil.ArraysEqual(_app.GetDb().KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash))
|
||||
if (!MemUtil.ArraysEqual(_app.CurrentDb.KpDatabase.HashOfFileOnDisk, hashingRemoteStream.Hash))
|
||||
{
|
||||
_app.TriggerReload(_context);
|
||||
Finish(true);
|
||||
|
@@ -26,6 +26,7 @@ using KeePassLib;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
using keepass2android.Io;
|
||||
using KeePassLib.Interfaces;
|
||||
using KeePassLib.Utility;
|
||||
using Exception = System.Exception;
|
||||
using String = System.String;
|
||||
@@ -33,27 +34,23 @@ using String = System.String;
|
||||
namespace keepass2android
|
||||
{
|
||||
|
||||
public class Database {
|
||||
|
||||
|
||||
public Dictionary<PwUuid, PwGroup> Groups = new Dictionary<PwUuid, PwGroup>(new PwUuidEqualityComparer());
|
||||
public Dictionary<PwUuid, PwEntry> Entries = new Dictionary<PwUuid, PwEntry>(new PwUuidEqualityComparer());
|
||||
public HashSet<PwGroup> Dirty = new HashSet<PwGroup>(new PwGroupEqualityFromIdComparer());
|
||||
public class Database
|
||||
{
|
||||
public HashSet<IStructureItem> Elements = new HashSet<IStructureItem>();
|
||||
public Dictionary<PwUuid, PwGroup> GroupsById = new Dictionary<PwUuid, PwGroup>(new PwUuidEqualityComparer());
|
||||
public Dictionary<PwUuid, PwEntry> EntriesById = new Dictionary<PwUuid, PwEntry>(new PwUuidEqualityComparer());
|
||||
public PwGroup Root;
|
||||
public PwDatabase KpDatabase;
|
||||
public IOConnectionInfo Ioc
|
||||
{
|
||||
get
|
||||
{
|
||||
return KpDatabase == null ? null : KpDatabase.IOConnectionInfo;
|
||||
|
||||
return KpDatabase?.IOConnectionInfo;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information about the last opened entry. Includes the entry but also transformed fields.
|
||||
/// </summary>
|
||||
public PwEntryOutput LastOpenedEntry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// if an OTP key was used, this property tells the location of the OTP auxiliary file.
|
||||
/// Must be set after loading.
|
||||
@@ -73,31 +70,14 @@ namespace keepass2android
|
||||
_app = app;
|
||||
CanWrite = true; //default
|
||||
}
|
||||
|
||||
private bool _loaded;
|
||||
|
||||
private bool _reloadRequested;
|
||||
private IDatabaseFormat _databaseFormat = new KdbxDatabaseFormat(KdbxFormat.Default);
|
||||
private IDatabaseFormat _databaseFormat = new KdbxDatabaseFormat(KdbxFormat.Default);
|
||||
|
||||
public bool ReloadRequested
|
||||
{
|
||||
get { return _reloadRequested; }
|
||||
set { _reloadRequested = value; }
|
||||
}
|
||||
public bool ReloadRequested { get; set; }
|
||||
|
||||
public bool Loaded {
|
||||
get { return _loaded;}
|
||||
set { _loaded = value; }
|
||||
}
|
||||
|
||||
public bool DidOpenFileChange()
|
||||
public bool DidOpenFileChange()
|
||||
{
|
||||
if (Loaded == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _app.GetFileStorage(Ioc).CheckForFileChangeFast(Ioc, LastFileVersion);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -112,32 +92,20 @@ namespace keepass2android
|
||||
Stream s = databaseData ?? fileStorage.OpenFileForRead(iocInfo);
|
||||
var fileVersion = _app.GetFileStorage(iocInfo).GetCurrentFileVersionFast(iocInfo);
|
||||
PopulateDatabaseFromStream(pwDatabase, s, iocInfo, compositeKey, status, databaseFormat);
|
||||
try
|
||||
{
|
||||
LastFileVersion = fileVersion;
|
||||
LastFileVersion = fileVersion;
|
||||
|
||||
status.UpdateSubMessage("");
|
||||
status.UpdateSubMessage("");
|
||||
|
||||
Root = pwDatabase.RootGroup;
|
||||
PopulateGlobals(Root);
|
||||
Root = pwDatabase.RootGroup;
|
||||
PopulateGlobals(Root);
|
||||
|
||||
|
||||
KpDatabase = pwDatabase;
|
||||
SearchHelper = new SearchDbHelper(app);
|
||||
KpDatabase = pwDatabase;
|
||||
SearchHelper = new SearchDbHelper(app);
|
||||
|
||||
_databaseFormat = databaseFormat;
|
||||
_databaseFormat = databaseFormat;
|
||||
|
||||
CanWrite = databaseFormat.CanWrite && !fileStorage.IsReadOnly(iocInfo);
|
||||
Loaded = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Clear();
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CanWrite = databaseFormat.CanWrite && !fileStorage.IsReadOnly(iocInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -219,8 +187,7 @@ namespace keepass2android
|
||||
|
||||
public void SaveData() {
|
||||
|
||||
KpDatabase.UseFileTransactions = _app.GetBooleanPreference(PreferenceKey.UseFileTransactions);
|
||||
using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, KpDatabase.UseFileTransactions))
|
||||
using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, _app.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
|
||||
{
|
||||
DatabaseFormat.Save(KpDatabase, trans.OpenFile());
|
||||
|
||||
@@ -239,14 +206,18 @@ namespace keepass2android
|
||||
{
|
||||
if (checkForDuplicateUuids)
|
||||
{
|
||||
if (Entries.ContainsKey(e.Uuid))
|
||||
if (EntriesById.ContainsKey(e.Uuid))
|
||||
{
|
||||
throw new DuplicateUuidsException("Same UUID for entries '"+Entries[e.Uuid].Strings.ReadSafe(PwDefs.TitleField)+"' and '"+e.Strings.ReadSafe(PwDefs.TitleField)+"'.");
|
||||
throw new DuplicateUuidsException("Same UUID for entries '"+EntriesById[e.Uuid].Strings.ReadSafe(PwDefs.TitleField)+"' and '"+e.Strings.ReadSafe(PwDefs.TitleField)+"'.");
|
||||
}
|
||||
|
||||
}
|
||||
Entries [e.Uuid] = e;
|
||||
EntriesById [e.Uuid] = e;
|
||||
Elements.Add(e);
|
||||
}
|
||||
|
||||
GroupsById[currentGroup.Uuid] = currentGroup;
|
||||
Elements.Add(currentGroup);
|
||||
foreach (PwGroup g in childGroups)
|
||||
{
|
||||
if (checkForDuplicateUuids)
|
||||
@@ -258,7 +229,6 @@ namespace keepass2android
|
||||
}
|
||||
* */
|
||||
}
|
||||
Groups[g.Uuid] = g;
|
||||
PopulateGlobals(g);
|
||||
}
|
||||
}
|
||||
@@ -266,33 +236,15 @@ namespace keepass2android
|
||||
{
|
||||
PopulateGlobals(currentGroup, _app.CheckForDuplicateUuids);
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
_loaded = false;
|
||||
|
||||
Groups.Clear();
|
||||
Entries.Clear();
|
||||
Dirty.Clear();
|
||||
DrawableFactory.Clear();
|
||||
|
||||
Root = null;
|
||||
KpDatabase = null;
|
||||
|
||||
CanWrite = true;
|
||||
_reloadRequested = false;
|
||||
OtpAuxFileIoc = null;
|
||||
LastOpenedEntry = null;
|
||||
}
|
||||
|
||||
public void MarkAllGroupsAsDirty() {
|
||||
foreach ( PwGroup group in Groups.Values ) {
|
||||
Dirty.Add(group);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void UpdateGlobals()
|
||||
{
|
||||
EntriesById.Clear();
|
||||
GroupsById.Clear();
|
||||
Elements.Clear();
|
||||
PopulateGlobals(Root);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
@@ -216,9 +216,16 @@ namespace keepass2android
|
||||
|
||||
private DateTime JavaTimeToCSharp(long javatime)
|
||||
{
|
||||
try
|
||||
{
|
||||
var utcTime = new DateTime(1970, 1, 1).AddMilliseconds(javatime);
|
||||
return TimeZoneInfo.ConvertTimeFromUtc(utcTime, TimeZoneInfo.Local);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
|
||||
var utcTime = new DateTime(1970, 1, 1).AddMilliseconds(javatime);
|
||||
return TimeZoneInfo.ConvertTimeFromUtc(utcTime, TimeZoneInfo.Local);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
@@ -14,13 +14,13 @@ namespace keepass2android
|
||||
public class PwEntryOutput
|
||||
{
|
||||
private readonly PwEntry _entry;
|
||||
private readonly PwDatabase _db;
|
||||
private readonly Database _db;
|
||||
private readonly ProtectedStringDictionary _outputStrings = new ProtectedStringDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the PwEntryOutput by replacing the placeholders
|
||||
/// </summary>
|
||||
public PwEntryOutput(PwEntry entry, PwDatabase db)
|
||||
public PwEntryOutput(PwEntry entry, Database db)
|
||||
{
|
||||
_entry = entry;
|
||||
_db = db;
|
||||
@@ -34,7 +34,7 @@ namespace keepass2android
|
||||
string GetStringAndReplacePlaceholders(string key)
|
||||
{
|
||||
String value = Entry.Strings.ReadSafe(key);
|
||||
value = SprEngine.Compile(value, new SprContext(Entry, _db, SprCompileFlags.All));
|
||||
value = SprEngine.Compile(value, new SprContext(Entry, _db.KpDatabase, SprCompileFlags.All));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@@ -26,7 +26,7 @@ namespace keepass2android
|
||||
{
|
||||
try
|
||||
{
|
||||
IOConnectionInfo ioc = _app.GetDb().Ioc;
|
||||
IOConnectionInfo ioc = _app.CurrentDb.Ioc;
|
||||
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
||||
if (!(fileStorage is CachingFileStorage))
|
||||
{
|
||||
@@ -70,10 +70,12 @@ namespace keepass2android
|
||||
Finish(true, _app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
||||
}
|
||||
_saveDb = null;
|
||||
}), false, remoteData);
|
||||
}), _app.CurrentDb, false, remoteData);
|
||||
_saveDb.Run();
|
||||
|
||||
_app.GetDb().MarkAllGroupsAsDirty();
|
||||
_app.CurrentDb.UpdateGlobals();
|
||||
|
||||
_app.MarkAllGroupsAsDirty();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -103,6 +105,7 @@ namespace keepass2android
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Kp2aLog.LogUnexpectedError(e);
|
||||
Finish(false, e.Message);
|
||||
}
|
||||
|
||||
|
@@ -24,7 +24,7 @@ namespace keepass2android
|
||||
public class AddEntry : RunnableOnFinish {
|
||||
protected Database Db
|
||||
{
|
||||
get { return _app.GetDb(); }
|
||||
get { return _app.CurrentDb; }
|
||||
}
|
||||
|
||||
private readonly IKp2aApp _app;
|
||||
@@ -37,13 +37,13 @@ namespace keepass2android
|
||||
return new AddEntry(ctx, app, entry, parentGroup, finish);
|
||||
}
|
||||
|
||||
protected AddEntry(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
|
||||
public AddEntry(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
|
||||
_ctx = ctx;
|
||||
_parentGroup = parentGroup;
|
||||
_app = app;
|
||||
_entry = entry;
|
||||
|
||||
_onFinishToRun = new AfterAdd(ctx, app.GetDb(), entry, OnFinishToRun);
|
||||
_onFinishToRun = new AfterAdd(ctx, app.CurrentDb, entry, app,OnFinishToRun);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace keepass2android
|
||||
|
||||
|
||||
// Commit to disk
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
|
||||
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
}
|
||||
@@ -68,12 +68,13 @@ namespace keepass2android
|
||||
private class AfterAdd : OnFinish {
|
||||
private readonly Database _db;
|
||||
private readonly PwEntry _entry;
|
||||
private readonly IKp2aApp _app;
|
||||
|
||||
public AfterAdd(Activity activity, Database db, PwEntry entry, OnFinish finish):base(activity, finish) {
|
||||
public AfterAdd(Activity activity, Database db, PwEntry entry, IKp2aApp app, OnFinish finish):base(activity, finish) {
|
||||
_db = db;
|
||||
_entry = entry;
|
||||
|
||||
}
|
||||
_app = app;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -83,11 +84,12 @@ namespace keepass2android
|
||||
PwGroup parent = _entry.ParentGroup;
|
||||
|
||||
// Mark parent group dirty
|
||||
_db.Dirty.Add(parent);
|
||||
_app.DirtyGroups.Add(parent);
|
||||
|
||||
// Add entry to global
|
||||
_db.Entries[_entry.Uuid] = _entry;
|
||||
|
||||
_db.EntriesById[_entry.Uuid] = _entry;
|
||||
_db.Elements.Add(_entry);
|
||||
|
||||
} else
|
||||
{
|
||||
StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
|
||||
|
@@ -26,13 +26,16 @@ namespace keepass2android
|
||||
public class AddGroup : RunnableOnFinish {
|
||||
internal Database Db
|
||||
{
|
||||
get { return _app.GetDb(); }
|
||||
get { return _app.CurrentDb; }
|
||||
}
|
||||
private IKp2aApp _app;
|
||||
|
||||
public IKp2aApp App { get => _app; }
|
||||
|
||||
private IKp2aApp _app;
|
||||
private readonly String _name;
|
||||
private readonly int _iconId;
|
||||
private readonly PwUuid _groupCustomIconId;
|
||||
internal PwGroup Group;
|
||||
public PwGroup Group;
|
||||
internal PwGroup Parent;
|
||||
protected bool DontSave;
|
||||
readonly Activity _ctx;
|
||||
@@ -67,9 +70,11 @@ namespace keepass2android
|
||||
Group.CustomIconUuid = _groupCustomIconId;
|
||||
}
|
||||
Parent.AddGroup(Group, true);
|
||||
_app.CurrentDb.GroupsById[Group.Uuid] = Group;
|
||||
_app.CurrentDb.Elements.Add(Group);
|
||||
|
||||
// Commit to disk
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, DontSave);
|
||||
// Commit to disk
|
||||
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, DontSave);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
}
|
||||
@@ -86,13 +91,15 @@ namespace keepass2android
|
||||
|
||||
if ( Success ) {
|
||||
// Mark parent group dirty
|
||||
_addGroup.Db.Dirty.Add(_addGroup.Parent);
|
||||
_addGroup.App.DirtyGroups.Add(_addGroup.Parent);
|
||||
|
||||
// Add group to global list
|
||||
_addGroup.Db.Groups[_addGroup.Group.Uuid] = _addGroup.Group;
|
||||
_addGroup.Db.GroupsById[_addGroup.Group.Uuid] = _addGroup.Group;
|
||||
_addGroup.Db.Elements.Add(_addGroup.Group);
|
||||
} else {
|
||||
StatusLogger.UpdateMessage(UiStringKey.UndoingChanges);
|
||||
_addGroup.Parent.Groups.Remove(_addGroup.Group);
|
||||
|
||||
}
|
||||
|
||||
base.Run();
|
||||
|
@@ -28,7 +28,7 @@ namespace keepass2android
|
||||
{
|
||||
public class AddTemplateEntries : RunnableOnFinish {
|
||||
|
||||
class TemplateEntry
|
||||
public class TemplateEntry
|
||||
{
|
||||
public UiStringKey Title { get; set; }
|
||||
public PwIcon Icon { get; set; }
|
||||
@@ -47,11 +47,12 @@ namespace keepass2android
|
||||
void AddToEntry(IKp2aApp app, PwEntry entry, int position);
|
||||
}
|
||||
|
||||
internal enum FieldType
|
||||
public enum FieldType
|
||||
{
|
||||
Inline, ProtectedInline
|
||||
}
|
||||
internal enum SpecialFieldKey
|
||||
|
||||
public enum SpecialFieldKey
|
||||
{
|
||||
ExpDate, OverrideUrl, Tags
|
||||
}
|
||||
@@ -125,7 +126,7 @@ namespace keepass2android
|
||||
|
||||
protected Database Db
|
||||
{
|
||||
get { return _app.GetDb(); }
|
||||
get { return _app.CurrentDb; }
|
||||
}
|
||||
|
||||
private readonly IKp2aApp _app;
|
||||
@@ -140,7 +141,7 @@ namespace keepass2android
|
||||
//_onFinishToRun = new AfterAdd(this, OnFinishToRun);
|
||||
}
|
||||
|
||||
static readonly List<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
|
||||
public static readonly List<TemplateEntry> TemplateEntries = new List<TemplateEntry>()
|
||||
{
|
||||
new TemplateEntry()
|
||||
{
|
||||
@@ -285,12 +286,23 @@ namespace keepass2android
|
||||
|
||||
};
|
||||
|
||||
public static bool ContainsAllTemplates(IKp2aApp app)
|
||||
public static bool ContainsAllTemplates(Database db)
|
||||
{
|
||||
return TemplateEntries.All(t => app.GetDb().Entries.ContainsKey(t.Uuid));
|
||||
return TemplateEntries.All(t =>
|
||||
{
|
||||
string hexId = t.Uuid.ToHexString();
|
||||
|
||||
return db.EntriesById.Any(kvp => kvp.Key.Equals(t.Uuid) ||
|
||||
kvp.Value.Strings.ReadSafe(TemplateIdStringKey) == hexId);
|
||||
});
|
||||
}
|
||||
|
||||
public override void Run() {
|
||||
public static string TemplateIdStringKey
|
||||
{
|
||||
get { return "KP2A_TemplateId"; }
|
||||
}
|
||||
|
||||
public override void Run() {
|
||||
StatusLogger.UpdateMessage(UiStringKey.AddingEntry);
|
||||
|
||||
List<PwEntry> addedEntries;
|
||||
@@ -298,10 +310,10 @@ namespace keepass2android
|
||||
|
||||
if (addedEntries.Any())
|
||||
{
|
||||
_app.GetDb().Dirty.Add(templateGroup);
|
||||
_app.DirtyGroups.Add(templateGroup);
|
||||
|
||||
// Commit to disk
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
|
||||
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
}
|
||||
@@ -315,26 +327,28 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
PwGroup templateGroup;
|
||||
if (!_app.GetDb().Groups.TryGetValue(_app.GetDb().KpDatabase.EntryTemplatesGroup, out templateGroup))
|
||||
if (!_app.CurrentDb.GroupsById.TryGetValue(_app.CurrentDb.KpDatabase.EntryTemplatesGroup, out templateGroup))
|
||||
{
|
||||
//create template group
|
||||
templateGroup = new PwGroup(true, true, _app.GetResourceString(UiStringKey.TemplateGroupName), PwIcon.Folder);
|
||||
_app.GetDb().KpDatabase.RootGroup.AddGroup(templateGroup, true);
|
||||
_app.GetDb().KpDatabase.EntryTemplatesGroup = templateGroup.Uuid;
|
||||
_app.GetDb().KpDatabase.EntryTemplatesGroupChanged = DateTime.Now;
|
||||
_app.GetDb().Dirty.Add(_app.GetDb().KpDatabase.RootGroup);
|
||||
_app.GetDb().Groups[templateGroup.Uuid] = templateGroup;
|
||||
_app.CurrentDb.KpDatabase.RootGroup.AddGroup(templateGroup, true);
|
||||
_app.CurrentDb.KpDatabase.EntryTemplatesGroup = templateGroup.Uuid;
|
||||
_app.CurrentDb.KpDatabase.EntryTemplatesGroupChanged = DateTime.Now;
|
||||
_app.DirtyGroups.Add(_app.CurrentDb.KpDatabase.RootGroup);
|
||||
_app.CurrentDb.GroupsById[templateGroup.Uuid] = templateGroup;
|
||||
_app.CurrentDb.Elements.Add(templateGroup);
|
||||
|
||||
}
|
||||
addedEntries = new List<PwEntry>();
|
||||
|
||||
foreach (var template in TemplateEntries)
|
||||
{
|
||||
if (_app.GetDb().Entries.ContainsKey(template.Uuid))
|
||||
if (_app.CurrentDb.EntriesById.ContainsKey(template.Uuid))
|
||||
continue;
|
||||
PwEntry entry = CreateEntry(template);
|
||||
templateGroup.AddEntry(entry, true);
|
||||
addedEntries.Add(entry);
|
||||
_app.GetDb().Entries[entry.Uuid] = entry;
|
||||
_app.CurrentDb.EntriesById[entry.Uuid] = entry;
|
||||
}
|
||||
return templateGroup;
|
||||
}
|
||||
@@ -373,8 +387,12 @@ namespace keepass2android
|
||||
base.Run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static bool IsTemplateId(PwUuid pwUuid)
|
||||
{
|
||||
return TemplateEntries.Any(te => te.Uuid.Equals(pwUuid));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -27,23 +27,22 @@ namespace keepass2android
|
||||
{
|
||||
|
||||
public class CreateDb : RunnableOnFinish {
|
||||
|
||||
private const ulong DefaultEncryptionRounds = PwDefs.DefaultKeyEncryptionRounds;
|
||||
|
||||
private readonly IOConnectionInfo _ioc;
|
||||
private readonly IOConnectionInfo _ioc;
|
||||
private readonly bool _dontSave;
|
||||
private readonly Activity _ctx;
|
||||
private readonly IKp2aApp _app;
|
||||
private CompositeKey _key;
|
||||
private readonly bool _makeCurrent;
|
||||
|
||||
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave): base(ctx, finish) {
|
||||
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, bool makeCurrent): base(ctx, finish) {
|
||||
_ctx = ctx;
|
||||
_ioc = ioc;
|
||||
_dontSave = dontSave;
|
||||
_app = app;
|
||||
_makeCurrent = makeCurrent;
|
||||
_app = app;
|
||||
}
|
||||
|
||||
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, CompositeKey key)
|
||||
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, CompositeKey key, bool makeCurrent)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_ctx = ctx;
|
||||
@@ -51,12 +50,13 @@ namespace keepass2android
|
||||
_dontSave = dontSave;
|
||||
_app = app;
|
||||
_key = key;
|
||||
_makeCurrent = makeCurrent;
|
||||
}
|
||||
|
||||
|
||||
public override void Run() {
|
||||
StatusLogger.UpdateMessage(UiStringKey.progress_create);
|
||||
Database db = _app.CreateNewDatabase();
|
||||
Database db = _app.CreateNewDatabase(_makeCurrent);
|
||||
|
||||
db.KpDatabase = new KeePassLib.PwDatabase();
|
||||
|
||||
@@ -74,7 +74,6 @@ namespace keepass2android
|
||||
|
||||
// Set Database state
|
||||
db.Root = db.KpDatabase.RootGroup;
|
||||
db.Loaded = true;
|
||||
db.SearchHelper = new SearchDbHelper(_app);
|
||||
|
||||
// Add a couple default groups
|
||||
@@ -88,12 +87,14 @@ namespace keepass2android
|
||||
addTemplates.AddTemplates(out addedEntries);
|
||||
|
||||
// Commit changes
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave);
|
||||
SaveDb save = new SaveDb(_ctx, _app, db, OnFinishToRun, _dontSave);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
_onFinishToRun = null;
|
||||
save.Run();
|
||||
|
||||
|
||||
|
||||
db.UpdateGlobals();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ namespace keepass2android
|
||||
|
||||
public DeleteEntry(Activity activiy, IKp2aApp app, PwEntry entry, OnFinish finish):base(activiy, finish, app) {
|
||||
Ctx = activiy;
|
||||
Db = app.GetDb();
|
||||
Db = app.FindDatabaseForElement(entry);
|
||||
_entry = entry;
|
||||
|
||||
}
|
||||
@@ -40,7 +40,7 @@ namespace keepass2android
|
||||
{
|
||||
get
|
||||
{
|
||||
return App.GetDb().DatabaseFormat.CanRecycle && CanRecycleGroup(_entry.ParentGroup);
|
||||
return Db.DatabaseFormat.CanRecycle && CanRecycleGroup(_entry.ParentGroup);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -47,7 +47,7 @@ namespace keepass2android
|
||||
*/
|
||||
private void SetMembers(Activity activity, IKp2aApp app, PwGroup group, bool dontSave)
|
||||
{
|
||||
base.SetMembers(activity, app.GetDb());
|
||||
base.SetMembers(activity, app.FindDatabaseForElement(group));
|
||||
|
||||
_group = group;
|
||||
DontSave = dontSave;
|
||||
@@ -58,7 +58,7 @@ namespace keepass2android
|
||||
{
|
||||
get
|
||||
{
|
||||
return App.GetDb().DatabaseFormat.CanRecycle && CanRecycleGroup(_group);
|
||||
return Db.DatabaseFormat.CanRecycle && CanRecycleGroup(_group);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,12 +7,12 @@ using KeePassLib.Interfaces;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
public class DeleteMultipleItems : DeleteRunnable
|
||||
public class DeleteMultipleItemsFromOneDatabase : DeleteRunnable
|
||||
{
|
||||
private readonly List<IStructureItem> _elementsToDelete;
|
||||
private readonly bool _canRecycle;
|
||||
|
||||
public DeleteMultipleItems(Activity activity, Database db, List<IStructureItem> elementsToDelete, OnFinish finish, IKp2aApp app)
|
||||
public DeleteMultipleItemsFromOneDatabase(Activity activity, Database db, List<IStructureItem> elementsToDelete, OnFinish finish, IKp2aApp app)
|
||||
: base(activity, finish, app)
|
||||
{
|
||||
_elementsToDelete = elementsToDelete;
|
||||
@@ -21,12 +21,13 @@ namespace keepass2android
|
||||
//determine once. The property is queried for each delete operation, but might return false
|
||||
//after one entry/group is deleted (and thus in recycle bin and thus can't be recycled anymore)
|
||||
_canRecycle = DetermineCanRecycle();
|
||||
ShowDatabaseIocInStatus = true;
|
||||
}
|
||||
|
||||
private bool DetermineCanRecycle()
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "CanRecycle?");
|
||||
if (!App.GetDb().DatabaseFormat.CanRecycle)
|
||||
if (!Db.DatabaseFormat.CanRecycle)
|
||||
{
|
||||
Android.Util.Log.Debug("KP2A", "CanRecycle? No because of DB format.");
|
||||
return false;
|
@@ -100,7 +100,8 @@ namespace keepass2android
|
||||
};
|
||||
|
||||
Db.KpDatabase.RootGroup.AddGroup(pgRecycleBin, true);
|
||||
Db.Groups[pgRecycleBin.Uuid] = pgRecycleBin;
|
||||
Db.GroupsById[pgRecycleBin.Uuid] = pgRecycleBin;
|
||||
Db.Elements.Add(pgRecycleBin);
|
||||
Db.KpDatabase.RecycleBinUuid = pgRecycleBin.Uuid;
|
||||
|
||||
bGroupListUpdateRequired = true;
|
||||
@@ -121,24 +122,27 @@ namespace keepass2android
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (CanRecycle)
|
||||
string messageSuffix = ShowDatabaseIocInStatus ? "(" + App.GetFileStorage(Db.Ioc).GetDisplayName(Db.Ioc) + ")" : "";
|
||||
|
||||
if (CanRecycle)
|
||||
{
|
||||
App.AskYesNoCancel(UiStringKey.AskDeletePermanently_title,
|
||||
QuestionRecycleResourceId,
|
||||
(dlgSender, dlgEvt) =>
|
||||
{
|
||||
DeletePermanently = true;
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
DeletePermanently = true;
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
|
||||
},
|
||||
(dlgSender, dlgEvt) =>
|
||||
{
|
||||
DeletePermanently = false;
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
DeletePermanently = false;
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
},
|
||||
(dlgSender, dlgEvt) => { },
|
||||
Ctx);
|
||||
Ctx, messageSuffix);
|
||||
|
||||
|
||||
|
||||
@@ -149,12 +153,12 @@ namespace keepass2android
|
||||
QuestionNoRecycleResourceId,
|
||||
(dlgSender, dlgEvt) =>
|
||||
{
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
ProgressTask pt = new ProgressTask(App, Ctx, this);
|
||||
pt.Run();
|
||||
},
|
||||
null,
|
||||
(dlgSender, dlgEvt) => { },
|
||||
Ctx);
|
||||
Ctx, messageSuffix);
|
||||
|
||||
|
||||
}
|
||||
@@ -182,7 +186,8 @@ namespace keepass2android
|
||||
PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow);
|
||||
pd.DeletedObjects.Add(pdo);
|
||||
touchedGroups.Add(pgParent);
|
||||
Db.Entries.Remove(pe.Uuid);
|
||||
Db.EntriesById.Remove(pe.Uuid);
|
||||
Db.Elements.Remove(pe);
|
||||
}
|
||||
else // Recycle
|
||||
{
|
||||
@@ -215,31 +220,41 @@ namespace keepass2android
|
||||
if (success)
|
||||
{
|
||||
foreach (var g in touchedGroups)
|
||||
Db.Dirty.Add(g);
|
||||
App.DirtyGroups.Add(g);
|
||||
foreach (var g in permanentlyDeletedGroups)
|
||||
{
|
||||
//remove groups from global lists if present there
|
||||
Db.Dirty.Remove(g);
|
||||
Db.Groups.Remove(g.Uuid);
|
||||
//remove groups from global lists if present there
|
||||
App.DirtyGroups.Remove(g);
|
||||
Db.GroupsById.Remove(g.Uuid);
|
||||
Db.Elements.Remove(g);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's not bother recovering from a failure to save. It is too much work.
|
||||
App.LockDatabase(false);
|
||||
App.Lock(false);
|
||||
}
|
||||
}, OnFinishToRun);
|
||||
|
||||
// Commit database
|
||||
SaveDb save = new SaveDb(Ctx, App, OnFinishToRun, false);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
SaveDb save = new SaveDb(Ctx, App, Db, OnFinishToRun, false);
|
||||
save.ShowDatabaseIocInStatus = ShowDatabaseIocInStatus;
|
||||
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected abstract void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups);
|
||||
public bool ShowDatabaseIocInStatus
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
protected abstract void PerformDelete(List<PwGroup> touchedGroups, List<PwGroup> permanentlyDeletedGroups);
|
||||
|
||||
public abstract UiStringKey StatusMessage { get; }
|
||||
|
||||
|
@@ -26,9 +26,12 @@ namespace keepass2android
|
||||
public class EditGroup : RunnableOnFinish {
|
||||
internal Database Db
|
||||
{
|
||||
get { return _app.GetDb(); }
|
||||
get { return _app.FindDatabaseForElement(Group); }
|
||||
}
|
||||
private IKp2aApp _app;
|
||||
|
||||
public IKp2aApp App { get => _app; }
|
||||
|
||||
private IKp2aApp _app;
|
||||
private readonly String _name;
|
||||
private readonly PwIcon _iconId;
|
||||
private readonly PwUuid _customIconId;
|
||||
@@ -57,7 +60,7 @@ namespace keepass2android
|
||||
Group.Touch(true);
|
||||
|
||||
// Commit to disk
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
|
||||
SaveDb save = new SaveDb(_ctx, _app, Db, OnFinishToRun);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
}
|
||||
@@ -76,10 +79,10 @@ namespace keepass2android
|
||||
|
||||
if ( Success ) {
|
||||
// Mark parent group dirty
|
||||
_editGroup.Db.Dirty.Add(_editGroup.Group.ParentGroup);
|
||||
_editGroup.App.DirtyGroups.Add(_editGroup.Group.ParentGroup);
|
||||
} else
|
||||
{
|
||||
_editGroup._app.LockDatabase(false);
|
||||
_editGroup._app.Lock(false);
|
||||
}
|
||||
|
||||
base.Run();
|
||||
|
@@ -21,6 +21,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using keepass2android.database.edit;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
@@ -36,20 +37,25 @@ namespace keepass2android
|
||||
private readonly bool _rememberKeyfile;
|
||||
IDatabaseFormat _format;
|
||||
|
||||
public LoadDb(Activity activity, IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish): base(activity, finish)
|
||||
public LoadDb(Activity activity, IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish, bool updateLastUsageTimestamp, bool makeCurrent): base(activity, finish)
|
||||
{
|
||||
_app = app;
|
||||
_ioc = ioc;
|
||||
_databaseData = databaseData;
|
||||
_compositeKey = compositeKey;
|
||||
_keyfileOrProvider = keyfileOrProvider;
|
||||
_updateLastUsageTimestamp = updateLastUsageTimestamp;
|
||||
_makeCurrent = makeCurrent;
|
||||
|
||||
|
||||
_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile);
|
||||
_rememberKeyfile = app.GetBooleanPreference(PreferenceKey.remember_keyfile);
|
||||
}
|
||||
|
||||
|
||||
public override void Run()
|
||||
|
||||
protected bool success = false;
|
||||
private bool _updateLastUsageTimestamp;
|
||||
private readonly bool _makeCurrent;
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -78,6 +84,10 @@ namespace keepass2android
|
||||
//ok, try to load the database. Let's start with Kdbx format and retry later if that is the wrong guess:
|
||||
_format = new KdbxDatabaseFormat(KdbpFile.GetFormatToUse(_ioc));
|
||||
TryLoad(databaseStream);
|
||||
|
||||
|
||||
|
||||
success = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -89,7 +99,7 @@ namespace keepass2android
|
||||
{
|
||||
Kp2aLog.Log("KeyFileException");
|
||||
Finish(false, /*TODO Localize: use Keepass error text KPRes.KeyFileError (including "or invalid format")*/
|
||||
_app.GetResourceString(UiStringKey.keyfile_does_not_exist), Exception);
|
||||
_app.GetResourceString(UiStringKey.keyfile_does_not_exist), false, Exception);
|
||||
}
|
||||
catch (AggregateException e)
|
||||
{
|
||||
@@ -100,20 +110,20 @@ namespace keepass2android
|
||||
// Override the message shown with the last (hopefully most recent) inner exception
|
||||
Kp2aLog.LogUnexpectedError(innerException);
|
||||
}
|
||||
Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + message, Exception);
|
||||
Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + message, false, Exception);
|
||||
return;
|
||||
}
|
||||
catch (DuplicateUuidsException e)
|
||||
{
|
||||
Kp2aLog.Log(e.ToString());
|
||||
Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError) + " " + e.Message + _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional), Exception);
|
||||
Finish(false, _app.GetResourceString(UiStringKey.DuplicateUuidsError) + " " + e.Message + _app.GetResourceString(UiStringKey.DuplicateUuidsErrorAdditional), false, Exception);
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!(e is InvalidCompositeKeyException))
|
||||
Kp2aLog.LogUnexpectedError(e);
|
||||
Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message, Exception);
|
||||
Finish(false, _app.GetResourceString(UiStringKey.ErrorOcurred) + " " + e.Message, false, Exception);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,7 +135,7 @@ namespace keepass2android
|
||||
/// </summary>
|
||||
public Exception Exception { get; set; }
|
||||
|
||||
private void TryLoad(MemoryStream databaseStream)
|
||||
Database TryLoad(MemoryStream databaseStream)
|
||||
{
|
||||
//create a copy of the stream so we can try again if we get an exception which indicates we should change parameters
|
||||
//This is not optimal in terms of (short-time) memory usage but is hard to avoid because the Keepass library closes streams also in case of errors.
|
||||
@@ -138,19 +148,16 @@ namespace keepass2android
|
||||
//now let's go:
|
||||
try
|
||||
{
|
||||
_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format);
|
||||
Database newDb = _app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format, _makeCurrent);
|
||||
Kp2aLog.Log("LoadDB OK");
|
||||
|
||||
//make sure the stored access time for the actual file is more recent than that of its backup
|
||||
Thread.Sleep(10);
|
||||
SaveFileData(_ioc, _keyfileOrProvider);
|
||||
|
||||
Finish(true, _format.SuccessMessage);
|
||||
return newDb;
|
||||
}
|
||||
catch (OldFormatException)
|
||||
{
|
||||
_format = new KdbDatabaseFormat(_app);
|
||||
TryLoad(databaseStream);
|
||||
return TryLoad(databaseStream);
|
||||
}
|
||||
catch (InvalidCompositeKeyException)
|
||||
{
|
||||
@@ -162,7 +169,7 @@ namespace keepass2android
|
||||
//retry without password:
|
||||
_compositeKey.RemoveUserKey(passwordKey);
|
||||
//retry:
|
||||
TryLoad(databaseStream);
|
||||
return TryLoad(databaseStream);
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
@@ -175,7 +182,7 @@ namespace keepass2android
|
||||
{
|
||||
keyfileOrProvider = "";
|
||||
}
|
||||
_app.StoreOpenedFileAsRecent(ioc, keyfileOrProvider);
|
||||
_app.StoreOpenedFileAsRecent(ioc, keyfileOrProvider, _updateLastUsageTimestamp);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Android.App;
|
||||
@@ -50,10 +51,19 @@ namespace keepass2android.database.edit
|
||||
|
||||
}
|
||||
|
||||
HashSet<Database> removeDatabases = new HashSet<Database>();
|
||||
Database addDatabase = _app.FindDatabaseForElement(_targetGroup);
|
||||
if (addDatabase == null)
|
||||
{
|
||||
Finish(false, "Did not find target database. Did you lock it?");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var elementToMove in _elementsToMove)
|
||||
{
|
||||
|
||||
_app.GetDb().Dirty.Add(elementToMove.ParentGroup);
|
||||
_app.DirtyGroups.Add(elementToMove.ParentGroup);
|
||||
|
||||
|
||||
PwGroup pgParent = elementToMove.ParentGroup;
|
||||
if (pgParent != _targetGroup)
|
||||
@@ -63,8 +73,14 @@ namespace keepass2android.database.edit
|
||||
PwEntry entry = elementToMove as PwEntry;
|
||||
if (entry != null)
|
||||
{
|
||||
var dbRem = _app.FindDatabaseForElement(entry);
|
||||
removeDatabases.Add(dbRem);
|
||||
dbRem.EntriesById.Remove(entry.Uuid);
|
||||
dbRem.Elements.Remove(entry);
|
||||
pgParent.Entries.Remove(entry);
|
||||
_targetGroup.AddEntry(entry, true, true);
|
||||
addDatabase.EntriesById.Add(entry.Uuid, entry);
|
||||
addDatabase.Elements.Add(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -74,27 +90,60 @@ namespace keepass2android.database.edit
|
||||
Finish(false, _app.GetResourceString(UiStringKey.CannotMoveGroupHere));
|
||||
return;
|
||||
}
|
||||
|
||||
var dbRem = _app.FindDatabaseForElement(@group);
|
||||
if (dbRem == null)
|
||||
{
|
||||
Finish(false, "Did not find source database. Did you lock it?");
|
||||
return;
|
||||
}
|
||||
|
||||
dbRem.GroupsById.Remove(group.Uuid);
|
||||
dbRem.Elements.Remove(group);
|
||||
removeDatabases.Add(dbRem);
|
||||
pgParent.Groups.Remove(group);
|
||||
_targetGroup.AddGroup(group, true, true);
|
||||
addDatabase.GroupsById.Add(group.Uuid, group);
|
||||
addDatabase.Elements.Add(group);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
_onFinishToRun = new ActionOnFinish(ActiveActivity, (success, message, activity) =>
|
||||
{
|
||||
if (!success)
|
||||
{ // Let's not bother recovering from a failure.
|
||||
_app.LockDatabase(false);
|
||||
}
|
||||
}, OnFinishToRun);
|
||||
|
||||
// Save
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, false);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
|
||||
|
||||
|
||||
//first save the database where we added the elements
|
||||
var allDatabasesToSave = new List<Database> {addDatabase};
|
||||
//then all databases where we removed elements:
|
||||
removeDatabases.RemoveWhere(db => db == addDatabase);
|
||||
allDatabasesToSave.AddRange(removeDatabases);
|
||||
|
||||
int indexToSave = 0;
|
||||
bool allSavesSuccess = true;
|
||||
void ContinueSave(bool success, string message, Activity activeActivity)
|
||||
{
|
||||
allSavesSuccess &= success;
|
||||
indexToSave++;
|
||||
if (indexToSave == allDatabasesToSave.Count)
|
||||
{
|
||||
OnFinishToRun.SetResult(allSavesSuccess);
|
||||
OnFinishToRun.Run();
|
||||
return;
|
||||
}
|
||||
SaveDb saveDb = new SaveDb(_ctx, _app, allDatabasesToSave[indexToSave], new ActionOnFinish(activeActivity, ContinueSave), false);
|
||||
saveDb.SetStatusLogger(StatusLogger);
|
||||
saveDb.ShowDatabaseIocInStatus = allDatabasesToSave.Count > 1;
|
||||
saveDb.Run();
|
||||
}
|
||||
|
||||
|
||||
SaveDb save = new SaveDb(_ctx, _app, allDatabasesToSave[0], new ActionOnFinish(ActiveActivity, ContinueSave), false);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.ShowDatabaseIocInStatus = allDatabasesToSave.Count > 1;
|
||||
save.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Android;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
@@ -28,8 +29,14 @@ namespace keepass2android
|
||||
protected bool Success;
|
||||
protected String Message;
|
||||
protected Exception Exception;
|
||||
|
||||
protected OnFinish BaseOnFinish;
|
||||
|
||||
protected bool ImportantMessage
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
protected OnFinish BaseOnFinish;
|
||||
protected Handler Handler;
|
||||
private ProgressDialogStatusLogger _statusLogger = new ProgressDialogStatusLogger(); //default: no logging but not null -> can be used whenever desired
|
||||
private Activity _activeActivity;
|
||||
@@ -77,20 +84,22 @@ namespace keepass2android
|
||||
Handler = null;
|
||||
}
|
||||
|
||||
public void SetResult(bool success, string message, Exception exception) {
|
||||
public void SetResult(bool success, string message, bool importantMessage, Exception exception) {
|
||||
Success = success;
|
||||
Message = message;
|
||||
ImportantMessage = importantMessage;
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
public void SetResult(bool success) {
|
||||
|
||||
|
||||
public void SetResult(bool success) {
|
||||
Success = success;
|
||||
}
|
||||
|
||||
public virtual void Run() {
|
||||
if (BaseOnFinish == null) return;
|
||||
// Pass on result on call finish
|
||||
BaseOnFinish.SetResult(Success, Message, Exception);
|
||||
BaseOnFinish.SetResult(Success, Message, ImportantMessage, Exception);
|
||||
|
||||
if ( Handler != null ) {
|
||||
Handler.Post(BaseOnFinish.Run);
|
||||
@@ -100,14 +109,31 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
protected void DisplayMessage(Context ctx) {
|
||||
DisplayMessage(ctx, Message);
|
||||
DisplayMessage(ctx, Message, ImportantMessage);
|
||||
}
|
||||
|
||||
public static void DisplayMessage(Context ctx, string message)
|
||||
public static void DisplayMessage(Context ctx, string message, bool makeDialog)
|
||||
{
|
||||
if ( !String.IsNullOrEmpty(message) ) {
|
||||
Kp2aLog.Log("OnFinish message: "+message);
|
||||
Toast.MakeText(ctx ?? Application.Context, message, ToastLength.Long).Show();
|
||||
Kp2aLog.Log("OnFinish message: " + message);
|
||||
if (makeDialog && ctx != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
|
||||
|
||||
builder.SetMessage(message)
|
||||
.SetPositiveButton(Android.Resource.String.Ok, (sender, args) => ((Dialog)sender).Dismiss())
|
||||
.Show();
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Toast.MakeText(ctx, message, ToastLength.Long).Show();
|
||||
}
|
||||
}
|
||||
else
|
||||
Toast.MakeText(ctx ?? Application.Context, message, ToastLength.Long).Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -50,9 +50,9 @@ namespace keepass2android
|
||||
}
|
||||
}
|
||||
|
||||
protected void Finish(bool result, String message, Exception exception = null) {
|
||||
protected void Finish(bool result, String message, bool importantMessage = false, Exception exception = null) {
|
||||
if ( OnFinishToRun != null ) {
|
||||
OnFinishToRun.SetResult(result, message, exception);
|
||||
OnFinishToRun.SetResult(result, message, importantMessage, exception);
|
||||
OnFinishToRun.Run();
|
||||
}
|
||||
}
|
||||
|
@@ -34,7 +34,8 @@ namespace keepass2android
|
||||
|
||||
public class SaveDb : RunnableOnFinish {
|
||||
private readonly IKp2aApp _app;
|
||||
private readonly bool _dontSave;
|
||||
private readonly Database _db;
|
||||
private readonly bool _dontSave;
|
||||
|
||||
/// <summary>
|
||||
/// stream for reading the data from the original file. If this is set to a non-null value, we know we need to sync
|
||||
@@ -43,9 +44,10 @@ namespace keepass2android
|
||||
private readonly Context _ctx;
|
||||
private Thread _workerThread;
|
||||
|
||||
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, bool dontSave)
|
||||
public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish, bool dontSave)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_db = db;
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
_dontSave = dontSave;
|
||||
@@ -59,46 +61,55 @@ namespace keepass2android
|
||||
/// <param name="finish"></param>
|
||||
/// <param name="dontSave"></param>
|
||||
/// <param name="streamForOrigFile">Stream for reading the data from the (changed) original location</param>
|
||||
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, bool dontSave, Stream streamForOrigFile)
|
||||
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, Database db, bool dontSave, Stream streamForOrigFile)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_db = db;
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
_dontSave = dontSave;
|
||||
_streamForOrigFile = streamForOrigFile;
|
||||
}
|
||||
|
||||
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish)
|
||||
public SaveDb(Activity ctx, IKp2aApp app, Database db, OnFinish finish)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
_dontSave = false;
|
||||
_db = db;
|
||||
_dontSave = false;
|
||||
}
|
||||
|
||||
|
||||
public override void Run ()
|
||||
|
||||
public bool ShowDatabaseIocInStatus { get; set; }
|
||||
|
||||
public override void Run ()
|
||||
{
|
||||
|
||||
if (!_dontSave)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_app.GetDb().CanWrite == false)
|
||||
if (_db.CanWrite == false)
|
||||
{
|
||||
//this should only happen if there is a problem in the UI so that the user sees an edit interface.
|
||||
Finish(false,"Cannot save changes. File is read-only!");
|
||||
return;
|
||||
}
|
||||
|
||||
StatusLogger.UpdateMessage(UiStringKey.saving_database);
|
||||
IOConnectionInfo ioc = _app.GetDb().Ioc;
|
||||
string message = _app.GetResourceString(UiStringKey.saving_database);
|
||||
|
||||
if (ShowDatabaseIocInStatus)
|
||||
message += " (" + _app.GetFileStorage(_db.Ioc).GetDisplayName(_db.Ioc) + ")";
|
||||
|
||||
StatusLogger.UpdateMessage(message);
|
||||
|
||||
IOConnectionInfo ioc = _db.Ioc;
|
||||
IFileStorage fileStorage = _app.GetFileStorage(ioc);
|
||||
|
||||
if (_streamForOrigFile == null)
|
||||
{
|
||||
if ((!_app.GetBooleanPreference(PreferenceKey.CheckForFileChangesOnSave))
|
||||
|| (_app.GetDb().KpDatabase.HashOfFileOnDisk == null)) //first time saving
|
||||
|| (_db.KpDatabase.HashOfFileOnDisk == null)) //first time saving
|
||||
{
|
||||
PerformSaveWithoutCheck(fileStorage, ioc);
|
||||
Finish(true);
|
||||
@@ -109,8 +120,8 @@ namespace keepass2android
|
||||
|
||||
if (
|
||||
(_streamForOrigFile != null)
|
||||
|| fileStorage.CheckForFileChangeFast(ioc, _app.GetDb().LastFileVersion) //first try to use the fast change detection
|
||||
|| (FileHashChanged(ioc, _app.GetDb().KpDatabase.HashOfFileOnDisk) == FileHashChange.Changed) //if that fails, hash the file and compare:
|
||||
|| fileStorage.CheckForFileChangeFast(ioc, _db.LastFileVersion) //first try to use the fast change detection
|
||||
|| (FileHashChanged(ioc, _db.KpDatabase.HashOfFileOnDisk) == FileHashChange.Changed) //if that fails, hash the file and compare:
|
||||
)
|
||||
{
|
||||
|
||||
@@ -128,6 +139,7 @@ namespace keepass2android
|
||||
//small.
|
||||
MergeIn(fileStorage, ioc);
|
||||
PerformSaveWithoutCheck(fileStorage, ioc);
|
||||
_db.UpdateGlobals();
|
||||
Finish(true);
|
||||
};
|
||||
RunInWorkerThread(runHandler);
|
||||
@@ -217,13 +229,13 @@ namespace keepass2android
|
||||
StatusLogger.UpdateSubMessage(_app.GetResourceString(UiStringKey.SynchronizingDatabase));
|
||||
|
||||
PwDatabase pwImp = new PwDatabase();
|
||||
PwDatabase pwDatabase = _app.GetDb().KpDatabase;
|
||||
PwDatabase pwDatabase = _db.KpDatabase;
|
||||
pwImp.New(new IOConnectionInfo(), pwDatabase.MasterKey);
|
||||
pwImp.MemoryProtection = pwDatabase.MemoryProtection.CloneDeep();
|
||||
pwImp.MasterKey = pwDatabase.MasterKey;
|
||||
var stream = GetStreamForBaseFile(fileStorage, ioc);
|
||||
|
||||
_app.GetDb().DatabaseFormat.PopulateDatabaseFromStream(pwImp, stream, null);
|
||||
_db.DatabaseFormat.PopulateDatabaseFromStream(pwImp, stream, null);
|
||||
|
||||
|
||||
pwDatabase.MergeIn(pwImp, PwMergeMethod.Synchronize, null);
|
||||
@@ -249,8 +261,8 @@ namespace keepass2android
|
||||
private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc)
|
||||
{
|
||||
StatusLogger.UpdateSubMessage("");
|
||||
_app.GetDb().SaveData();
|
||||
_app.GetDb().LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
|
||||
_db.SaveData();
|
||||
_db.LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
|
||||
}
|
||||
|
||||
public byte[] HashOriginalFile(IOConnectionInfo iocFile)
|
||||
|
@@ -52,7 +52,7 @@ namespace keepass2android
|
||||
public override void Run ()
|
||||
{
|
||||
StatusLogger.UpdateMessage(UiStringKey.SettingPassword);
|
||||
PwDatabase pm = _app.GetDb().KpDatabase;
|
||||
PwDatabase pm = _app.CurrentDb.KpDatabase;
|
||||
CompositeKey newKey = new CompositeKey ();
|
||||
if (String.IsNullOrEmpty (_password) == false) {
|
||||
newKey.AddUserKey (new KcpPassword (_password));
|
||||
@@ -74,7 +74,7 @@ namespace keepass2android
|
||||
|
||||
// Save Database
|
||||
_onFinishToRun = new AfterSave(ActiveActivity, previousKey, previousMasterKeyChanged, pm, OnFinishToRun);
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave);
|
||||
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun, _dontSave);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ namespace keepass2android
|
||||
|
||||
public override void Run() {
|
||||
// Commit to disk
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun);
|
||||
SaveDb save = new SaveDb(_ctx, _app, _app.CurrentDb, OnFinishToRun);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace keepass2android
|
||||
if ( parent != null ) {
|
||||
|
||||
// Mark parent group dirty
|
||||
_app.GetDb().Dirty.Add(parent);
|
||||
_app.DirtyGroups.Add(parent);
|
||||
|
||||
}
|
||||
|
||||
|
@@ -63,6 +63,8 @@
|
||||
<TransformFile Include="Transforms\EnumMethods.xml" />
|
||||
<TransformFile Include="Transforms\Metadata.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildExtensionsPath)\Novell\Xamarin.Android.Bindings.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Bindings.targets" />
|
||||
<ItemGroup>
|
||||
<Folder Include="libs\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
48
src/PCloudBindings/Additions/AboutAdditions.txt
Normal file
48
src/PCloudBindings/Additions/AboutAdditions.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
Additions allow you to add arbitrary C# to the generated classes
|
||||
before they are compiled. This can be helpful for providing convenience
|
||||
methods or adding pure C# classes.
|
||||
|
||||
== Adding Methods to Generated Classes ==
|
||||
|
||||
Let's say the library being bound has a Rectangle class with a constructor
|
||||
that takes an x and y position, and a width and length size. It will look like
|
||||
this:
|
||||
|
||||
public partial class Rectangle
|
||||
{
|
||||
public Rectangle (int x, int y, int width, int height)
|
||||
{
|
||||
// JNI bindings
|
||||
}
|
||||
}
|
||||
|
||||
Imagine we want to add a constructor to this class that takes a Point and
|
||||
Size structure instead of 4 ints. We can add a new file called Rectangle.cs
|
||||
with a partial class containing our new method:
|
||||
|
||||
public partial class Rectangle
|
||||
{
|
||||
public Rectangle (Point location, Size size) :
|
||||
this (location.X, location.Y, size.Width, size.Height)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
At compile time, the additions class will be added to the generated class
|
||||
and the final assembly will a Rectangle class with both constructors.
|
||||
|
||||
|
||||
== Adding C# Classes ==
|
||||
|
||||
Another thing that can be done is adding fully C# managed classes to the
|
||||
generated library. In the above example, let's assume that there isn't a
|
||||
Point class available in Java or our library. The one we create doesn't need
|
||||
to interact with Java, so we'll create it like a normal class in C#.
|
||||
|
||||
By adding a Point.cs file with this class, it will end up in the binding library:
|
||||
|
||||
public class Point
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
}
|
24
src/PCloudBindings/Jars/AboutJars.txt
Normal file
24
src/PCloudBindings/Jars/AboutJars.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
This directory is for Android .jars.
|
||||
|
||||
There are 2 types of jars that are supported:
|
||||
|
||||
== Input Jar ==
|
||||
|
||||
This is the jar that bindings should be generated for.
|
||||
|
||||
For example, if you were binding the Google Maps library, this would
|
||||
be Google's "maps.jar".
|
||||
|
||||
Set the build action for these jars in the properties page to "InputJar".
|
||||
|
||||
|
||||
== Reference Jars ==
|
||||
|
||||
These are jars that are referenced by the input jar. C# bindings will
|
||||
not be created for these jars. These jars will be used to resolve
|
||||
types used by the input jar.
|
||||
|
||||
NOTE: Do not add "android.jar" as a reference jar. It will be added automatically
|
||||
based on the Target Framework selected.
|
||||
|
||||
Set the build action for these jars in the properties page to "ReferenceJar".
|
BIN
src/PCloudBindings/Jars/pcloud-sdk-android-1.0.1.aar
Normal file
BIN
src/PCloudBindings/Jars/pcloud-sdk-android-1.0.1.aar
Normal file
Binary file not shown.
BIN
src/PCloudBindings/Jars/pcloud-sdk-java-core-1.0.1.jar
Normal file
BIN
src/PCloudBindings/Jars/pcloud-sdk-java-core-1.0.1.jar
Normal file
Binary file not shown.
90
src/PCloudBindings/PCloudBindings.csproj
Normal file
90
src/PCloudBindings/PCloudBindings.csproj
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{2DB80C77-D46F-4970-B967-E9FFA9B2AC2E}</ProjectGuid>
|
||||
|
||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
|
||||
|
||||
|
||||
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>PCloudBindings</RootNamespace>
|
||||
<AssemblyName>PCouldBindings</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
|
||||
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>0</WarningLevel>
|
||||
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoNet|AnyCPU'">
|
||||
<OutputPath>bin\ReleaseNoNet\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="System" />
|
||||
|
||||
<Reference Include="System.Core" />
|
||||
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
<None Include="Jars\AboutJars.txt" />
|
||||
<None Include="Additions\AboutAdditions.txt" />
|
||||
<LibraryProjectZip Include="Jars\pcloud-sdk-android-1.0.1.aar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TransformFile Include="Transforms\Metadata.xml" />
|
||||
<TransformFile Include="Transforms\EnumFields.xml" />
|
||||
<TransformFile Include="Transforms\EnumMethods.xml" />
|
||||
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Bindings.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\pcloud-sdk-java-core-1.0.1.jar" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
30
src/PCloudBindings/Properties/AssemblyInfo.cs
Normal file
30
src/PCloudBindings/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Android.App;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("PCloudBindings")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("PCloudBindings")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
14
src/PCloudBindings/Transforms/EnumFields.xml
Normal file
14
src/PCloudBindings/Transforms/EnumFields.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<enum-field-mappings>
|
||||
<!--
|
||||
This example converts the constants Fragment_id, Fragment_name,
|
||||
and Fragment_tag from android.support.v4.app.FragmentActivity.FragmentTag
|
||||
to an enum called Android.Support.V4.App.FragmentTagType with values
|
||||
Id, Name, and Tag.
|
||||
|
||||
<mapping jni-class="android/support/v4/app/FragmentActivity$FragmentTag" clr-enum-type="Android.Support.V4.App.FragmentTagType">
|
||||
<field jni-name="Fragment_name" clr-name="Name" value="0" />
|
||||
<field jni-name="Fragment_id" clr-name="Id" value="1" />
|
||||
<field jni-name="Fragment_tag" clr-name="Tag" value="2" />
|
||||
</mapping>
|
||||
-->
|
||||
</enum-field-mappings>
|
13
src/PCloudBindings/Transforms/EnumMethods.xml
Normal file
13
src/PCloudBindings/Transforms/EnumMethods.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<enum-method-mappings>
|
||||
<!--
|
||||
This example changes the Java method:
|
||||
android.support.v4.app.Fragment.SavedState.writeToParcel (int flags)
|
||||
to be:
|
||||
android.support.v4.app.Fragment.SavedState.writeToParcel (Android.OS.ParcelableWriteFlags flags)
|
||||
when bound in C#.
|
||||
|
||||
<mapping jni-class="android/support/v4/app/Fragment.SavedState">
|
||||
<method jni-name="writeToParcel" parameter="flags" clr-enum-type="Android.OS.ParcelableWriteFlags" />
|
||||
</mapping>
|
||||
-->
|
||||
</enum-method-mappings>
|
10
src/PCloudBindings/Transforms/Metadata.xml
Normal file
10
src/PCloudBindings/Transforms/Metadata.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<metadata>
|
||||
<!--
|
||||
This sample removes the class: android.support.v4.content.AsyncTaskLoader.LoadTask:
|
||||
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='AsyncTaskLoader.LoadTask']" />
|
||||
|
||||
This sample removes the method: android.support.v4.content.CursorLoader.loadInBackground:
|
||||
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='CursorLoader']/method[@name='loadInBackground']" />
|
||||
-->
|
||||
<remove-node path="/api/package[@name='com.pcloud.sdk']" />
|
||||
</metadata>
|
@@ -53,8 +53,8 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<LibraryProjectZip Include="..\java\Keepass2AndroidPluginSDK2\app\build\outputs\aar\app-release.aar">
|
||||
<Link>Jars\app-release.aar</Link>
|
||||
<LibraryProjectZip Include="..\java\Keepass2AndroidPluginSDK2\app\build\outputs\aar\Keepass2AndroidPluginSDK2-release.aar">
|
||||
<Link>Jars\Keepass2AndroidPluginSDK2-release.aar</Link>
|
||||
</LibraryProjectZip>
|
||||
<None Include="Jars\AboutJars.txt" />
|
||||
<None Include="Additions\AboutAdditions.txt" />
|
||||
|
20
src/build-scripts/build-all.sh
Executable file
20
src/build-scripts/build-all.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo '*****************************************'
|
||||
echo '********** Building Java parts **********'
|
||||
echo '*****************************************'
|
||||
./build-java.sh
|
||||
|
||||
echo '*****************************************'
|
||||
echo '******** Building Xamarin parts *********'
|
||||
echo '*****************************************'
|
||||
./build-xamarin.sh
|
||||
|
||||
echo '*****************************************'
|
||||
echo '************** Building APK *************'
|
||||
echo '*****************************************'
|
||||
./build-apk.sh
|
||||
|
||||
echo
|
||||
echo 'Congratulations! You you can find the target APK in src/keepass2android/bin/Debug/.'
|
8
src/build-scripts/build-apk.sh
Executable file
8
src/build-scripts/build-apk.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
pushd ../keepass2android
|
||||
|
||||
xabuild keepass2android.csproj /t:SignAndroidPackage "$@"
|
||||
|
||||
popd
|
@@ -7,4 +7,7 @@ call gradlew assemble
|
||||
cd ..\Keepass2AndroidPluginSDK2
|
||||
call gradlew assemble
|
||||
|
||||
cd ..\..\build-scripts
|
||||
cd ..\PluginQR
|
||||
call gradlew assemble
|
||||
|
||||
cd ..\..\build-scripts
|
||||
|
22
src/build-scripts/build-java.sh
Executable file
22
src/build-scripts/build-java.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
pushd ../java/
|
||||
|
||||
pushd JavaFileStorageTest-AS
|
||||
./gradlew assemble
|
||||
popd
|
||||
|
||||
pushd KP2ASoftkeyboard_AS
|
||||
./gradlew assemble
|
||||
popd
|
||||
|
||||
pushd Keepass2AndroidPluginSDK2
|
||||
./gradlew assemble
|
||||
popd
|
||||
|
||||
pushd PluginQR
|
||||
./gradlew assemble
|
||||
popd
|
||||
|
||||
popd
|
@@ -6,11 +6,11 @@ if exist "DropboxFileStorageKeys.cs" (
|
||||
)
|
||||
|
||||
cd ..\..\keepass2android
|
||||
call UseManifestNoNet.bat
|
||||
call UseManifestDebug.bat
|
||||
cd ..
|
||||
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64
|
||||
|
||||
msbuild KeePass.sln /target:keepass2android /p:BuildProjectReferences=true /p:Configuration="Release" /p:Platform="Any CPU"
|
||||
msbuild KeePass.sln /target:keepass2android /p:BuildProjectReferences=true /p:Configuration="Debug" /p:Platform="Any CPU"
|
||||
|
||||
cd build-scripts
|
25
src/build-scripts/build-xamarin.sh
Executable file
25
src/build-scripts/build-xamarin.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
pushd ..
|
||||
|
||||
pushd Kp2aBusinessLogic/Io
|
||||
|
||||
if [ -f "DropboxFileStorageKeys.cs" ]
|
||||
then
|
||||
echo "DropboxFileStorageKeys.cs found."
|
||||
else
|
||||
cp DropboxFileStorageKeysDummy.cs DropboxFileStorageKeys.cs
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
pushd keepass2android
|
||||
./UseManifestDebug.sh
|
||||
popd
|
||||
|
||||
# call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64
|
||||
|
||||
xabuild KeePass.sln /target:keepass2android /p:BuildProjectReferences=true /p:Configuration="Debug" /p:Platform="Any CPU" "$@"
|
||||
|
||||
popd
|
55
src/build.readme.md
Normal file
55
src/build.readme.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# 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.
|
||||
The current build-scripts assume that the native libraries are already built (they are included in the repo).
|
||||
|
||||
To build KP2A from scratch, make sure that you have Xamarin's Mono for Android installed and also install Android Studio. Make sure that both point to the same Android SDK location.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Install Xamarin.Android
|
||||
- Fetch all submodules (`git submodule init && git submodule update`)
|
||||
|
||||
## Build
|
||||
|
||||
### On Windows
|
||||
|
||||
```bat
|
||||
cd build-scripts
|
||||
build-java.bat
|
||||
build-xamarin.bat
|
||||
```
|
||||
|
||||
build-java.bat will call gradlew for several Java modules. build-xamarin.bat will first make sure that you have all files at their place. (There is a "secret" file for Dropbox SDK keys which is not in the repo, this is replaced with a dummy file. There are also different Android Manifest files depending on the configuration which is selected by calling the appropriate script.)
|
||||
|
||||
**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 Linux
|
||||
|
||||
- Install [Mono](https://www.mono-project.com/)
|
||||
- Install Xamarin.Android
|
||||
- Option 1: Use the mono-project [CI builds](https://jenkins.mono-project.com/view/Xamarin.Android/job/xamarin-android-linux/lastSuccessfulBuild/Azure/)
|
||||
- Option 2: [Build it from source](https://github.com/xamarin/xamarin-android/blob/master/Documentation/README.md#building-from-source)
|
||||
- Setup your environment:
|
||||
- Add `xabuild` to your path: `export PATH=/path/to/xamarin.android-oss/bin/Release/bin/:$PATH`
|
||||
- Setup your `ANDROID_HOME` if it's not already: `export ANDROID_HOME=/path/to/android/`
|
||||
- Alternatively, you can set your `ANDROID_SDK_PATH` and `ANDROID_NDK_PATH`.
|
||||
- Build [jar2xml](https://github.com/xamarin/jar2xml) and copy `jar2xml.jar` to `/path/to/xamarin.android-oss/bin/Release/lib/xamarin.android/xbuild/Xamarin/Android/`
|
||||
- Install [libzip](https://libzip.org/) for your distribution.
|
||||
- Note: Xamarin seems to require `libzip4`, yet most distributions only ships `libzip5`. As a dirty workaround, it's possible to symlink `libzip.so.5` to `libzip.so.4`. Luckily, it appears to be working.
|
||||
- `sudo ln -s /usr/lib/libzip.so.5 /usr/lib/libzip.so.4`
|
||||
- Install NuGet dependencies:
|
||||
- `cd src/ && nuget restore KeePass.sln`
|
||||
- Build:
|
||||
- Option 1: `cd build-scripts && ./build-all.sh`
|
||||
- Option 2:
|
||||
- Build the Java parts: `cd build-scripts/ && ./build-java.sh`
|
||||
- Build the Xamarin parts: `./build-xamarin.sh`
|
||||
- Build the signed APK: `./build-apk.sh`
|
||||
- Enjoy:
|
||||
- `adb install ../keepass2android/bin/Debug/keepass2android.keepass2android_debug-Signed.apk`
|
@@ -1,23 +0,0 @@
|
||||
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.
|
||||
The current build-scripts assume that the native libraries are already built (they are included in the repo).
|
||||
|
||||
To build KP2A from scratch, make sure that you have Xamarin's Mono for Android installed and also install Android Studio. Make sure that both point to the same Android SDK location.
|
||||
|
||||
On Windows you can use
|
||||
|
||||
cd build-scripts
|
||||
build-java.bat
|
||||
build-xamarin.bat
|
||||
|
||||
build-java.bat will call gradlew for several Java modules. build-xamarin.bat will first make sure that you have all files at their place. (There is a "secret" file for Dropbox SDK keys which is not in the repo, this is replaced with a dummy file. There are also different Android Manifest files depending on the configuration which is selected by calling the appropriate script.)
|
||||
|
||||
|
||||
* Notes *
|
||||
- Please don't forget to update the git submodules before building.
|
||||
- For building the java parts on Windows, 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.
|
||||
|
||||
|
@@ -35,6 +35,12 @@ dependencies {
|
||||
compile('com.onedrive.sdk:onedrive-sdk-android:1.2.0') {
|
||||
transitive = false
|
||||
}
|
||||
compile 'com.pcloud.sdk:java-core:1.0.1'
|
||||
compile 'com.pcloud.sdk:android:1.0.1'
|
||||
compile('com.microsoft.graph:msgraph-sdk-android:1.2.+')
|
||||
compile ('com.microsoft.identity.client:msal:0.1.+') {
|
||||
exclude group: 'com.android.support', module: 'appcompat-v7'
|
||||
}
|
||||
compile 'com.google.code.gson:gson:2.3.1'
|
||||
compile 'com.microsoft.services.msa:msa-auth:0.8.6'
|
||||
compile 'com.microsoft.aad:adal:1.14.0'
|
||||
|
@@ -1,15 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="keepass2android.javafilestorage"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="8"
|
||||
android:targetSdkVersion="14" />
|
||||
|
||||
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
|
||||
</manifest>
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name=".NotifSlave"
|
||||
android:label="Keepass2Android"></activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
@@ -190,12 +190,14 @@ public class GoogleDriveFileStorage extends JavaFileStorageBase {
|
||||
String part = parts[parts.length-1];
|
||||
logDebug("parsing part " + part);
|
||||
int indexOfSeparator = part.lastIndexOf(NAME_ID_SEP);
|
||||
String name = "";
|
||||
if (indexOfSeparator < 0)
|
||||
{
|
||||
//seems invalid, but we're very generous here
|
||||
displayName += "/"+part;
|
||||
}
|
||||
String name = part.substring(0, indexOfSeparator);
|
||||
else
|
||||
name = part.substring(0, indexOfSeparator);
|
||||
try {
|
||||
name = decode(name);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
|
@@ -132,7 +132,7 @@ public class FileEntry {
|
||||
|
||||
public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception;
|
||||
|
||||
public String getCurrentFileVersionFast(String path);
|
||||
public String getCurrentFileVersionFast(String path) throws Exception;
|
||||
|
||||
public InputStream openFileForRead(String path) throws Exception;
|
||||
|
||||
@@ -157,4 +157,4 @@ public class FileEntry {
|
||||
public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data);
|
||||
public void onRequestPermissionsResult(FileStorageSetupActivity activity, int requestCode, String[] permissions, int[] grantResults);
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,169 @@
|
||||
package keepass2android.javafilestorage;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
//based on https://github.com/jwise/dumload/blob/master/src/com/joshuawise/dumload/NotifSlave.java
|
||||
public class NotifSlave extends Activity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
private void say(String s) {
|
||||
Toast.makeText(getApplicationContext(), s, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private int _nextdialog = 0;
|
||||
private Dialog dialog = null;
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id)
|
||||
{
|
||||
Log.e("KP2AJ.NotifSlave", "Create for dialog "+(Integer.toString(id)));
|
||||
if (id != _nextdialog)
|
||||
return null;
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private void showDialog(Dialog d)
|
||||
{
|
||||
_nextdialog++;
|
||||
dialog = d;
|
||||
Log.e("KP2AJ.NotifSlave", "Attempting to show dialog "+(Integer.toString(_nextdialog)));
|
||||
showDialog(_nextdialog);
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
Intent i = getIntent(); /* i *am* not an intent! */
|
||||
final Activity thisact = this;
|
||||
|
||||
final Messenger m = (Messenger)i.getParcelableExtra("keepass2android.sftp.returnmessenger");
|
||||
String reqtype = i.getStringExtra("keepass2android.sftp.reqtype");
|
||||
String prompt = i.getStringExtra("keepass2android.sftp.prompt");
|
||||
|
||||
if (prompt == null || reqtype == null || m == null) /* i.e., we got called by a dummy notification */
|
||||
{
|
||||
this.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (reqtype.equals("yesno")) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("Keepass2Android");
|
||||
builder.setMessage(prompt);
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
Log.e("KP2AJ.NotifSlave", "Responding with a 1.");
|
||||
try {
|
||||
Message me = Message.obtain();
|
||||
me.arg1 = 1;
|
||||
m.send(me);
|
||||
} catch (Exception e) {
|
||||
Log.e("KP2AJ.NotifSlave", "Failed to send a message back to my buddy.");
|
||||
}
|
||||
dialog.cancel();
|
||||
thisact.finish();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
Log.e("KP2AJ.NotifSlave", "Responding with a 1.");
|
||||
try {
|
||||
Message me = Message.obtain();
|
||||
me.arg1 = 0;
|
||||
m.send(me);
|
||||
} catch (Exception e) {
|
||||
Log.e("KP2AJ.NotifSlave", "Failed to send a message back to my buddy.");
|
||||
}
|
||||
dialog.cancel();
|
||||
thisact.finish();
|
||||
}
|
||||
});
|
||||
AlertDialog alert = builder.create();
|
||||
showDialog(alert);
|
||||
} else if (reqtype.equals("message")) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("Keepass2Android");
|
||||
builder.setMessage(prompt);
|
||||
builder.setCancelable(false);
|
||||
builder.setNeutralButton("OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
try {
|
||||
Message me = Message.obtain();
|
||||
m.send(me);
|
||||
} catch (Exception e) {
|
||||
Log.e("KP2AJ.NotifSlave", "Failed to send a message back to my buddy.");
|
||||
}
|
||||
dialog.cancel();
|
||||
thisact.finish();
|
||||
}
|
||||
});
|
||||
AlertDialog alert = builder.create();
|
||||
showDialog(alert);
|
||||
} /*else if (reqtype.equals("password")) {
|
||||
final Dialog d = new Dialog(this);
|
||||
|
||||
d.setContentView(R.layout.notfif_slave);
|
||||
d.setTitle("Keepass2Android");
|
||||
d.setCancelable(false);
|
||||
|
||||
TextView text = (TextView) d.findViewById(R.id.prompt);
|
||||
text.setText(prompt);
|
||||
|
||||
Button ok = (Button) d.findViewById(R.id.ok);
|
||||
ok.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
try {
|
||||
Message me = Message.obtain();
|
||||
me.arg1 = 1;
|
||||
TextView entry = (TextView) d.findViewById(R.id.entry);
|
||||
Bundle b = new Bundle(1);
|
||||
b.putString("response", entry.getText().toString());
|
||||
me.setData(b);
|
||||
m.send(me);
|
||||
} catch (Exception e) {
|
||||
Log.e("KP2AJ.NotifSlave", "Failed to send a message back to my buddy.");
|
||||
}
|
||||
d.cancel();
|
||||
thisact.finish();
|
||||
}
|
||||
});
|
||||
|
||||
Button cancel = (Button) d.findViewById(R.id.cancel);
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
try {
|
||||
Message me = Message.obtain();
|
||||
me.arg1 = 0;
|
||||
m.send(me);
|
||||
} catch (Exception e) {
|
||||
Log.e("KP2AJ.NotifSlave", "Failed to send a message back to my buddy.");
|
||||
}
|
||||
d.cancel();
|
||||
thisact.finish();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
showDialog(d);
|
||||
} */else {
|
||||
Log.e("KP2AJ.NotifSlave", "What's a "+reqtype+"?");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,486 @@
|
||||
package keepass2android.javafilestorage;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
import com.microsoft.graph.core.ClientException;
|
||||
import com.microsoft.graph.core.DefaultClientConfig;
|
||||
import com.microsoft.graph.core.GraphErrorCodes;
|
||||
import com.microsoft.graph.extensions.DriveItem;
|
||||
import com.microsoft.graph.extensions.GraphServiceClient;
|
||||
import com.microsoft.graph.extensions.IDriveItemCollectionPage;
|
||||
import com.microsoft.graph.extensions.IDriveItemCollectionRequestBuilder;
|
||||
import com.microsoft.graph.extensions.IDriveItemRequest;
|
||||
import com.microsoft.graph.extensions.IDriveItemRequestBuilder;
|
||||
import com.microsoft.graph.extensions.IGraphServiceClient;
|
||||
import com.microsoft.identity.client.AuthenticationCallback;
|
||||
import com.microsoft.identity.client.AuthenticationResult;
|
||||
import com.microsoft.identity.client.MsalException;
|
||||
import com.microsoft.identity.client.PublicClientApplication;
|
||||
import com.microsoft.identity.client.User;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import keepass2android.javafilestorage.onedrive2.GraphServiceClientManager;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Philipp on 20.11.2016.
|
||||
*/
|
||||
public class OneDriveStorage2 extends JavaFileStorageBase
|
||||
{
|
||||
PublicClientApplication mPublicClientApp;
|
||||
|
||||
final HashMap<String /*userid*/, IGraphServiceClient> mClientByUser = new HashMap<String /*userid*/, IGraphServiceClient>();
|
||||
|
||||
private static final String[] scopes = {"openid","offline_access", "https://graph.microsoft.com/Files.ReadWrite","https://graph.microsoft.com/User.Read"};
|
||||
|
||||
|
||||
public OneDriveStorage2(final Activity context, final String clientId) {
|
||||
|
||||
mPublicClientApp = new PublicClientApplication(context, clientId);
|
||||
initAuthenticator(context);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean requiresSetup(String path)
|
||||
{
|
||||
|
||||
return !isConnected(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave, int requestCode) {
|
||||
|
||||
initAuthenticator((Activity)activity.getActivity());
|
||||
|
||||
String path = getProtocolId()+":///";
|
||||
Log.d("KP2AJ", "startSelectFile "+path+", connected: "+path);
|
||||
if (isConnected(null))
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_IS_FOR_SAVE, isForSave);
|
||||
intent.putExtra(EXTRA_PATH, path);
|
||||
activity.onImmediateResult(requestCode, RESULT_FILECHOOSER_PREPARED, intent);
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.startSelectFileProcess(path, isForSave, requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isConnected(String path) {
|
||||
try {
|
||||
if (tryGetMsGraphClient(path) == null)
|
||||
try {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
Log.d("KP2AJ", "trying silent login");
|
||||
|
||||
String userId = extractUserId(path);
|
||||
final MsalException[] _exception = {null};
|
||||
final AuthenticationResult[] _result = {null};
|
||||
User user = mPublicClientApp.getUser(userId);
|
||||
mPublicClientApp.acquireTokenSilentAsync(scopes, user,
|
||||
new AuthenticationCallback() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(AuthenticationResult authenticationResult) {
|
||||
_result[0] = authenticationResult;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(MsalException exception) {
|
||||
_exception[0] = exception;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
latch.countDown();
|
||||
|
||||
}
|
||||
});
|
||||
latch.await();
|
||||
if (_result[0] != null) {
|
||||
buildClient(_result[0]);
|
||||
} else if (_exception[0] != null){
|
||||
_exception[0].printStackTrace();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return tryGetMsGraphClient(path) != null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IGraphServiceClient tryGetMsGraphClient(String path) throws Exception
|
||||
{
|
||||
String userId = extractUserId(path);
|
||||
if (mClientByUser.containsKey(userId))
|
||||
return mClientByUser.get(userId);
|
||||
return null;
|
||||
}
|
||||
|
||||
private String extractUserId(String path) throws Exception {
|
||||
String pathWithoutProtocol = removeProtocol(path);
|
||||
String[] parts = pathWithoutProtocol.split("/",1);
|
||||
if (parts.length != 2 || ("".equals(parts[0])))
|
||||
{
|
||||
throw new Exception("path does not contain user");
|
||||
}
|
||||
return parts[0];
|
||||
}
|
||||
|
||||
private void initAuthenticator(Activity activity) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode, boolean alwaysReturnSuccess) {
|
||||
initAuthenticator((Activity)activity.getActivity());
|
||||
if (isConnected(path))
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_PATH, path);
|
||||
activity.onImmediateResult(requestCode, RESULT_FILEUSAGE_PREPARED, intent);
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.startFileUsageProcess(path, requestCode, alwaysReturnSuccess);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocolId() {
|
||||
return "onedrive";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareFileUsage(Context appContext, String path) throws UserInteractionRequiredException {
|
||||
if (!isConnected(null))
|
||||
{
|
||||
throw new UserInteractionRequiredException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState) {
|
||||
|
||||
Log.d("KP2AJ", "OnCreate");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(final FileStorageSetupActivity activity) {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private IGraphServiceClient buildClient(AuthenticationResult authenticationResult) throws InterruptedException {
|
||||
|
||||
IGraphServiceClient newClient = new GraphServiceClient.Builder()
|
||||
.fromConfig(DefaultClientConfig.createWithAuthenticationProvider(new GraphServiceClientManager(authenticationResult.getAccessToken())))
|
||||
.buildClient();
|
||||
mClientByUser.put(authenticationResult.getUser().getUserIdentifier(), newClient);
|
||||
|
||||
return newClient;
|
||||
}
|
||||
|
||||
|
||||
|
||||
String removeProtocol(String path) throws Exception {
|
||||
if (path == null)
|
||||
return null;
|
||||
return path.substring(getProtocolId().length()+3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName(String path) {
|
||||
|
||||
if (path == null)
|
||||
return null;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename(String path) throws Exception {
|
||||
return path.substring(path.lastIndexOf("/")+1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentFileVersionFast(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
class ClientAndPath
|
||||
{
|
||||
public IGraphServiceClient client;
|
||||
public String oneDrivePath;
|
||||
public IDriveItemRequestBuilder getPathItem()
|
||||
{
|
||||
IDriveItemRequestBuilder pathItem = client.getDrive().getRoot();
|
||||
if ("".equals(oneDrivePath) == false) {
|
||||
pathItem = pathItem.getItemWithPath(oneDrivePath);
|
||||
}
|
||||
return pathItem;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openFileForRead(String path) throws Exception {
|
||||
try {
|
||||
ClientAndPath clientAndpath = getOneDriveClientAndPath(path);
|
||||
logDebug("openFileForRead. Path="+path);
|
||||
InputStream result = clientAndpath.client.getDrive()
|
||||
.getRoot()
|
||||
.getItemWithPath(clientAndpath.oneDrivePath)
|
||||
.getContent()
|
||||
.buildRequest()
|
||||
.get();
|
||||
logDebug("ok");
|
||||
return result;
|
||||
|
||||
}
|
||||
catch (ClientException e)
|
||||
{
|
||||
throw convertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ClientAndPath getOneDriveClientAndPath(String path) throws Exception {
|
||||
ClientAndPath result = new ClientAndPath();
|
||||
|
||||
String pathWithoutProtocol = removeProtocol(path);
|
||||
String[] parts = pathWithoutProtocol.split("/",2);
|
||||
if (parts.length != 2 || ("".equals(parts[0])))
|
||||
{
|
||||
throw new Exception("path does not contain user");
|
||||
}
|
||||
result.client = mClientByUser.get(parts[0]);
|
||||
result.oneDrivePath = parts[1];
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private Exception convertException(ClientException e) {
|
||||
if (e.isError(GraphErrorCodes.ItemNotFound))
|
||||
return new FileNotFoundException(e.getMessage());
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadFile(String path, byte[] data, boolean writeTransactional) throws Exception {
|
||||
try {
|
||||
ClientAndPath clientAndPath = getOneDriveClientAndPath(path);
|
||||
clientAndPath.client.getDrive()
|
||||
.getRoot()
|
||||
.getItemWithPath(clientAndPath.oneDrivePath)
|
||||
.getContent()
|
||||
.buildRequest()
|
||||
.put(data);
|
||||
} catch (ClientException e) {
|
||||
throw convertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createFolder(String parentPath, String newDirName) throws Exception {
|
||||
throw new Exception("not implemented.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createFilePath(String parentPath, String newFileName) throws Exception {
|
||||
String path = parentPath;
|
||||
if (!path.endsWith("/"))
|
||||
path = path + "/";
|
||||
path = path + newFileName;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileEntry> listFiles(String parentPath) throws Exception {
|
||||
try {
|
||||
ArrayList<FileEntry> result = new ArrayList<FileEntry>();
|
||||
ClientAndPath clientAndPath = getOneDriveClientAndPath(parentPath);
|
||||
parentPath = clientAndPath.oneDrivePath;
|
||||
|
||||
IDriveItemCollectionPage itemsPage = clientAndPath.getPathItem()
|
||||
.getChildren()
|
||||
.buildRequest()
|
||||
.get();
|
||||
if (parentPath.endsWith("/"))
|
||||
parentPath = parentPath.substring(0,parentPath.length()-1);
|
||||
while (true)
|
||||
{
|
||||
List<DriveItem> items = itemsPage.getCurrentPage();
|
||||
if (items.isEmpty())
|
||||
return result;
|
||||
|
||||
for (DriveItem i: items)
|
||||
{
|
||||
FileEntry e = getFileEntry(parentPath + "/" + i.name, i);
|
||||
Log.d("KP2AJ", e.path);
|
||||
result.add(e);
|
||||
}
|
||||
IDriveItemCollectionRequestBuilder nextPageReqBuilder = itemsPage.getNextPage();
|
||||
if (nextPageReqBuilder == null)
|
||||
return result;
|
||||
itemsPage = nextPageReqBuilder.buildRequest().get();
|
||||
|
||||
}
|
||||
} catch (ClientException e) {
|
||||
throw convertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private FileEntry getFileEntry(String path, DriveItem i) {
|
||||
FileEntry e = new FileEntry();
|
||||
if (i.size != null)
|
||||
e.sizeInBytes = i.size;
|
||||
else if ((i.remoteItem != null) && (i.remoteItem.size != null))
|
||||
e.sizeInBytes = i.remoteItem.size;
|
||||
|
||||
e.displayName = i.name;
|
||||
e.canRead = e.canWrite = true;
|
||||
e.path = getProtocolId() +"://"+path;
|
||||
if (i.lastModifiedDateTime != null)
|
||||
e.lastModifiedTime = i.lastModifiedDateTime.getTimeInMillis();
|
||||
else if ((i.remoteItem != null)&&(i.remoteItem.lastModifiedDateTime != null))
|
||||
e.lastModifiedTime = i.remoteItem.lastModifiedDateTime.getTimeInMillis();
|
||||
e.isDirectory = (i.folder != null) || ((i.remoteItem != null) && (i.remoteItem.folder != null));
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileEntry getFileEntry(String filename) throws Exception {
|
||||
try {
|
||||
|
||||
ClientAndPath clientAndPath = getOneDriveClientAndPath(filename);
|
||||
IDriveItemRequestBuilder pathItem = clientAndPath.getPathItem();
|
||||
|
||||
IDriveItemRequest request = pathItem.buildRequest();
|
||||
DriveItem item = request.get();
|
||||
return getFileEntry(filename, item);
|
||||
} catch (ClientException e) {
|
||||
throw convertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String path) throws Exception {
|
||||
try {
|
||||
ClientAndPath clientAndPath = getOneDriveClientAndPath(path);
|
||||
clientAndPath.client.getDrive()
|
||||
.getRoot()
|
||||
.getItemWithPath(clientAndPath.oneDrivePath)
|
||||
.buildRequest()
|
||||
.delete();
|
||||
} catch (ClientException e) {
|
||||
throw convertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
boolean acquireTokenRunning = false;
|
||||
@Override
|
||||
public void onStart(final FileStorageSetupActivity activity) {
|
||||
Log.d("KP2AJ", "onStart " + activity.getPath());
|
||||
if (activity.getProcessName().equals(PROCESS_NAME_SELECTFILE))
|
||||
activity.getState().putString(EXTRA_PATH, activity.getPath());
|
||||
|
||||
String userId = activity.getState().getString("OneDriveUser");
|
||||
if (mClientByUser.containsKey(userId)) {
|
||||
finishActivityWithSuccess(activity);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
JavaFileStorage.FileStorageSetupActivity storageSetupAct = activity;
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AuthenticationResult[] _authenticationResult = {null};
|
||||
MsalException _exception[] = {null};
|
||||
|
||||
if (!acquireTokenRunning) {
|
||||
acquireTokenRunning = true;
|
||||
|
||||
mPublicClientApp.acquireToken((Activity) activity, scopes, new AuthenticationCallback() {
|
||||
@Override
|
||||
public void onSuccess(AuthenticationResult authenticationResult) {
|
||||
Log.i(TAG, "authenticating successful");
|
||||
|
||||
try {
|
||||
buildClient(authenticationResult);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
activity.getState().putString(EXTRA_PATH, getProtocolPrefix() + authenticationResult.getUser().getUserIdentifier() + "/");
|
||||
|
||||
finishActivityWithSuccess(activity);
|
||||
acquireTokenRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(MsalException exception) {
|
||||
Log.i(TAG, "authenticating not successful");
|
||||
Intent data = new Intent();
|
||||
data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not successful");
|
||||
((Activity) activity).setResult(Activity.RESULT_CANCELED, data);
|
||||
((Activity) activity).finish();
|
||||
acquireTokenRunning = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
|
||||
Log.i(TAG, "authenticating cancelled");
|
||||
Intent data = new Intent();
|
||||
data.putExtra(EXTRA_ERROR_MESSAGE, "authenticating not cancelled");
|
||||
((Activity) activity).setResult(Activity.RESULT_CANCELED, data);
|
||||
((Activity) activity).finish();
|
||||
acquireTokenRunning = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) {
|
||||
mPublicClientApp.handleInteractiveRequestRedirect(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
@@ -0,0 +1,407 @@
|
||||
package keepass2android.javafilestorage;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.pcloud.sdk.ApiClient;
|
||||
import com.pcloud.sdk.ApiError;
|
||||
import com.pcloud.sdk.Authenticators;
|
||||
import com.pcloud.sdk.AuthorizationActivity;
|
||||
import com.pcloud.sdk.AuthorizationResult;
|
||||
import com.pcloud.sdk.Call;
|
||||
import com.pcloud.sdk.DataSource;
|
||||
import com.pcloud.sdk.PCloudSdk;
|
||||
import com.pcloud.sdk.RemoteEntry;
|
||||
import com.pcloud.sdk.RemoteFile;
|
||||
import com.pcloud.sdk.RemoteFolder;
|
||||
|
||||
/**
|
||||
* FileStorage implementation for PCloud provider.
|
||||
* https://www.pcloud.com/
|
||||
*/
|
||||
public class PCloudFileStorage extends JavaFileStorageBase
|
||||
{
|
||||
final static private int PCLOUD_AUTHORIZATION_REQUEST_CODE = 1001845497;
|
||||
|
||||
final static private String SHARED_PREF_NAME = "PCLOUD";
|
||||
final static private String SHARED_PREF_AUTH_TOKEN = "AUTH_TOKEN";
|
||||
|
||||
private final Context ctx;
|
||||
|
||||
private ApiClient apiClient;
|
||||
private String clientId;
|
||||
|
||||
public PCloudFileStorage(Context ctx, String clientId) {
|
||||
this.ctx = ctx;
|
||||
this.clientId = clientId;
|
||||
this.apiClient = createApiClientFromSharedPrefs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresSetup(String path) {
|
||||
return !this.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSelectFile(FileStorageSetupInitiatorActivity activity, boolean isForSave, int requestCode) {
|
||||
String path = getProtocolId() + "://";
|
||||
activity.startSelectFileProcess(path, isForSave, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareFileUsage(Context appContext, String path) throws Throwable {
|
||||
if (!isConnected()) {
|
||||
throw new UserInteractionRequiredException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareFileUsage(FileStorageSetupInitiatorActivity activity, String path, int requestCode,
|
||||
boolean alwaysReturnSuccess) {
|
||||
if (this.isConnected()) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_PATH, path);
|
||||
activity.onImmediateResult(requestCode, RESULT_FILEUSAGE_PREPARED, intent);
|
||||
} else {
|
||||
activity.startFileUsageProcess(path, requestCode, alwaysReturnSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocolId() {
|
||||
return "pcloud";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName(String path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename(String path) {
|
||||
return path.substring(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkForFileChangeFast(String path, String previousFileVersion) throws Exception {
|
||||
if (previousFileVersion == null || "".equals(previousFileVersion)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
path = this.cleanPath(path);
|
||||
|
||||
RemoteFile remoteFile = this.getRemoteFileByPath(path);
|
||||
|
||||
return !remoteFile.hash().equals(previousFileVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentFileVersionFast(String path) throws Exception {
|
||||
path = this.cleanPath(path);
|
||||
|
||||
RemoteFile remoteFile = this.getRemoteFileByPath(path);
|
||||
|
||||
return remoteFile.hash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openFileForRead(String path) throws Exception {
|
||||
path = this.cleanPath(path);
|
||||
|
||||
RemoteFile remoteFile = this.getRemoteFileByPath(path);
|
||||
|
||||
return remoteFile.byteStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadFile(String path, byte[] data, boolean writeTransactional) throws Exception {
|
||||
path = this.cleanPath(path);
|
||||
|
||||
DataSource dataSource = DataSource.create(data);
|
||||
String filename = path.substring(path.lastIndexOf("/") + 1);
|
||||
String filePath = path.substring(0, path.lastIndexOf("/") + 1);
|
||||
RemoteFolder remoteFolder = this.getRemoteFolderByPath(filePath);
|
||||
|
||||
String tempName = "." + UUID.randomUUID().toString();
|
||||
try {
|
||||
RemoteFile remoteFile = this.apiClient.createFile(remoteFolder, tempName, dataSource).execute();
|
||||
this.apiClient.rename(remoteFile, filename).execute();
|
||||
} catch (ApiError e) {
|
||||
throw convertApiError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createFolder(String parentPath, String newDirName) throws Exception {
|
||||
String parentPathWithoutProtocol = this.cleanPath(parentPath);
|
||||
|
||||
RemoteFolder remoteFolder = this.getRemoteFolderByPath(parentPathWithoutProtocol);
|
||||
|
||||
try {
|
||||
this.apiClient.createFolder(remoteFolder, newDirName).execute();
|
||||
} catch (ApiError e) {
|
||||
throw convertApiError(e);
|
||||
}
|
||||
|
||||
return this.createFilePath(parentPath, newDirName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createFilePath(String parentPath, String newFileName) throws Exception {
|
||||
return (
|
||||
this.getProtocolId() + "://" +
|
||||
this.cleanPath(parentPath) +
|
||||
("".equals(newFileName) ? "" : "/") +
|
||||
newFileName
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileEntry> listFiles(String parentPath) throws Exception {
|
||||
parentPath = this.cleanPath(parentPath);
|
||||
|
||||
ArrayList<FileEntry> fileEntries = new ArrayList<>();
|
||||
|
||||
RemoteFolder remoteFolder = this.getRemoteFolderByPath(parentPath);
|
||||
|
||||
for (RemoteEntry remoteEntry : remoteFolder.children()) {
|
||||
fileEntries.add(this.convertRemoteEntryToFileEntry(remoteEntry, parentPath));
|
||||
}
|
||||
|
||||
return fileEntries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileEntry getFileEntry(String path) throws Exception {
|
||||
path = this.cleanPath(path);
|
||||
|
||||
RemoteEntry remoteEntry = this.getRemoteEntryByPath(path);
|
||||
|
||||
return this.convertRemoteEntryToFileEntry(
|
||||
remoteEntry,
|
||||
path.substring(0, path.lastIndexOf("/"))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String path) throws Exception {
|
||||
path = this.cleanPath(path);
|
||||
|
||||
RemoteEntry remoteEntry = this.getRemoteFileByPath(path);
|
||||
|
||||
try {
|
||||
this.apiClient.delete(remoteEntry).execute();
|
||||
} catch (ApiError e) {
|
||||
throw convertApiError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(FileStorageSetupActivity activity, Bundle savedInstanceState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(FileStorageSetupActivity activity) {
|
||||
if (activity.getProcessName().equals(PROCESS_NAME_SELECTFILE)) {
|
||||
activity.getState().putString(EXTRA_PATH, activity.getPath());
|
||||
}
|
||||
|
||||
if (this.isConnected()) {
|
||||
finishActivityWithSuccess(activity);
|
||||
} else if (!activity.getState().getBoolean("hasStartedAuth", false)) {
|
||||
Activity castedActivity = (Activity)activity;
|
||||
Intent authIntent = AuthorizationActivity.createIntent(castedActivity, this.clientId);
|
||||
castedActivity.startActivityForResult(authIntent, PCLOUD_AUTHORIZATION_REQUEST_CODE);
|
||||
activity.getState().putBoolean("hasStartedAuth", true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(FileStorageSetupActivity activity, int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == PCLOUD_AUTHORIZATION_REQUEST_CODE && data != null) {
|
||||
activity.getState().putBoolean("hasStartedAuth", false);
|
||||
AuthorizationResult result = (AuthorizationResult)(
|
||||
data.getSerializableExtra(AuthorizationActivity.KEY_AUTHORIZATION_RESULT)
|
||||
);
|
||||
this.handleAuthResult(activity, result, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAuthResult(FileStorageSetupActivity activity, AuthorizationResult authorizationResult,
|
||||
Intent data) {
|
||||
if (authorizationResult == AuthorizationResult.ACCESS_GRANTED) {
|
||||
String authToken = data.getStringExtra(AuthorizationActivity.KEY_ACCESS_TOKEN);
|
||||
setAuthToken(authToken);
|
||||
finishActivityWithSuccess(activity);
|
||||
} else {
|
||||
Activity castedActivity = (Activity)activity;
|
||||
Intent resultData = new Intent();
|
||||
resultData.putExtra(EXTRA_ERROR_MESSAGE, "Authentication failed.");
|
||||
castedActivity.setResult(Activity.RESULT_CANCELED, resultData);
|
||||
castedActivity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(FileStorageSetupActivity activity) {
|
||||
|
||||
}
|
||||
|
||||
private ApiClient createApiClientFromSharedPrefs() {
|
||||
SharedPreferences prefs = this.ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
|
||||
String authToken = prefs.getString(SHARED_PREF_AUTH_TOKEN, null);
|
||||
return this.createApiClient(authToken);
|
||||
}
|
||||
|
||||
private ApiClient createApiClient(String authToken) {
|
||||
if (authToken == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
PCloudSdk.newClientBuilder()
|
||||
.authenticator(Authenticators.newOAuthAuthenticator(authToken))
|
||||
.create()
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isConnected() {
|
||||
return (this.apiClient != null);
|
||||
}
|
||||
|
||||
private void clearAuthToken() {
|
||||
this.apiClient = null;
|
||||
SharedPreferences prefs = this.ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor edit = prefs.edit();
|
||||
edit.clear();
|
||||
edit.apply();
|
||||
}
|
||||
|
||||
private void setAuthToken(String authToken) {
|
||||
this.apiClient = this.createApiClient(authToken);
|
||||
SharedPreferences prefs = this.ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor edit = prefs.edit();
|
||||
edit.putString(SHARED_PREF_AUTH_TOKEN, authToken);
|
||||
edit.apply();
|
||||
}
|
||||
|
||||
private String cleanPath(String path) {
|
||||
return (
|
||||
"/" + path.replaceAll("^(" + Pattern.quote(this.getProtocolId()) + "://)?/*", "")
|
||||
);
|
||||
}
|
||||
|
||||
private RemoteFile getRemoteFileByPath(String path) throws Exception {
|
||||
RemoteEntry remoteEntry = this.getRemoteEntryByPath(path);
|
||||
|
||||
try {
|
||||
return remoteEntry.asFile();
|
||||
} catch (IllegalStateException e) {
|
||||
throw new FileNotFoundException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteFolder getRemoteFolderByPath(String path) throws Exception {
|
||||
RemoteEntry remoteEntry = this.getRemoteEntryByPath(path);
|
||||
|
||||
try {
|
||||
return remoteEntry.asFolder();
|
||||
} catch (IllegalStateException e) {
|
||||
throw new FileNotFoundException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteEntry getRemoteEntryByPath(String path) throws Exception {
|
||||
Call<RemoteFolder> call = this.apiClient.listFolder(RemoteFolder.ROOT_FOLDER_ID, true);
|
||||
|
||||
RemoteFolder folder;
|
||||
try {
|
||||
folder = call.execute();
|
||||
} catch (ApiError apiError) {
|
||||
throw convertApiError(apiError);
|
||||
}
|
||||
|
||||
if ("/".equals(path)) {
|
||||
return folder;
|
||||
}
|
||||
|
||||
String[] fileNames = path.substring(1).split("/");
|
||||
RemoteFolder currentFolder = folder;
|
||||
Iterator<String> fileNamesIterator = Arrays.asList(fileNames).iterator();
|
||||
while (true) {
|
||||
String fileName = fileNamesIterator.next();
|
||||
|
||||
Iterator<RemoteEntry> entryIterator = currentFolder.children().iterator();
|
||||
while (true) {
|
||||
RemoteEntry remoteEntry;
|
||||
try {
|
||||
remoteEntry = entryIterator.next();
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new FileNotFoundException(e.toString());
|
||||
}
|
||||
|
||||
if (currentFolder.folderId() == remoteEntry.parentFolderId() && fileName.equals(remoteEntry.name())) {
|
||||
if (!fileNamesIterator.hasNext()) {
|
||||
return remoteEntry;
|
||||
}
|
||||
|
||||
try {
|
||||
currentFolder = remoteEntry.asFolder();
|
||||
} catch (IllegalStateException e) {
|
||||
throw new FileNotFoundException(e.toString());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Exception convertApiError(ApiError e) {
|
||||
String strErrorCode = String.valueOf(e.errorCode());
|
||||
if (strErrorCode.startsWith("1") || "2000".equals(strErrorCode) || "2095".equals(strErrorCode)) {
|
||||
this.clearAuthToken();
|
||||
return new UserInteractionRequiredException("Unlinked from PCloud! User must re-link.", e);
|
||||
} else if (strErrorCode.startsWith("2")) {
|
||||
return new FileNotFoundException(e.toString());
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
private FileEntry convertRemoteEntryToFileEntry(RemoteEntry remoteEntry, String parentPath) {
|
||||
FileEntry fileEntry = new FileEntry();
|
||||
fileEntry.canRead = true;
|
||||
fileEntry.canWrite = true;
|
||||
fileEntry.path = (
|
||||
this.getProtocolId() + "://" +
|
||||
("/".equals(parentPath) ? "" : parentPath) +
|
||||
"/" + remoteEntry.name()
|
||||
);
|
||||
fileEntry.displayName = remoteEntry.name();
|
||||
fileEntry.isDirectory = !remoteEntry.isFile();
|
||||
fileEntry.lastModifiedTime = remoteEntry.lastModified().getTime();
|
||||
|
||||
if (remoteEntry.isFile()) {
|
||||
fileEntry.sizeInBytes = remoteEntry.asFile().size();
|
||||
}
|
||||
|
||||
return fileEntry;
|
||||
}
|
||||
}
|
@@ -2,7 +2,9 @@ package keepass2android.javafilestorage;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
@@ -13,6 +15,7 @@ import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.KeyPair;
|
||||
import com.jcraft.jsch.Session;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
@@ -35,8 +38,10 @@ public class SftpStorage extends JavaFileStorageBase {
|
||||
public String localPath;
|
||||
public int port;
|
||||
}
|
||||
Context _appContext;
|
||||
|
||||
public SftpStorage() {
|
||||
public SftpStorage(Context appContext) {
|
||||
_appContext = appContext;
|
||||
|
||||
}
|
||||
|
||||
@@ -318,12 +323,28 @@ public class SftpStorage extends JavaFileStorageBase {
|
||||
jsch = new JSch();
|
||||
ConnectionInfo ci = splitStringToConnectionInfo(filename);
|
||||
|
||||
String base_dir = getBaseDir();
|
||||
jsch.setKnownHosts(base_dir + "/known_hosts");
|
||||
|
||||
String key_filename = getKeyFileName();
|
||||
try{
|
||||
createKeyPair(key_filename);
|
||||
} catch (Exception ex) {
|
||||
System.out.println(ex);
|
||||
}
|
||||
|
||||
try {
|
||||
jsch.addIdentity(key_filename);
|
||||
} catch (java.lang.Exception e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Session session = jsch.getSession(ci.username, ci.host, ci.port);
|
||||
UserInfo ui = new SftpUserInfo(ci.password);
|
||||
UserInfo ui = new SftpUserInfo(ci.password,_appContext);
|
||||
session.setUserInfo(ui);
|
||||
|
||||
session.setConfig("PreferredAuthentications",
|
||||
"password,publickey");
|
||||
session.setConfig("PreferredAuthentications", "publickey,password");
|
||||
|
||||
session.connect();
|
||||
|
||||
@@ -336,6 +357,35 @@ public class SftpStorage extends JavaFileStorageBase {
|
||||
|
||||
}
|
||||
|
||||
private String getBaseDir() {
|
||||
return _appContext.getFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
private String getKeyFileName() {
|
||||
return getBaseDir() + "/id_kp2a_rsa";
|
||||
}
|
||||
|
||||
public String createKeyPair() throws IOException, JSchException {
|
||||
return createKeyPair(getKeyFileName());
|
||||
|
||||
}
|
||||
|
||||
private String createKeyPair(String key_filename) throws JSchException, IOException {
|
||||
String public_key_filename = key_filename + ".pub";
|
||||
File file = new File(key_filename);
|
||||
if (file.exists())
|
||||
return public_key_filename;
|
||||
int type = KeyPair.RSA;
|
||||
KeyPair kpair = KeyPair.genKeyPair(jsch, type, 2048);
|
||||
kpair.writePrivateKey(key_filename);
|
||||
|
||||
kpair.writePublicKey(public_key_filename, "generated by Keepass2Android");
|
||||
//ret = "Fingerprint: " + kpair.getFingerPrint();
|
||||
kpair.dispose();
|
||||
return public_key_filename;
|
||||
|
||||
}
|
||||
|
||||
public ConnectionInfo splitStringToConnectionInfo(String filename)
|
||||
throws UnsupportedEncodingException {
|
||||
ConnectionInfo ci = new ConnectionInfo();
|
||||
|
@@ -1,14 +1,119 @@
|
||||
package keepass2android.javafilestorage;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.SystemClock;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.jcraft.jsch.UserInfo;
|
||||
|
||||
public class SftpUserInfo implements UserInfo {
|
||||
|
||||
private Object /* pick one type, and fixate on it */ dance(final String type, final String text) /* for inside the thread */
|
||||
{
|
||||
|
||||
final Message msg = Message.obtain();
|
||||
|
||||
/* t(*A*t) */
|
||||
Thread t = new Thread() {
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
int bogon = (int)SystemClock.elapsedRealtime();
|
||||
|
||||
NotificationManager mNotificationManager = (NotificationManager)_appContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(_appContext);
|
||||
builder.setContentText("SFTP prompt");
|
||||
builder.setSmallIcon(R.drawable.ic_logo_green_foreground);
|
||||
|
||||
|
||||
Handler h = new Handler() {
|
||||
public void handleMessage(Message M) {
|
||||
msg.copyFrom(M);
|
||||
Looper.myLooper().quit();
|
||||
}
|
||||
};
|
||||
Messenger m = new Messenger(h);
|
||||
|
||||
Intent intent = new Intent(_appContext, NotifSlave.class);
|
||||
|
||||
intent.setAction("keepass2android.sftp.NotifSlave");
|
||||
intent.putExtra("keepass2android.sftp.returnmessenger", m);
|
||||
intent.putExtra("keepass2android.sftp.reqtype", type);
|
||||
intent.putExtra("keepass2android.sftp.prompt", text);
|
||||
intent.setData((Uri.parse("suckit://"+SystemClock.elapsedRealtime())));
|
||||
|
||||
PendingIntent contentIntent = PendingIntent.getActivity(_appContext, 0, intent, 0);
|
||||
builder.setContentIntent(contentIntent);
|
||||
|
||||
{
|
||||
Intent directIntent = new Intent(_appContext, NotifSlave.class);
|
||||
directIntent.setAction("keepass2android.sftp.NotifSlave");
|
||||
directIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
directIntent.putExtra("keepass2android.sftp.returnmessenger", m);
|
||||
directIntent.putExtra("keepass2android.sftp.reqtype", type);
|
||||
directIntent.putExtra("keepass2android.sftp.prompt", text);
|
||||
directIntent.setData((Uri.parse("suckit://" + SystemClock.elapsedRealtime())));
|
||||
_appContext.startActivity(directIntent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Log.e("KP2AJFS[thread]", "Notifying...");
|
||||
|
||||
mNotificationManager.notify(bogon, builder.build());
|
||||
|
||||
|
||||
|
||||
Log.e("KP2AJFS[thread]", "About to go to 'sleep'...");
|
||||
Looper.loop();
|
||||
Log.e("KP2AJFS[thread]", "And we're alive!");
|
||||
|
||||
Log.e("KP2AJFS[thread]", "result was: "+(Integer.toString(msg.arg1)));
|
||||
|
||||
mNotificationManager.cancel(bogon);
|
||||
}
|
||||
};
|
||||
|
||||
t.start();
|
||||
try {
|
||||
t.join();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type.equals("yesno"))
|
||||
return new Boolean(msg.arg1 == 1);
|
||||
else if (type.equals("message"))
|
||||
return null;
|
||||
else if (type.equals("password")) {
|
||||
if (msg.arg1 == 0)
|
||||
return null;
|
||||
Bundle b = msg.getData();
|
||||
return b.getString("response");
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Context _appContext;
|
||||
|
||||
String _password;
|
||||
|
||||
public SftpUserInfo(String password) {
|
||||
public SftpUserInfo(String password, Context appContext)
|
||||
{
|
||||
_password = password;
|
||||
_appContext = appContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,12 +140,15 @@ public class SftpUserInfo implements UserInfo {
|
||||
|
||||
@Override
|
||||
public boolean promptYesNo(String message) {
|
||||
return true; //continue all operations without user action
|
||||
return (Boolean)dance("yesno", message);
|
||||
|
||||
//test with https://www.sftp.net/public-online-sftp-servers?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showMessage(String message) {
|
||||
Log.d("KP2AJ", message);
|
||||
public void showMessage(String message)
|
||||
{
|
||||
dance("message", message);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
@@ -70,9 +71,18 @@ public class WebDavStorage extends JavaFileStorageBase {
|
||||
|
||||
String scheme = filename.substring(0, filename.indexOf("://"));
|
||||
filename = filename.substring(scheme.length() + 3);
|
||||
String userPwd = filename.substring(0, filename.indexOf('@'));
|
||||
ci.username = decode(userPwd.substring(0, userPwd.indexOf(":")));
|
||||
ci.password = decode(userPwd.substring(userPwd.indexOf(":") + 1));
|
||||
int idxAt = filename.indexOf('@');
|
||||
if (idxAt >= 0)
|
||||
{
|
||||
String userPwd = filename.substring(0, idxAt);
|
||||
int idxColon = userPwd.indexOf(":");
|
||||
if (idxColon >= 0);
|
||||
{
|
||||
ci.username = decode(userPwd.substring(0, idxColon));
|
||||
ci.password = decode(userPwd.substring(idxColon + 1));
|
||||
}
|
||||
}
|
||||
|
||||
ci.URL = scheme + "://" +filename.substring(filename.indexOf('@') + 1);
|
||||
return ci;
|
||||
}
|
||||
@@ -149,13 +159,19 @@ public class WebDavStorage extends JavaFileStorageBase {
|
||||
sslContext.init(null, new TrustManager[] { trustManager }, null);
|
||||
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||
|
||||
|
||||
builder = builder.sslSocketFactory(sslSocketFactory, trustManager)
|
||||
.hostnameVerifier(new DecoratedHostnameVerifier(OkHostnameVerifier.INSTANCE, mCertificateErrorHandler));
|
||||
|
||||
|
||||
builder.connectTimeout(25, TimeUnit.SECONDS);
|
||||
builder.readTimeout(25, TimeUnit.SECONDS);
|
||||
builder.writeTimeout(25, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
OkHttpClient client = builder.build();
|
||||
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,45 @@
|
||||
package keepass2android.javafilestorage.onedrive2;
|
||||
|
||||
import com.microsoft.graph.authentication.IAuthenticationProvider;
|
||||
import com.microsoft.graph.core.DefaultClientConfig;
|
||||
import com.microsoft.graph.core.IClientConfig;
|
||||
import com.microsoft.graph.extensions.GraphServiceClient;
|
||||
import com.microsoft.graph.extensions.IGraphServiceClient;
|
||||
import com.microsoft.graph.http.IHttpRequest;
|
||||
|
||||
/**
|
||||
* Singleton class that manages a GraphServiceClient object.
|
||||
* It implements IAuthentication provider to authenticate requests using an access token.
|
||||
*/
|
||||
public class GraphServiceClientManager implements IAuthenticationProvider {
|
||||
private IGraphServiceClient mGraphServiceClient;
|
||||
private String mAccessToken;
|
||||
|
||||
public GraphServiceClientManager(String accessToken) {
|
||||
mAccessToken = accessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticateRequest(IHttpRequest request) {
|
||||
try {
|
||||
request.addHeader("Authorization", "Bearer " + mAccessToken);
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized IGraphServiceClient getGraphServiceClient() {
|
||||
return getGraphServiceClient(this);
|
||||
}
|
||||
|
||||
public synchronized IGraphServiceClient getGraphServiceClient(IAuthenticationProvider authenticationProvider) {
|
||||
if (mGraphServiceClient == null) {
|
||||
IClientConfig clientConfig = DefaultClientConfig.createWithAuthenticationProvider(
|
||||
authenticationProvider
|
||||
);
|
||||
mGraphServiceClient = new GraphServiceClient.Builder().fromConfig(clientConfig).buildClient();
|
||||
}
|
||||
|
||||
return mGraphServiceClient;
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
<vector android:height="24dp" android:viewportHeight="800.0"
|
||||
android:viewportWidth="800.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#ffffff" android:fillType="evenOdd" android:pathData="m318.7,232c13.6,0 24.6,10.9 24.6,24.4 0,13.5 -11,24.4 -24.6,24.4 -13.6,0 -24.6,-10.9 -24.6,-24.4 0,-13.5 11,-24.4 24.6,-24.4zM453.5,256.4c0,13.5 11,24.4 24.6,24.4 13.6,0 24.6,-10.9 24.6,-24.4 0,-13.5 -11,-24.4 -24.6,-24.4 -13.6,0 -24.6,10.9 -24.6,24.4zM399.8,177.7M140.2,600.6v47h517.3v-47zM140.2,350.9v47h517.3v-47zM140.1,481.2h191.8c-0.8,-4.1 -1.2,-8.3 -1.2,-12.4 0,-12.4 3.4,-24.2 9.9,-34.6L140.1,434.2ZM657.5,481.2v-47L454.9,434.2c6.5,10.4 9.8,22.2 9.8,34.6 0,4.1 -0.4,8.3 -1.2,12.4zM140.1,517.4v47h186.6l14.3,-47zM454.3,517.4 L468.7,564.4h188.8v-47zM432.7,565.8 L411.7,496.9 414.4,495.3c10.3,-5.9 16.7,-16.9 16.7,-28.6 0,-18.2 -15,-33 -33.3,-33 -18.3,0 -33.3,14.8 -33.3,33 0,11.8 6.4,22.7 16.7,28.6l2.7,1.6 -21.1,68.9zM507.5,158.5 L507.7,158.2 543.3,106.9c2.4,-3.5 1.8,-8.1 -1.4,-10.3 -3.2,-2.2 -7.7,-1.1 -10.2,2.4l-37.2,53.5 -0.1,0.3c-29.3,-11.8 -62.1,-18.5 -96.8,-18.5 -35.2,0 -68.5,6.9 -98.1,19L261.8,99c-2.4,-3.5 -7,-4.6 -10.2,-2.4 -3.2,2.2 -3.8,6.8 -1.4,10.3l36.2,52.2c-66.8,32.2 -111.9,92.4 -111.9,161.3h42.9c0,-79.1 80.8,-143.5 180.1,-143.5 99.3,0 180.1,64.3 180.1,143.5h42.9c0.2,-69.3 -45.4,-129.8 -113,-161.9z"/>
|
||||
</vector>
|
0
src/java/JavaFileStorage/gradlew
vendored
Normal file → Executable file
0
src/java/JavaFileStorage/gradlew
vendored
Normal file → Executable file
@@ -10,6 +10,8 @@ android {
|
||||
targetSdkVersion 23
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
multiDexEnabled true
|
||||
|
||||
|
||||
}
|
||||
buildTypes {
|
||||
|
@@ -59,6 +59,18 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.microsoft.identity.client.BrowserTabActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="@string/msalPrefix"
|
||||
android:host="auth" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
|
||||
@@ -68,6 +80,8 @@
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
|
||||
|
||||
</manifest>
|
@@ -89,6 +89,8 @@ extends Activity implements JavaFileStorage.FileStorageSetupActivity {
|
||||
@Override
|
||||
public String getPath() {
|
||||
// TODO Auto-generated method stub
|
||||
if (getState().containsKey(JavaFileStorage.EXTRA_PATH))
|
||||
return getState().getString(JavaFileStorage.EXTRA_PATH);
|
||||
return getIntent().getStringExtra(JavaFileStorage.EXTRA_PATH);
|
||||
}
|
||||
|
||||
|
@@ -135,17 +135,19 @@ package com.crocoapps.javafilestoragetest;
|
||||
import group.pals.android.lib.ui.filechooser.FileChooserActivity;
|
||||
import group.pals.android.lib.ui.filechooser.providers.BaseFileProviderUtils;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
//import keepass2android.javafilestorage.DropboxCloudRailStorage;
|
||||
import keepass2android.javafilestorage.DropboxV2Storage;
|
||||
import keepass2android.javafilestorage.ICertificateErrorHandler;
|
||||
import keepass2android.javafilestorage.JavaFileStorage;
|
||||
import keepass2android.javafilestorage.JavaFileStorage.FileEntry;
|
||||
import keepass2android.javafilestorage.OneDriveStorage;
|
||||
import keepass2android.javafilestorage.OneDriveStorage2;
|
||||
import keepass2android.javafilestorage.SftpStorage;
|
||||
import keepass2android.javafilestorage.UserInteractionRequiredException;
|
||||
import keepass2android.javafilestorage.WebDavStorage;
|
||||
@@ -201,10 +203,17 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.d("KP2AJ",e.toString());
|
||||
//if exception because folder exists
|
||||
path = fs.createFilePath(parentPath, testPath);
|
||||
}
|
||||
|
||||
String textToUpload2 = "abcdefg";
|
||||
String filename2 = fs.createFilePath(parentPath, "file.txt");
|
||||
/*if (!path.endsWith("/"))
|
||||
path += "/";
|
||||
String filename = path+"file.text";*/
|
||||
fs.uploadFile(filename2,textToUpload2.getBytes(),true);
|
||||
|
||||
FileEntry e1 = fs.getFileEntry(parentPath);
|
||||
FileEntry e2 = fs.getFileEntry(path);
|
||||
@@ -526,9 +535,11 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
|
||||
}
|
||||
|
||||
static JavaFileStorage createStorageToTest(Context ctx, Context appContext, boolean simulateRestart) {
|
||||
//storageToTest = new SftpStorage();
|
||||
//storageToTest = new SftpStorage(ctx.getApplicationContext());
|
||||
//storageToTest = new SkyDriveFileStorage("000000004010C234", appContext);
|
||||
storageToTest = new OneDriveStorage(appContext, "000000004010C234");
|
||||
storageToTest = new OneDriveStorage2((Activity) ctx, "8374f801-0f55-407d-80cc-9a04fe86d9b2");
|
||||
|
||||
|
||||
//storageToTest = new GoogleDriveFileStorage();
|
||||
/*storageToTest = new WebDavStorage(new ICertificateErrorHandler() {
|
||||
@Override
|
||||
@@ -615,7 +626,7 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
|
||||
Toast.makeText(this, "requestCode: "+requestCode, Toast.LENGTH_LONG).show();
|
||||
if (requestCode == 1)
|
||||
//new PerformTestTask().execute(path,"TestFileStorage<67>", storageToTest); //use an umlaut to see how that works
|
||||
new PerformTestTask().execute(path,"TestFileStorage", storageToTest);
|
||||
new PerformTestTask().execute(path,"TestFileStorage", storageToTest);
|
||||
else
|
||||
if (requestCode == 2)
|
||||
{
|
||||
@@ -662,6 +673,20 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
|
||||
return this;
|
||||
}
|
||||
|
||||
public static String readStream(InputStream is) {
|
||||
StringBuilder sb = new StringBuilder(512);
|
||||
try {
|
||||
Reader r = new InputStreamReader(is, "UTF-8");
|
||||
int c = 0;
|
||||
while ((c = r.read()) != -1) {
|
||||
sb.append((char) c);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performManualFileSelect(boolean isForSave, final int requestCode,
|
||||
String protocolId)
|
||||
@@ -669,6 +694,30 @@ public class MainActivity extends Activity implements JavaFileStorage.FileStorag
|
||||
if (protocolId.equals("sftp"))
|
||||
{
|
||||
final View view = getLayoutInflater().inflate(R.layout.sftp_credentials, null);
|
||||
|
||||
view.findViewById(R.id.send_public_key).setOnClickListener(v -> {
|
||||
Intent sendIntent = new Intent();
|
||||
|
||||
|
||||
SftpStorage sftpStorage = (SftpStorage)storageToTest;
|
||||
try {
|
||||
String pub_filename = sftpStorage.createKeyPair();
|
||||
|
||||
sendIntent.setAction(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, readStream(new FileInputStream(pub_filename)));
|
||||
|
||||
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Keepass2Android sftp public key");
|
||||
sendIntent.setType("text/plain");
|
||||
this.startActivity(Intent.createChooser(sendIntent, "Send public key to..."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Toast.makeText(this,"Failed to create key pair: " + ex.getMessage(), Toast.LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
new AlertDialog.Builder(this)
|
||||
.setView(view)
|
||||
.setTitle("Enter SFTP credentials")
|
||||
|
@@ -63,6 +63,9 @@
|
||||
android:singleLine="true"
|
||||
android:text="/home/philipp"
|
||||
/>
|
||||
|
||||
<Button android:id="@+id/send_public_key"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="send public key" />
|
||||
|
||||
</LinearLayout>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user